Skip to content

Commit ad42737

Browse files
docs(keycloak): add comprehensive README with values reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dd86f48 commit ad42737

1 file changed

Lines changed: 346 additions & 0 deletions

File tree

charts/keycloak/README.md

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
# Keycloak Helm Chart
2+
3+
[![CI](https://github.com/KitStream/helms/actions/workflows/ci.yaml/badge.svg)](https://github.com/KitStream/helms/actions/workflows/ci.yaml)
4+
[![Chart Version](https://img.shields.io/badge/chart-26.5.0-blue)](https://github.com/KitStream/helms/releases)
5+
[![App Version](https://img.shields.io/badge/keycloak-26.5.0-green)](https://github.com/keycloak/keycloak)
6+
7+
A Helm chart for deploying [Keycloak](https://www.keycloak.org) IAM using the upstream `quay.io/keycloak/keycloak` image on Kubernetes.
8+
9+
## Overview
10+
11+
This chart deploys Keycloak directly from the upstream container image with native `KC_*` environment variable configuration. It supports:
12+
13+
- **Databases**: PostgreSQL, MySQL, MSSQL, or embedded H2 (dev mode)
14+
- **Clustering**: JDBC-PING (default) or Kubernetes DNS-PING via headless service
15+
- **Build optimization**: Optional init container for `kc.sh build` to speed up startup
16+
- **Observability**: Health endpoints on a dedicated management port, Prometheus metrics, and optional ServiceMonitor
17+
18+
## Prerequisites
19+
20+
- Kubernetes 1.24+
21+
- Helm 3.x
22+
- An external database for production use (PostgreSQL recommended)
23+
24+
## Installation
25+
26+
### From OCI Registry (recommended)
27+
28+
```bash
29+
helm install keycloak oci://ghcr.io/kitstream/helms/keycloak \
30+
--version 26.5.0 \
31+
-n keycloak --create-namespace \
32+
-f my-values.yaml
33+
```
34+
35+
### From Source
36+
37+
```bash
38+
helm install keycloak ./charts/keycloak \
39+
-n keycloak --create-namespace \
40+
-f my-values.yaml
41+
```
42+
43+
## Minimal Configuration Examples
44+
45+
### Development (embedded H2)
46+
47+
No external database required. **Not suitable for production.**
48+
49+
```yaml
50+
database:
51+
type: dev
52+
53+
admin:
54+
username: admin
55+
password:
56+
secretName: keycloak-admin-password
57+
secretKey: password
58+
```
59+
60+
### PostgreSQL (production)
61+
62+
```yaml
63+
hostname: keycloak.example.com
64+
hostnameStrict: true
65+
66+
database:
67+
type: postgresql
68+
host: postgres.database.svc.cluster.local
69+
port: 5432
70+
user: keycloak
71+
name: keycloak
72+
password:
73+
secretName: keycloak-db-password
74+
secretKey: password
75+
76+
admin:
77+
username: admin
78+
password:
79+
secretName: keycloak-admin-password
80+
secretKey: password
81+
82+
ingress:
83+
enabled: true
84+
className: nginx
85+
hosts:
86+
- host: keycloak.example.com
87+
paths:
88+
- path: /
89+
pathType: Prefix
90+
tls:
91+
- secretName: keycloak-tls
92+
hosts:
93+
- keycloak.example.com
94+
```
95+
96+
### Multi-Replica with Kubernetes DNS-PING
97+
98+
```yaml
99+
replicaCount: 3
100+
101+
hostname: keycloak.example.com
102+
hostnameStrict: true
103+
104+
database:
105+
type: postgresql
106+
host: postgres.database.svc.cluster.local
107+
port: 5432
108+
user: keycloak
109+
name: keycloak
110+
password:
111+
secretName: keycloak-db-password
112+
secretKey: password
113+
114+
cache:
115+
stack: kubernetes
116+
117+
admin:
118+
username: admin
119+
password:
120+
secretName: keycloak-admin-password
121+
secretKey: password
122+
```
123+
124+
## Creating Secrets
125+
126+
### Admin Password
127+
128+
```bash
129+
kubectl create secret generic keycloak-admin-password \
130+
--from-literal=password='YOUR_SECURE_PASSWORD' \
131+
-n keycloak
132+
```
133+
134+
### Database Password
135+
136+
```bash
137+
kubectl create secret generic keycloak-db-password \
138+
--from-literal=password='YOUR_DB_PASSWORD' \
139+
-n keycloak
140+
```
141+
142+
## Hostname Configuration
143+
144+
Keycloak 26 requires explicit hostname configuration for production mode:
145+
146+
- **`hostname`**: Set to your public Keycloak URL (e.g. `keycloak.example.com`)
147+
- **`hostnameStrict`**: Set to `true` when `hostname` is configured to prevent dynamic hostname resolution from request headers. Defaults to `false` so Keycloak can start without a hostname (dev/testing).
148+
149+
If `hostnameStrict` is `true` and no `hostname` is set, Keycloak will refuse to start.
150+
151+
## Clustering
152+
153+
The chart supports two cache stacks for multi-replica deployments:
154+
155+
| Stack | Description | Default |
156+
| ------------ | ------------------------------------------------------------------------ | ------- |
157+
| `jdbc-ping` | Uses the configured database for node discovery. No extra config needed. | Yes |
158+
| `kubernetes` | Uses DNS-PING via the headless service for JGroups discovery. | No |
159+
160+
In dev mode (`database.type: dev`), clustering is disabled (`KC_CACHE=local`) since the embedded H2 database cannot be shared across replicas.
161+
162+
## Build Optimization
163+
164+
By default, Keycloak runs an auto-build at startup in production mode (`start`), which can take 2-5 minutes. To speed up startup, enable the build init container:
165+
166+
```yaml
167+
build:
168+
enabled: true
169+
```
170+
171+
This runs `kc.sh build` in an init container and passes `--optimized` to the main container, reducing startup time to seconds.
172+
173+
## Values Reference
174+
175+
### Global
176+
177+
| Key | Type | Default | Description |
178+
| ---------------------------- | ------ | ------- | -------------------------------- |
179+
| `nameOverride` | string | `""` | Override the chart name |
180+
| `fullnameOverride` | string | `""` | Fully override the resource name |
181+
| `imagePullSecrets` | list | `[]` | Global image pull secrets |
182+
| `serviceAccount.create` | bool | `true` | Create a ServiceAccount |
183+
| `serviceAccount.annotations` | object | `{}` | ServiceAccount annotations |
184+
| `serviceAccount.name` | string | `""` | ServiceAccount name override |
185+
186+
### Image
187+
188+
| Key | Type | Default | Description |
189+
| ------------------ | ------ | ----------------------------- | --------------------------- |
190+
| `image.repository` | string | `"quay.io/keycloak/keycloak"` | Container image repository |
191+
| `image.tag` | string | `""` (appVersion) | Image tag |
192+
| `image.pullPolicy` | string | `"IfNotPresent"` | Image pull policy |
193+
| `replicaCount` | int | `1` | Number of Keycloak replicas |
194+
195+
### Database
196+
197+
| Key | Type | Default | Description |
198+
| ------------------------------ | ------ | ------------ | ------------------------------------------------------- |
199+
| `database.type` | string | `"dev"` | Database type (`postgresql`, `mysql`, `mssql`, `dev`) |
200+
| `database.host` | string | `""` | Database hostname (required for external databases) |
201+
| `database.port` | string | `""` | Database port (auto-defaults: 5432/3306/1433) |
202+
| `database.name` | string | `"keycloak"` | Database name |
203+
| `database.user` | string | `""` | Database username |
204+
| `database.password.secretName` | string | `""` | Secret containing the database password |
205+
| `database.password.secretKey` | string | `"password"` | Key in the Secret |
206+
| `database.poolMinSize` | string | `""` | Connection pool minimum size |
207+
| `database.poolInitialSize` | string | `""` | Connection pool initial size |
208+
| `database.poolMaxSize` | string | `""` | Connection pool maximum size |
209+
| `database.sslMode` | string | `""` | SSL mode for PostgreSQL (e.g. `verify-full`, `require`) |
210+
211+
### Hostname & Proxy
212+
213+
| Key | Type | Default | Description |
214+
| ---------------- | ------ | ------- | ------------------------------------------------------------------------ |
215+
| `hostname` | string | `""` | Public hostname (maps to `KC_HOSTNAME`) |
216+
| `hostnameAdmin` | string | `""` | Separate admin console hostname (maps to `KC_HOSTNAME_ADMIN`) |
217+
| `hostnameStrict` | bool | `false` | Disable dynamic hostname from request headers (set `true` with hostname) |
218+
| `proxyHeaders` | string | `""` | Proxy header mode: `xforwarded` or `forwarded` |
219+
| `httpEnabled` | bool | `true` | Enable HTTP listener (required for edge-terminated TLS) |
220+
221+
### TLS
222+
223+
| Key | Type | Default | Description |
224+
| ---------------- | ------ | ------- | ------------------------------------------- |
225+
| `tls.enabled` | bool | `false` | Enable TLS passthrough (mount cert and key) |
226+
| `tls.secretName` | string | `""` | Secret containing `tls.crt` and `tls.key` |
227+
228+
### Clustering
229+
230+
| Key | Type | Default | Description |
231+
| ------------- | ------ | ------------- | -------------------------------------------------- |
232+
| `cache.stack` | string | `"jdbc-ping"` | Cache stack: `jdbc-ping` (default) or `kubernetes` |
233+
234+
### Admin Credentials
235+
236+
| Key | Type | Default | Description |
237+
| --------------------------- | ------ | ------------ | ----------------------------------------- |
238+
| `admin.username` | string | `""` | Admin username (maps to `KEYCLOAK_ADMIN`) |
239+
| `admin.password.secretName` | string | `""` | Secret containing the admin password |
240+
| `admin.password.secretKey` | string | `"password"` | Key in the Secret |
241+
242+
### Observability
243+
244+
| Key | Type | Default | Description |
245+
| -------------------------------------- | ------ | -------- | ----------------------------------- |
246+
| `healthEnabled` | bool | `true` | Enable health endpoints |
247+
| `metrics.enabled` | bool | `true` | Enable Prometheus metrics |
248+
| `metrics.serviceMonitor.enabled` | bool | `false` | Create a ServiceMonitor resource |
249+
| `metrics.serviceMonitor.labels` | object | `{}` | Additional ServiceMonitor labels |
250+
| `metrics.serviceMonitor.interval` | string | `""` | Scrape interval |
251+
| `metrics.serviceMonitor.scrapeTimeout` | string | `""` | Scrape timeout |
252+
| `logLevel` | string | `"info"` | Log level (`debug`, `info`, `warn`) |
253+
254+
### Build Optimization
255+
256+
| Key | Type | Default | Description |
257+
| --------------- | ---- | ------- | ----------------------------------- |
258+
| `build.enabled` | bool | `false` | Run `kc.sh build` in init container |
259+
260+
### Extra Configuration
261+
262+
| Key | Type | Default | Description |
263+
| -------------------- | ------ | ------- | ------------------------------------------- |
264+
| `extraEnvVars` | list | `[]` | Additional environment variables |
265+
| `extraEnvVarsSecret` | string | `""` | Existing Secret to mount as env vars |
266+
| `extraVolumes` | list | `[]` | Additional volumes |
267+
| `extraVolumeMounts` | list | `[]` | Additional volume mounts |
268+
| `features` | string | `""` | Comma-separated Keycloak features to enable |
269+
270+
### Persistent Storage
271+
272+
| Key | Type | Default | Description |
273+
| -------------------------- | ------ | ------------------- | -------------------------------------- |
274+
| `persistence.enabled` | bool | `false` | Enable PVC for custom themes/providers |
275+
| `persistence.storageClass` | string | `""` | Storage class |
276+
| `persistence.accessModes` | list | `["ReadWriteOnce"]` | PVC access modes |
277+
| `persistence.size` | string | `"1Gi"` | Volume size |
278+
| `persistence.annotations` | object | `{}` | PVC annotations |
279+
280+
### Services
281+
282+
| Key | Type | Default | Description |
283+
| ----------------------------- | ------ | ------------- | ---------------------------------- |
284+
| `service.type` | string | `"ClusterIP"` | Service type |
285+
| `service.httpPort` | int | `8080` | HTTP port |
286+
| `service.managementPort` | int | `9000` | Management port (health + metrics) |
287+
| `service.annotations` | object | `{}` | Service annotations |
288+
| `headlessService.jgroupsPort` | int | `7800` | JGroups clustering port |
289+
| `headlessService.annotations` | object | `{}` | Headless service annotations |
290+
291+
### Ingress
292+
293+
| Key | Type | Default | Description |
294+
| --------------------- | ------ | ------- | ----------------------- |
295+
| `ingress.enabled` | bool | `false` | Create Ingress resource |
296+
| `ingress.className` | string | `""` | Ingress class name |
297+
| `ingress.annotations` | object | `{}` | Ingress annotations |
298+
| `ingress.hosts` | list | `[]` | Ingress host rules |
299+
| `ingress.tls` | list | `[]` | TLS configuration |
300+
301+
### Probes
302+
303+
| Key | Type | Default | Description |
304+
| ---------------- | ------ | --------------------------------------------- | --------------- |
305+
| `startupProbe` | object | HTTP GET `/health/started` on management:9000 | Startup probe |
306+
| `livenessProbe` | object | HTTP GET `/health/live` on management:9000 | Liveness probe |
307+
| `readinessProbe` | object | HTTP GET `/health/ready` on management:9000 | Readiness probe |
308+
309+
### Pod Scheduling & Metadata
310+
311+
| Key | Type | Default | Description |
312+
| -------------------- | ------ | ------- | -------------------------------- |
313+
| `resources` | object | `{}` | CPU/memory requests and limits |
314+
| `nodeSelector` | object | `{}` | Node selector labels |
315+
| `tolerations` | list | `[]` | Pod tolerations |
316+
| `affinity` | object | `{}` | Pod affinity rules |
317+
| `podAnnotations` | object | `{}` | Pod annotations |
318+
| `podLabels` | object | `{}` | Additional pod labels |
319+
| `podSecurityContext` | object | `{}` | Pod-level security context |
320+
| `securityContext` | object | `{}` | Container-level security context |
321+
322+
## Architecture
323+
324+
```
325+
┌──────────────────────────────────────────────────────┐
326+
│ Ingress Controller │
327+
│ │
328+
│ / ────────────────────────► Keycloak Pod │
329+
│ ├─ :8080 HTTP │
330+
│ ├─ :9000 Management │
331+
│ │ (health + metrics) │
332+
│ └─ :7800 JGroups │
333+
│ (clustering) │
334+
└──────────────────────────────────────────────────────┘
335+
│ │
336+
Headless Service Main Service
337+
(JGroups DNS-PING) (HTTP + Management)
338+
```
339+
340+
## Upstream Source
341+
342+
This chart deploys the upstream [Keycloak](https://github.com/keycloak/keycloak) image from `quay.io/keycloak/keycloak`. See the `sources` field in `Chart.yaml` for details.
343+
344+
## License
345+
346+
Apache License 2.0 — see [LICENSE](../../LICENSE).

0 commit comments

Comments
 (0)