A Helm chart for Keycloak - Open Source Identity and Access Management Solution. Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more. It supports modern standards like OpenID Connect, OAuth 2.0, and SAML.
- Kubernetes 1.24+
- Helm 3.2.0+
- PV provisioner support in the underlying infrastructure (if persistence is enabled)
To install the chart with the release name my-keycloak:
helm install my-keycloak oci://registry-1.docker.io/cloudpirates/keycloakTo install with custom values:
helm install my-keycloak oci://registry-1.docker.io/cloudpirates/keycloak -f my-values.yamlOr install directly from the local chart:
helm install my-keycloak ./charts/keycloakThe command deploys Keycloak on the Kubernetes cluster in the default configuration. The Configuration section lists the parameters that can be configured during installation.
To uninstall/delete the my-keycloak deployment:
helm uninstall my-keycloakThe command removes all the Kubernetes components associated with the chart and deletes the release.
This Helm chart is cryptographically signed with Cosign to ensure authenticity and prevent tampering.
Public Key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5U+rM2d3hDjgP5T3cLShuuQIU9vR
Z4/G+Nug6q5vRa+C3qUA1wXjbaJFAfcIrv5VjmYAYOj13shnPpp3Zh4fnQ==
-----END PUBLIC KEY-----
To verify the helm chart before installation, copy the public key to the file cosign.pub and run cosign:
cosign verify --key cosign.pub registry-1.docker.io/cloudpirates/keycloak:<version>The following table lists the configurable parameters of the Keycloak chart and their default values.
| Parameter | Description | Default |
|---|---|---|
global.imageRegistry |
Global Docker image registry | "" |
global.imagePullSecrets |
Global Docker registry secret names as an array | [] |
| Parameter | Description | Default |
|---|---|---|
nameOverride |
String to partially override keycloak.fullname | "" |
fullnameOverride |
String to fully override keycloak.fullname | "" |
namespaceOverride |
String to override the namespace for all resources | "" |
commonLabels |
Labels to add to all deployed objects | {} |
commonAnnotations |
Annotations to add to all deployed objects | {} |
| Parameter | Description | Default |
|---|---|---|
image.registry |
Keycloak image registry | docker.io |
image.repository |
Keycloak image repository | keycloak/keycloak |
image.tag |
Keycloak image tag (immutable tags are recommended) | "26.5.5@sha256:a7b0cb7a43a1235a61872883414d3f1d9a3ceac9df6e5907bd12202778a6265c" |
image.imagePullPolicy |
Keycloak image pull policy | Always |
image.command |
Keycloak container entrypoint command | "/opt/keycloak/bin/kc.sh" |
| Parameter | Description | Default |
|---|---|---|
replicaCount |
Number of Keycloak replicas to deploy | 1 |
| Parameter | Description | Default |
|---|---|---|
podAnnotations |
Map of annotations to add to the pods | {} |
podLabels |
Map of labels to add to the pods | {} |
| Parameter | Description | Default |
|---|---|---|
shareProcessNamespace |
Enable process namespace sharing between containers in the pod | false |
| Parameter | Description | Default |
|---|---|---|
extraVolumes |
Array of Volume to add to the keycloak pod | [] |
extraVolumeMounts |
Array of VolumeMount to add to the keycloak container | [] |
preserveThemes |
Disable mounting an emptyDir over /opt/keycloak/themes, preserving the themes bundled in the image |
false |
preserveProviders |
Disable mounting an emptyDir over /opt/keycloak/providers, preserving the providers bundled in the image |
false |
| Parameter | Description | Default |
|---|---|---|
extraInitContainers |
Array of initContainer to add to the keycloak pod. Supports Helm template expressions (see Template Expressions in Extra Fields). | [] |
| Parameter | Description | Default |
|---|---|---|
extraContainers |
Array of containers to add to the keycloak pod | [] |
| Parameter | Description | Default |
|---|---|---|
podSecurityContext.fsGroup |
Group ID for the volumes of the pod | 1001 |
podSecurityContext.seccompProfile |
Seccomp profile for the pod | {type: RuntimeDefault} |
containerSecurityContext.allowPrivilegeEscalation |
Enable container privilege escalation | false |
containerSecurityContext.runAsNonRoot |
Configure the container to run as a non-root user | true |
containerSecurityContext.runAsUser |
User ID for the Keycloak container | 1001 |
containerSecurityContext.runAsGroup |
Group ID for the Keycloak container | 1001 |
containerSecurityContext.readOnlyRootFilesystem |
Mount container root filesystem as read-only | false |
containerSecurityContext.capabilities.drop |
Linux capabilities to be dropped | ["ALL"] |
| Parameter | Description | Default |
|---|---|---|
keycloak.adminUser |
Keycloak admin username | admin |
keycloak.adminPassword |
Keycloak admin password | "" |
keycloak.existingSecret |
Name of existing secret to use for Keycloak admin credentials | "" |
keycloak.secretKeys.adminPasswordKey |
Secret key for admin credentials | "admin-password" |
keycloak.hostname |
Keycloak hostname including scheme (e.g. https://keycloak.example.com) |
"" |
keycloak.hostnameAdmin |
Keycloak admin hostname including scheme (e.g. https://keycloak.example.com) |
"" |
keycloak.hostnameStrict |
Enable strict hostname resolution | false |
keycloak.hostnameBackchannel |
Keycloak backchannel hostname including scheme (e.g. https://keycloak.example.com) |
"" |
keycloak.httpEnabled |
Enable HTTP listener | true |
keycloak.httpsEnabled |
Enable HTTPS listener (only when Keycloak itself terminates TLS via tls.enabled) |
false |
keycloak.httpPort |
HTTP port | 8080 |
keycloak.httpsPort |
HTTPS port | 8443 |
keycloak.proxyHeaders |
Proxy headers to trust (forwarded or xforwarded). Must be set when running behind a reverse proxy (e.g. nginx ingress). Leave empty only when Keycloak is directly exposed without a proxy. |
"" |
keycloak.proxyProtocolEnabled |
Whether the server should use the HA PROXY protocol when serving requests from behind a proxy. (true, false) | false |
keycloak.proxyTrustedAddresses |
A comma separated list of trusted proxy addresses | "" |
keycloak.production |
Enable production mode (runs start instead of start-dev). Should only be disabled for local development. |
true |
keycloak.httpRelativePath |
Set relative path for serving resources; must start with a / | / |
keycloak.extraArgsPrefix |
Additional arguments to pass before the start command (e.g., for --config-file) | [] |
keycloak.extraArgs |
Additional arguments to pass to the Keycloak startup command | [] |
| Parameter | Description | Default |
|---|---|---|
tls.enabled |
Enable TLS/HTTPS support using custom certificates | false |
tls.existingSecret |
Name of existing secret containing TLS certificate and key (PEM format, keys: tls.crt, tls.key) | "" |
tls.certificateFile |
Path where the TLS certificate file will be mounted (internal) | "/opt/keycloak/certs/tls.crt" |
tls.certificateKeyFile |
Path where the TLS certificate key file will be mounted (internal) | "/opt/keycloak/certs/tls.key" |
tls.certManager.enabled |
Enable cert-manager integration for automatic certificate provisioning | false |
tls.certManager.issuerRef.name |
Name of the cert-manager Issuer or ClusterIssuer | "" |
tls.certManager.issuerRef.kind |
Kind of the cert-manager issuer (Issuer or ClusterIssuer) | ClusterIssuer |
tls.certManager.issuerRef.group |
Group of the cert-manager issuer | cert-manager.io |
tls.certManager.duration |
Certificate duration (e.g., 2160h for 90 days) | "" |
tls.certManager.renewBefore |
Time before expiry to renew certificate (e.g., 360h for 15 days) | "" |
tls.certManager.commonName |
Certificate common name (defaults to first dnsName if not specified) | "" |
tls.certManager.dnsNames |
List of DNS names for the certificate (uses ingress.hosts if not specified) | [] |
tls.certManager.ipAddresses |
List of IP addresses for the certificate | [] |
tls.certManager.secretName |
Name for the generated secret (defaults to <fullname>-tls) |
"" |
tls.certManager.usages |
Certificate key usages | ["digital signature", "key encipherment"] |
tls.certManager.annotations |
Additional annotations for the Certificate resource | {} |
tls.truststoreEnabled |
Enable truststore for client certificate validation or outgoing HTTPS requests | false |
tls.truststoreExistingSecret |
Name of existing secret containing truststore file (Java Keystore format, default-key: truststore.jks) | "" |
tls.truststoreExistingSecretKey |
Key within the truststore secret that holds the truststore file | "truststore.jks" |
tls.truststorePassword |
Password for the truststore (use with caution - consider using existing secret) | "" |
tls.truststoreFile |
Path where the truststore file will be mounted (internal) | "/opt/keycloak/truststore/truststore.jks" |
| Parameter | Description | Default |
|---|---|---|
database.type |
Database type (postgres, mysql, mariadb, mssql). Set to "" to omit all database configuration (e.g. when using a custom --db-url via keycloak.extraArgs). Note: H2 databases are not supported due to readonly filesystem restrictions. |
postgres |
database.host |
Database host (only used when not using embedded database) | "" |
database.port |
Database port (only used when not using embedded database, defaults: postgres=5432, mysql/mariadb=3306, mssql=1433) | "" |
database.schema |
Database schema | "" |
database.urlProperties |
Additional database url properties | "" |
database.name |
Database name (only used when not using embedded database) | keycloak |
database.username |
Database username (only used when not using embedded database) | keycloak |
database.password |
Database password (only used when not using embedded database) | "" |
database.existingSecret |
Name of existing secret for database credentials (only used when not using embedded database) | "" |
database.secretKeys.passwordKey |
Name of key in existing secret for database password | "db-password" |
database.secretKeys.usernameKey |
Name of key in existing secret for database username | "db-username" |
database.jdbcParams |
Additional JDBC parameters | "" |
| Parameter | Description | Default |
|---|---|---|
cache.stack |
Cache stack (local, ispn, default) | local |
cache.configFile |
Custom cache configuration file | "" |
| Parameter | Description | Default |
|---|---|---|
realm.import |
Enable import of realms from /opt/keycloak/data/import | false |
realm.configFile |
Json config for initial realm configuration, stored in a Secret and mounted in /opt/keycloak/data/import | "" |
realm.existingSecret |
Name of existing secret containing realm configuration (key: realm.json) | "" |
| Parameter | Description | Default |
|---|---|---|
features.enabled |
List of enabled features | [] |
features.disabled |
List of disabled features | [] |
| Parameter | Description | Default |
|---|---|---|
service.type |
Keycloak service type | ClusterIP |
service.httpPort |
Keycloak HTTP service port | 8080 |
service.httpsPort |
Keycloak HTTPS service port | 8443 |
service.httpTargetPort |
Keycloak HTTP container port | 8080 |
service.httpsTargetPort |
Keycloak HTTPS container port | 8443 |
service.annotations |
Service annotations | {} |
service.trafficDistribution |
Traffic distribution preference (e.g. PreferClose). Requires Kubernetes 1.31+. |
"" |
service.httpNodePort |
HTTP node port (only used when service.type: NodePort) |
30080 |
service.httpsNodePort |
HTTPS node port (only used when service.type: NodePort) |
30443 |
| Parameter | Description | Default |
|---|---|---|
ingress.enabled |
Enable ingress record generation for Keycloak | false |
ingress.className |
IngressClass that will be used to implement the Ingress | "" |
ingress.annotations |
Additional annotations for the Ingress resource | {} |
ingress.hosts[0].host |
Hostname for Keycloak ingress | keycloak.local |
ingress.hosts[0].paths[0].path |
Path for Keycloak ingress | / |
ingress.hosts[0].paths[0].pathType |
Path type for Keycloak ingress | Prefix |
ingress.tls |
TLS configuration for Keycloak ingress | [] |
| Parameter | Description | Default |
|---|---|---|
resources |
The resources to allocate for the main Keycloak container | {} |
| Parameter | Description | Default |
|---|---|---|
persistence.enabled |
Enable persistence using Persistent Volume Claims | false |
persistence.storageClass |
Persistent Volume storage class | "" |
persistence.annotations |
Persistent Volume Claim annotations | {} |
persistence.size |
Persistent Volume size | 1Gi |
persistence.accessModes |
Persistent Volume access modes | ["ReadWriteOnce"] |
persistence.existingClaim |
The name of an existing PVC to use for persistence | "" |
| Parameter | Description | Default |
|---|---|---|
metrics.enabled |
Enable metrics endpoint on Keycloak | false |
metrics.service.type |
Metrics service type | ClusterIP |
metrics.service.port |
Metrics service port | 9000 |
metrics.service.targetPort |
Metrics service target port | http-metrics |
metrics.service.annotations |
Additional annotations for metrics service | {} |
metrics.service.labels |
Additional labels for metrics service | {} |
metrics.serviceMonitor.enabled |
Create ServiceMonitor resource for scraping metrics using PrometheusOperator | false |
metrics.serviceMonitor.namespace |
Namespace in which ServiceMonitor is created | "" |
metrics.serviceMonitor.interval |
Interval at which metrics should be scraped | 30s |
metrics.serviceMonitor.scrapeTimeout |
Timeout after which the scrape is ended | "" |
metrics.serviceMonitor.additionalLabels |
Additional labels for ServiceMonitor | {} |
metrics.serviceMonitor.annotations |
Additional annotations for ServiceMonitor | {} |
metrics.serviceMonitor.relabelings |
RelabelConfigs to apply to samples before scraping | [] |
metrics.serviceMonitor.metricRelabelings |
MetricRelabelConfigs to apply to samples before ingestion | [] |
metrics.serviceMonitor.honorLabels |
Specify honorLabels parameter to add the scrape endpoint | false |
metrics.serviceMonitor.jobLabel |
The name of the label on the target service to use as the job name in Prometheus | "" |
| Parameter | Description | Default |
|---|---|---|
livenessProbe.enabled |
Enable livenessProbe on Keycloak containers | true |
livenessProbe.initialDelaySeconds |
Initial delay seconds for livenessProbe | 60 |
livenessProbe.periodSeconds |
Period seconds for livenessProbe | 30 |
livenessProbe.timeoutSeconds |
Timeout seconds for livenessProbe | 5 |
livenessProbe.failureThreshold |
Failure threshold for livenessProbe | 3 |
livenessProbe.successThreshold |
Success threshold for livenessProbe | 1 |
readinessProbe.enabled |
Enable readinessProbe on Keycloak containers | true |
readinessProbe.initialDelaySeconds |
Initial delay seconds for readinessProbe | 30 |
readinessProbe.periodSeconds |
Period seconds for readinessProbe | 10 |
readinessProbe.timeoutSeconds |
Timeout seconds for readinessProbe | 5 |
readinessProbe.failureThreshold |
Failure threshold for readinessProbe | 3 |
readinessProbe.successThreshold |
Success threshold for readinessProbe | 1 |
startupProbe.enabled |
Enable startupProbe on Keycloak containers | true |
startupProbe.initialDelaySeconds |
Initial delay seconds for startupProbe | 30 |
startupProbe.periodSeconds |
Period seconds for startupProbe | 10 |
startupProbe.timeoutSeconds |
Timeout seconds for startupProbe | 5 |
startupProbe.failureThreshold |
Failure threshold for startupProbe | 60 |
startupProbe.successThreshold |
Success threshold for startupProbe | 1 |
| Parameter | Description | Default |
|---|---|---|
nodeSelector |
Node labels for pod assignment | {} |
tolerations |
Toleration labels for pod assignment | [] |
affinity |
Affinity settings for pod assignment | {} |
topologySpreadConstraints |
Topology Spread Constraints for pod assignment | [] |
priorityClassName |
Priority class name for pod eviction | "" |
| Parameter | Description | Default |
|---|---|---|
serviceAccount.create |
Specifies whether a service account should be created | false |
serviceAccount.annotations |
Annotations to add to the service account | {} |
serviceAccount.name |
The name of the service account to use. If not set and create is true, a name is generated using the fullname template. |
"" |
serviceAccount.automountServiceAccountToken |
Whether to automount the SA token inside the pod | false |
| Parameter | Description | Default |
|---|---|---|
extraEnvVars |
Additional environment variables to set | [] |
extraEnvVarsSecret |
Name of an existing secret containing additional environment variables | `` |
| Parameter | Description | Default |
|---|---|---|
extraObjects |
Array of extra objects to deploy with the release | [] |
| Parameter | Description | Default |
|---|---|---|
initContainers.copyQuarkusLib.resources |
Resource requests and limits for the copy-quarkus-lib init container | {} |
initContainers.waitForPostgres.image |
Full image override for PostgreSQL init container | "" |
initContainers.waitForPostgres.registry |
PostgreSQL image registry (overrides global.imageRegistry) | "" |
initContainers.waitForPostgres.repository |
PostgreSQL image repository | postgres |
initContainers.waitForPostgres.tag |
PostgreSQL image tag | "18.3@sha256:69e8582b781cb44fa4557b98ed586fe68361e320d9b12f9707494335634f4f3d" |
initContainers.waitForPostgres.pullPolicy |
PostgreSQL image pull policy | IfNotPresent |
initContainers.waitForPostgres.resources |
Resource requests and limits for PostgreSQL init container | {} |
initContainers.waitForMariadb.image |
Full image override for MariaDB init container | "" |
initContainers.waitForMariadb.registry |
MariaDB image registry (overrides global.imageRegistry) | "" |
initContainers.waitForMariadb.repository |
MariaDB image repository | mariadb |
initContainers.waitForMariadb.tag |
MariaDB image tag | "12.2.2@sha256:b1cb255a9939d28a1856815f0de6046c20c28c21b92a9f2696bc782b247a47ee" |
initContainers.waitForMariadb.pullPolicy |
MariaDB image pull policy | IfNotPresent |
initContainers.waitForMariadb.resources |
Resource requests and limits for MariaDB init container | {} |
Init containers support the global image registry configuration and can also be overridden individually per container.
Init container resource requests and limits can be configured individually.
Example:
initContainers:
waitForPostgres:
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi| Parameter | Description | Default |
|---|---|---|
postgres.enabled |
Enable embedded PostgreSQL database | true |
postgres.auth.database |
PostgreSQL database name | "keycloak" |
postgres.auth.username |
PostgreSQL database user (leave empty for default 'postgres') | "" |
postgres.auth.password |
PostgreSQL database password | "" |
postgres.auth.existingSecret |
Existing secret containing PostgreSQL credentials | "" |
| Parameter | Description | Default |
|---|---|---|
mariadb.enabled |
Enable embedded MariaDB database | false |
mariadb.auth.database |
MariaDB database name | "keycloak" |
mariadb.auth.username |
MariaDB database user (leave empty for root user) | "" |
mariadb.auth.password |
MariaDB database password | "" |
mariadb.auth.rootPassword |
MariaDB root password | "" |
mariadb.auth.existingSecret |
Existing secret containing MariaDB root and user passwords | "" |
mariadb.auth.secretKeys |
Secret keys for MariaDB credentials | {rootPasswordKey: mariadb-root-password, userPasswordKey: mariadb-password} |
You can use the extraObjects array to deploy additional Kubernetes resources (such as NetworkPolicies, ConfigMaps, etc.) alongside the release. This is useful for customizing your deployment with extra manifests that are not covered by the default chart options.
Helm templating is supported in any field, but all template expressions must be quoted. For example, to use the release namespace, write namespace: "{{ .Release.Namespace }}".
Example: Deploy a NetworkPolicy with templating
extraObjects:
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: "{{ .Release.Namespace }}"
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCPAll objects in extraObjects will be rendered and deployed with the release. You can use any valid Kubernetes manifest, and reference Helm values or built-in objects as needed (just remember to quote template expressions).
Deploy Keycloak with default configuration:
helm install my-keycloak ./charts/keycloak# values-production.yaml
keycloak:
adminPassword: "secure-admin-password"
hostname: "https://auth.yourdomain.com"
proxyHeaders: "xforwarded" # required when running behind nginx ingress
database:
type: "postgres"
host: "postgres.database.svc.cluster.local"
port: "5432"
name: "keycloak"
username: "keycloak"
password: "secure-db-password"
# Disable embedded databases
postgres:
enabled: false
mariadb:
enabled: false
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: auth.yourdomain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: keycloak-tls
hosts:
- auth.yourdomain.comDeploy with production values:
helm install my-keycloak ./charts/keycloak -f values-production.yaml# values-development.yaml
keycloak:
adminPassword: "admin"
production: false # overrides default — enables start-dev mode for local development
postgres:
enabled: true
auth:
database: "keycloak"
password: "keycloak-db-password"
ingress:
enabled: true
className: "nginx"
hosts:
- host: keycloak.local
paths:
- path: /
pathType: Prefix
persistence:
enabled: true
size: 5Gi# values-external-secret.yaml
keycloak:
existingSecret: "keycloak-credentials"
secretKeys:
adminPasswordKey: "admin-password"
database:
type: "postgres"
host: "postgres.database.svc.cluster.local"
existingSecret: "keycloak-db-credentials"
secretKeys:
passwordKey: "db-password"
usernameKey: "db-username"
# Disable embedded databases
postgres:
enabled: false
mariadb:
enabled: falseCreate the secrets first:
kubectl create secret generic keycloak-credentials \
--from-literal=admin-password=your-admin-password
kubectl create secret generic keycloak-db-credentials \
--from-literal=db-password=your-db-password \
--from-literal=db-username=keycloakrealm:
import: true
configFile: |
{
"realm": "my-realm",
"enabled": true
}For production use, it is recommended to store realm configuration in a Kubernetes Secret.
realm:
import: true
existingSecret: my-realm-secretThe Keycloak deployment automatically mounts empty directories at /opt/keycloak/themes and /opt/keycloak/providers. You can use initContainers to copy custom themes and providers into these directories.
To disable this feature, set preserveThemes = true, preserveProviders = true. This will keep the original themes and providers respectively intact.
Example: Adding custom themes and providers with an initContainer
# values-custom-themes.yaml
extraInitContainers:
- name: add-custom-themes
image: your-registry/keycloak-themes:latest
imagePullPolicy: Always
command:
- sh
- -c
- |
cp -r /themes/* /opt/keycloak/themes/
cp -r /providers/* /opt/keycloak/providers/
volumeMounts:
- name: keycloak-themes
mountPath: /opt/keycloak/themes
- name: keycloak-providers
mountPath: /opt/keycloak/providersIn this example:
- Create a Docker image containing your custom themes in
/themesand providers in/providers - The initContainer copies these files to the mounted volumes
- Keycloak will automatically detect and use them on startup
You can also use this approach to download themes/providers from external sources:
extraInitContainers:
- name: download-themes
image: curlimages/curl:latest
command:
- sh
- -c
- |
curl -L -o /tmp/theme.zip https://example.com/theme.zip
unzip /tmp/theme.zip -d /opt/keycloak/themes/
volumeMounts:
- name: keycloak-themes
mountPath: /opt/keycloak/themesThe extraInitContainers, extraVolumes, extraVolumeMounts, extraEnvVars, and extraContainers fields support Helm template expressions. This enables dynamic image references that respect global.imageRegistry, which is useful in air-gapped or on-premises environments where all images must come from an internal registry.
extraInitContainers:
- name: custom-themes
image: '{{ printf "%s/%s:%s" .Values.global.imageRegistry "my-themes" "1.0.0" }}'
command: ["sh", "-c", "cp -r /themes/* /opt/keycloak/themes/"]
volumeMounts:
- name: keycloak-themes
mountPath: /opt/keycloak/themesYou can also use the built-in keycloak.initContainerImage helper for structured image configuration:
customInit:
registry: ""
repository: my-themes
tag: "1.0.0"
extraInitContainers:
- name: custom-themes
image: '{{ include "keycloak.initContainerImage" (dict "config" .Values.customInit "global" .Values.global) }}'
command: ["sh", "-c", "cp -r /themes/* /opt/keycloak/themes/"]
volumeMounts:
- name: keycloak-themes
mountPath: /opt/keycloak/themesWhen global.imageRegistry is set, the image resolves to <global.imageRegistry>/my-themes:1.0.0. When not set, it uses the customInit.registry or just my-themes:1.0.0.
Note: Values without template syntax are rendered unchanged — this feature is fully backward-compatible.
Automatically provision and renew certificates using cert-manager:
# values-tls-certmanager.yaml
tls:
enabled: true
certManager:
enabled: true
issuerRef:
name: "letsencrypt-prod"
kind: ClusterIssuer
# dnsNames will be automatically populated from ingress.hosts if not specified
dnsNames:
- auth.yourdomain.com
keycloak:
httpEnabled: false # Disable HTTP when using TLS
hostname: "https://auth.yourdomain.com"
ingress:
enabled: true
className: "nginx"
hosts:
- host: auth.yourdomain.com
paths:
- path: /
pathType: PrefixPrerequisites:
- cert-manager must be installed in your cluster
- A ClusterIssuer or Issuer must be configured (e.g., Let's Encrypt)
Install cert-manager if not already installed:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yamlCreate a ClusterIssuer for Let's Encrypt:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginxUse pre-existing certificates:
# values-tls.yaml
tls:
enabled: true
existingSecret: "keycloak-tls-certs"
keycloak:
httpEnabled: false # Disable HTTP when using TLS
hostname: "https://auth.yourdomain.com"Create the TLS secret first with your certificate and key in PEM format:
kubectl create secret tls keycloak-tls-certs \
--cert=/path/to/certificate.pem \
--key=/path/to/private-key.pem# values-tls-truststore.yaml
tls:
enabled: true
existingSecret: "keycloak-tls-certs"
truststoreEnabled: true
truststoreExistingSecret: "keycloak-truststore"
truststorePassword: "changeit"Create the truststore secret:
kubectl create secret generic keycloak-truststore \
--from-file=truststore.jks=/path/to/truststore.jks# values-ha.yaml
replicaCount: 3
keycloak:
adminPassword: "secure-admin-password"
hostname: "https://auth.yourdomain.com"
proxyHeaders: "xforwarded" # required when running behind nginx ingress
cache:
stack: "ispn" # Use Infinispan for clustering
database:
type: "postgres"
host: "postgres-ha.database.svc.cluster.local"
name: "keycloak"
username: "keycloak"
password: "secure-db-password"
# Disable embedded databases
postgres:
enabled: false
mariadb:
enabled: false
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- keycloak
topologyKey: kubernetes.io/hostnamekubectl port-forward service/my-keycloak 8080:8080Navigate to http://localhost:8080 and log in with the admin credentials.
- Admin User:
admin(configurable) - Admin Password: Auto-generated (check secret) or configured value
Get the auto-generated admin password:
kubectl get secret my-keycloak -o jsonpath="{.data.admin-password}" | base64 --decode-
Pod fails to start with database connection errors
- Verify database connection parameters
- Ensure the database is running and accessible
- Check database credentials in secrets
- Review pod logs:
kubectl logs <pod-name>
-
Cannot access Keycloak via ingress
- Verify ingress configuration and annotations
- Check if ingress controller is installed
- Ensure DNS resolves to the correct IP
- Check TLS certificate configuration
-
Admin login fails
- Verify admin password in the secret
- Check if the admin user exists in the database
- Review Keycloak logs for authentication errors
-
Database initialization fails
- Ensure database schema permissions are correct
- Check if the database already exists and is empty
- Verify database connection and credentials
- Review init container logs
-
Memory Configuration
resources: requests: memory: "2Gi" cpu: "1000m" limits: memory: "4Gi" cpu: "2000m"
-
JVM Settings (via extraEnv)
extraEnv: JAVA_OPTS: "-Xms1024m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
-
Database Connection Pool
database: jdbcParams: "?prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048"
For issues related to this Helm chart, please check: