Skip to content

Keycloak

Deploy Keycloak on Kubernetes for identity and access management, SSO, OAuth 2.0, OpenID Connect, and SAML. The chart supports explicit dev and production modes, Keycloak 26.6.1, bundled or external PostgreSQL/MySQL-compatible databases, optional Gateway API routes, External Secrets Operator integration, Prometheus metrics, S3-compatible database backup, and dual-stack Service fields.

Key Features

  • Explicit runtime modemode: dev for local testing and mode: production for hostname/database-backed deployments.
  • Keycloak 26.6.x alignment — image tag and app version use quay.io/keycloak/keycloak:26.6.1.
  • Database choices — embedded H2 for dev only, bundled PostgreSQL/MySQL subcharts, or external PostgreSQL/MySQL/MariaDB.
  • HA-ready controls — JDBC_PING cache stack, multi-replica scheduling defaults, PDB, rollout strategy, and capacity profiles.
  • Routing options — separate public/admin Ingress resources plus optional Gateway API HTTPRoute resources.
  • Security integration — non-root defaults, configurable ServiceAccount token mount, truststore controls, NetworkPolicy ingress and egress, and External Secrets Operator opt-in.
  • Observability — health probes, management service, metrics endpoint, ServiceMonitor, telemetry, tracing, and structured logging controls.
  • Backup — database-only PostgreSQL/MySQL dump CronJob uploaded to S3-compatible storage.

Architecture

Production

Public traffic reaches the Keycloak application service through Ingress or Gateway API. Health and metrics stay on the private management service.

Users HTTPS/OIDC Ingress or HTTPRoute App Service :8080 Keycloak Deployment 26.6.1 Management health/metrics :9000 PostgreSQL bundled or external ExternalSecret optional S3 Backup database dump

High Availability

Multiple Keycloak pods share the same database, use JDBC_PING for cache discovery, and can expose optional backup and External Secrets flows.

Users HTTPS/OIDC Ingress or HTTPRoute App Service dual-stack optional Keycloak pod replica 1 Keycloak pod replica n JDBC_PING/cache Management private :9000 PostgreSQL shared state NetworkPolicy ingress/egress S3 backup

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install keycloak helmforge/keycloak -f values.yaml

OCI registry:

helm install keycloak oci://ghcr.io/helmforgedev/helm/keycloak -f values.yaml
Production mode requires a hostname and a real database

mode: production fails fast unless hostname.hostname is set and one database path is configured through postgresql.enabled, mysql.enabled, or database.external.host.

Deployment Examples

# values.yaml - dev mode with embedded H2
mode: dev

admin:
  username: admin
  password: 'change-me'
Embedded H2 is not a production database

Dev mode is useful for local validation only. Use PostgreSQL, MySQL, MariaDB, or the PostgreSQL/MySQL subchart for production.

# values.yaml - production with external PostgreSQL
mode: production

hostname:
  hostname: https://sso.example.com

admin:
  existingSecret: keycloak-admin

database:
  external:
    vendor: postgres
    host: postgresql.database.svc.cluster.local
    port: 5432
    name: keycloak
    username: keycloak
    existingSecret: keycloak-db
    existingSecretPasswordKey: db-password
    pool:
      initialSize: 5
      minSize: 5
      maxSize: 30
    logSlowQueriesThreshold: 500ms
# values.yaml - HA with PostgreSQL subchart
mode: production
replicaCount: 3

hostname:
  hostname: https://sso.example.com

postgresql:
  enabled: true
  auth:
    database: keycloak
    username: keycloak
    password: 'strong-db-password'

cache:
  enabled: true
  stack: jdbc-ping

capacity:
  profile: medium

pdb:
  enabled: true
  minAvailable: 1
# values.yaml - public and admin ingress
mode: production

hostname:
  hostname: https://sso.example.com
  admin: https://admin-sso.example.com

database:
  external:
    host: postgresql.database.svc.cluster.local
    password: 'db-password'

proxy:
  headers: xforwarded
  trustedAddresses: 10.0.0.0/8,192.168.0.0/16

ingress:
  public:
    enabled: true
    ingressClassName: traefik
    hosts:
      - host: sso.example.com
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: keycloak-tls
        hosts:
          - sso.example.com
  admin:
    enabled: true
    ingressClassName: traefik
    hosts:
      - host: admin-sso.example.com
        paths:
          - path: /
            pathType: Prefix
# values.yaml - metrics, logs, backup, and network policy
mode: production

hostname:
  hostname: https://sso.example.com

postgresql:
  enabled: true
  auth:
    database: keycloak
    username: keycloak
    password: 'strong-db-password'

metrics:
  enabled: true
  userEvents: true
  serviceMonitor:
    enabled: true
    interval: 30s

logging:
  console:
    output: json
    jsonFormat: ecs
  access:
    enabled: true

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.example.com
    bucket: keycloak-backups
    existingSecret: keycloak-s3

networkPolicy:
  enabled: true
  egress:
    enabled: true
    databaseTo:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: database
# values.yaml - Gateway API and External Secrets Operator
mode: production

hostname:
  hostname: https://sso.example.com

database:
  external:
    host: postgresql.database.svc.cluster.local
    existingSecret: keycloak-db

gateway:
  public:
    enabled: true
    parentRefs:
      - name: public-gateway
        namespace: gateway-system
    hostnames:
      - sso.example.com

externalSecrets:
  enabled: true
  secretStoreRef:
    name: platform-secrets
    kind: ClusterSecretStore
  admin:
    enabled: true
    usernameRemoteRef:
      key: keycloak/admin
      property: username
    passwordRemoteRef:
      key: keycloak/admin
      property: password
  database:
    enabled: true
    passwordRemoteRef:
      key: keycloak/database
      property: password

Configuration Reference

Core and Image

ParameterTypeDefaultDescription
modestringdevRuntime mode: dev or production.
nameOverridestring""Override chart name.
fullnameOverridestring""Override full release name.
commonLabelsobject{}Labels added to all resources.
clusterDomainstringcluster.localKubernetes cluster DNS domain.
image.repositorystringquay.io/keycloak/keycloakKeycloak image repository.
image.tagstring"26.6.1"Keycloak image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

Bootstrap Admin

ParameterTypeDefaultDescription
admin.usernamestringadminBootstrap admin username.
admin.passwordstring""Bootstrap admin password. Generated when empty and no existing secret is set.
admin.existingSecretstring""Existing Kubernetes Secret for bootstrap admin credentials.
admin.existingSecretUsernameKeystringadmin-usernameUsername key in the admin secret.
admin.existingSecretPasswordKeystringadmin-passwordPassword key in the admin secret.

HTTP, Management, Hostname, and Proxy

ParameterTypeDefaultDescription
http.enabledbooleantrueKeep HTTP enabled inside the cluster.
http.portinteger8080Application HTTP port.
http.managementPortinteger9000Management interface port for health and metrics.
http.relativePathstring/Application relative path, for example /auth.
management.healthEnabledbooleantrueServe health endpoints from the management interface.
management.relativePathstring""Optional management interface relative path.
hostname.hostnamestring""Public Keycloak hostname or URL. Required in production.
hostname.adminstring""Dedicated admin hostname or URL.
hostname.strictbooleantrueEnforce strict hostname handling.
hostname.backchannelDynamicbooleanfalseEnable dynamic backchannel URL resolution.
proxy.headersstringxforwardedProxy header mode: xforwarded, forwarded, or empty.
proxy.trustedAddressesstring""Trusted proxy IPs/CIDRs for KC_PROXY_TRUSTED_ADDRESSES.
proxy.protocolEnabledbooleanfalseEnable HAProxy PROXY protocol. Cannot be combined with proxy.headers.

Database

ParameterTypeDefaultDescription
database.external.vendorstringpostgresExternal database vendor: postgres, mysql, or mariadb.
database.external.hoststring""External database host.
database.external.portstring/integer""External database port. Auto-detected from vendor when empty.
database.external.namestringkeycloakExternal database name.
database.external.usernamestringkeycloakExternal database username.
database.external.passwordstring""External database password. Generated when empty and no existing secret is set.
database.external.existingSecretstring""Existing Secret containing database password.
database.external.existingSecretPasswordKeystringdb-passwordPassword key in the existing Secret.
database.external.jdbcParametersstring""Additional JDBC URL parameters.
database.external.schemastring""Optional database schema passed to KC_DB_SCHEMA.
database.external.pool.initialSizestring/integer""Initial DB connection pool size.
database.external.pool.minSizestring/integer""Minimum DB connection pool size.
database.external.pool.maxSizestring/integer""Maximum DB connection pool size.
database.external.pool.maxLifetimestring""Maximum DB connection lifetime.
database.external.logSlowQueriesThresholdstring""Slow query log threshold.
database.external.transaction.xaEnabledstring/boolean""Enable or disable XA transactions.
database.external.transaction.timeoutstring""Transaction timeout.
postgresql.enabledbooleanfalseEnable the bundled PostgreSQL chart.
postgresql.auth.databasestringkeycloakPostgreSQL database name.
postgresql.auth.usernamestringkeycloakPostgreSQL username.
postgresql.auth.passwordstring""PostgreSQL password. Required for deterministic subchart connections.
mysql.enabledbooleanfalseEnable the bundled MySQL chart.
mysql.auth.databasestringkeycloakMySQL database name.
mysql.auth.usernamestringkeycloakMySQL username.
mysql.auth.passwordstring""MySQL password. Required for deterministic subchart connections.

Database TLS and Truststore

ParameterTypeDefaultDescription
database.tls.enabledbooleanfalseEnable database TLS settings.
database.tls.modestring""Keycloak database TLS mode.
database.tls.sslModestringverify-fullPostgreSQL SSL mode.
database.tls.mountPathstring/opt/keycloak/conf/db-certsMount path for DB TLS material.
database.tls.rootCertFilenamestringca.crtRoot CA filename.
database.tls.existingSecretstring""Secret holding DB CA material.
database.tls.existingConfigMapstring""ConfigMap holding DB CA material.
database.tls.trustStoreFilestring""DB TLS truststore file path.
database.tls.trustStorePasswordSecretstring""Secret containing DB TLS truststore password.
database.tls.trustStorePasswordKeystringpasswordPassword key for DB TLS truststore.
database.tls.trustStoreTypestring""DB TLS truststore type.
truststore.enabledbooleanfalseEnable additional outbound TLS trust material.
truststore.mountPathstring/opt/keycloak/conf/truststores/customCustom truststore mount path.
truststore.existingSecretstring""Secret with PEM or unencrypted PKCS12 trust material.
truststore.existingConfigMapstring""ConfigMap with PEM or unencrypted PKCS12 trust material.
truststore.tlsHostnameVerifierstringDEFAULTOutbound TLS hostname verifier mode.
truststore.kubernetes.enabledbooleantrueLet Keycloak trust Kubernetes/OpenShift service account CA files when mounted.

Runtime, HA, and Capacity

ParameterTypeDefaultDescription
replicaCountinteger1Number of Keycloak replicas.
cache.enabledbooleantrueEnable distributed cache settings in production mode.
cache.stackstringjdbc-pingCache stack used for multi-replica production deployments.
cache.multiReplicaDefaults.enabledbooleantrueApply scheduling defaults for multi-replica deployments.
cache.multiReplicaDefaults.podAntiAffinitystringpreferredAutomatic anti-affinity mode: preferred, required, or none.
cache.multiReplicaDefaults.topologySpread.enabledbooleantrueApply automatic topology spread for multi-replica deployments.
features.enabledarray[]Keycloak features rendered as KC_FEATURES.
features.disabledarray[]Keycloak features rendered as KC_FEATURES_DISABLED.
optimized.enabledbooleanfalseAdd --optimized; use only with pre-built/custom optimized images in production.
deployment.strategy.typestringRollingUpdateDeployment strategy: RollingUpdate or Recreate.
deployment.strategy.rollingUpdate.maxUnavailablestring/integer0Maximum unavailable pods during rolling update.
deployment.strategy.rollingUpdate.maxSurgestring/integer1Maximum surge pods during rolling update.
resourcesobject{}Custom container resources when capacity.profile=custom.
capacity.profilestringcustomResource profile: custom, small, medium, or large.
capacity.profilesobjectbuilt-in profilesProfile definitions for small, medium, and large.

Realm Import, Providers, and Themes

ParameterTypeDefaultDescription
realmImport.enabledbooleanfalseEnable startup realm import using --import-realm.
realmImport.filesobject{}Inline realm JSON files written to a generated ConfigMap.
realmImport.existingConfigMapstring""Existing ConfigMap mounted into /opt/keycloak/data/import.
extensions.providers.existingConfigMapstring""ConfigMap mounted into /opt/keycloak/providers.
extensions.providers.existingSecretstring""Secret mounted into /opt/keycloak/providers.
extensions.providers.rolloutTokenstring""Manual rollout token for provider changes.
extensions.themes.existingConfigMapstring""ConfigMap mounted into /opt/keycloak/themes.
extensions.themes.existingSecretstring""Secret mounted into /opt/keycloak/themes.
extensions.themes.rolloutTokenstring""Manual rollout token for theme changes.

Services, Ingress, and Gateway API

ParameterTypeDefaultDescription
service.typestringClusterIPApplication Service type.
service.annotationsobject{}Application Service annotations.
service.managementAnnotationsobject{}Management Service annotations.
service.ipFamilyPolicystring""Application Service IP family policy.
service.ipFamiliesarray[]Application Service IP families.
service.managementIpFamilyPolicystring""Management Service IP family policy.
service.managementIpFamiliesarray[]Management Service IP families.
ingress.public.enabledbooleanfalseCreate public Ingress.
ingress.public.ingressClassNamestringtraefikPublic Ingress class.
ingress.public.hostsarray[]Public host/path rules.
ingress.public.tlsarray[]Public TLS configuration.
ingress.admin.enabledbooleanfalseCreate admin Ingress.
ingress.admin.ingressClassNamestringtraefikAdmin Ingress class.
ingress.admin.hostsarray[]Admin host/path rules.
ingress.admin.tlsarray[]Admin TLS configuration.
gateway.public.enabledbooleanfalseCreate public Gateway API HTTPRoute.
gateway.public.parentRefsarray[]Required parentRefs when public Gateway is enabled.
gateway.public.hostnamesarray[]Public route hostnames.
gateway.admin.enabledbooleanfalseCreate admin Gateway API HTTPRoute.
gateway.admin.parentRefsarray[]Required parentRefs when admin Gateway is enabled.
gateway.admin.hostnamesarray[]Admin route hostnames.

Health, Metrics, Telemetry, and Logs

ParameterTypeDefaultDescription
health.enabledbooleantrueEnable Keycloak health endpoints.
probes.profilestringdefaultProbe profile: default or heavy-startup.
probes.liveness.enabledbooleantrueEnable liveness probe.
probes.readiness.enabledbooleantrueEnable readiness probe.
probes.startup.enabledbooleantrueEnable startup probe.
metrics.enabledbooleanfalseEnable Keycloak metrics endpoint.
metrics.userEventsbooleanfalseEnable user event metrics.
metrics.cacheHistogramsbooleanfalseEnable cache metrics histograms.
metrics.serviceMonitor.enabledbooleanfalseCreate a ServiceMonitor targeting the management service.
metrics.serviceMonitor.intervalstring30sServiceMonitor scrape interval.
metrics.serviceMonitor.scrapeTimeoutstring""Optional scrape timeout.
metrics.serviceMonitor.relabelingsarray[]Prometheus relabeling rules.
metrics.serviceMonitor.metricRelabelingsarray[]Prometheus metric relabeling rules.
telemetry.metricsEnabledbooleanfalseEnable OpenTelemetry metrics export.
telemetry.endpointstring""General OpenTelemetry endpoint.
telemetry.metricsEndpointstring""Metrics-specific OpenTelemetry endpoint.
tracing.enabledbooleanfalseEnable OpenTelemetry tracing.
tracing.endpointstring""Tracing endpoint.
tracing.samplerTypestring""Tracing sampler type.
tracing.samplerRatiostring/number""Tracing sampler ratio.
logging.levelstringinfoGlobal Keycloak log level.
logging.console.outputstringdefaultConsole output mode: default or json.
logging.console.jsonFormatstringdefaultJSON log format: default or ecs.
logging.access.enabledbooleanfalseEnable HTTP access logs.

Backup

ParameterTypeDefaultDescription
backup.enabledbooleanfalseEnable database backup CronJob.
backup.schedulestring"0 3 * * *"Backup schedule.
backup.suspendbooleanfalseSuspend scheduled execution.
backup.concurrencyPolicystringForbidCronJob concurrency policy.
backup.archivePrefixstringkeycloakPrefix for generated archive names.
backup.images.uploader.repositorystringdocker.io/helmforge/mcS3 uploader image.
backup.images.uploader.tagstring"1.0.0"S3 uploader image tag.
backup.s3.endpointstring""S3-compatible endpoint URL.
backup.s3.bucketstring""Target bucket name.
backup.s3.prefixstringkeycloakObject key prefix.
backup.s3.createBucketIfNotExistsbooleantrueCreate bucket when missing.
backup.s3.existingSecretstring""Existing Secret with S3 credentials.
backup.database.postgresDumpArgsstring--clean --if-existsExtra pg_dump arguments.
backup.database.mysqlDumpArgsstring--single-transaction --quick --skip-lock-tables --no-tablespacesExtra mysqldump arguments.

Security, Scheduling, and Extension Points

ParameterTypeDefaultDescription
pdb.enabledbooleanfalseCreate PodDisruptionBudget.
pdb.minAvailableinteger1Minimum available pods.
pdb.maxUnavailablestring""Maximum unavailable pods.
networkPolicy.enabledbooleanfalseCreate NetworkPolicy for Keycloak pods.
networkPolicy.ingress.allowSameNamespacebooleantrueAllow app traffic from the same namespace.
networkPolicy.management.allowSameNamespacebooleantrueAllow management traffic from the same namespace.
networkPolicy.clustering.enabledbooleantrueAllow Keycloak pod-to-pod cache transport when multi-replica.
networkPolicy.egress.enabledbooleanfalseAdd egress rules.
networkPolicy.egress.allowDnsbooleantrueAllow TCP/UDP DNS egress.
networkPolicy.egress.allowDatabasebooleantrueAllow DB egress when databaseTo is configured.
networkPolicy.egress.databaseToarray[]Egress destination peers for database traffic.
networkPolicy.egress.extraEgressarray[]Additional egress rules.
serviceAccount.createbooleanfalseCreate a ServiceAccount.
serviceAccount.namestring""ServiceAccount name override.
serviceAccount.automountServiceAccountTokenbooleantrueMount the Kubernetes ServiceAccount token and CA into the pod.
podSecurityContextobjectnon-root defaultsPod-level security context.
securityContextobjectnon-root defaultsContainer-level security context.
nodeSelectorobject{}Node selector.
tolerationsarray[]Pod tolerations.
affinityobject{}Custom affinity.
topologySpreadConstraintsarray[]Custom topology spread constraints.
priorityClassNamestring""PriorityClass name.
terminationGracePeriodSecondsinteger120Pod termination grace period.
extraEnvarray[]Extra environment variables.
extraEnvFromarray[]Extra envFrom sources.
initContainersarray[]Extra init containers.
extraContainersarray[]Extra sidecars.
extraVolumesarray[]Extra pod volumes.
extraVolumeMountsarray[]Extra Keycloak volume mounts.

External Secrets Operator

ParameterTypeDefaultDescription
externalSecrets.enabledbooleanfalseRender ExternalSecret resources for clusters that already run External Secrets Operator.
externalSecrets.apiVersionstringexternal-secrets.io/v1ExternalSecret API version.
externalSecrets.refreshIntervalstring1hRefresh interval.
externalSecrets.secretStoreRef.namestring""Existing SecretStore or ClusterSecretStore name. Required when enabled.
externalSecrets.secretStoreRef.kindstringSecretStoreStore kind: SecretStore or ClusterSecretStore.
externalSecrets.admin.enabledbooleanfalseMaterialize bootstrap admin Secret through ESO.
externalSecrets.database.enabledbooleanfalseMaterialize database password Secret through ESO.
externalSecrets.truststore.enabledbooleanfalseMaterialize truststore Secret through ESO.
External Secrets is optional

The chart does not install External Secrets Operator and does not create SecretStore or ClusterSecretStore resources. Enable this only on clusters where the operator and store already exist.

Operational Notes

  • Chart.yaml pins PostgreSQL chart 1.9.13 and MySQL chart 1.8.7.
  • Chart.lock is versioned for reproducible dependency builds.
  • Built-in backup is database-only. Back up providers, themes, realm import files, truststores, and external secret backends through their own source-of-truth.
  • The management service is private and should not be exposed through public ingress.
  • Gateway API routes target the application service only; they do not route the management service.
  • Dual-stack fields are optional and omitted by default so the cluster default remains authoritative.
  • serviceAccount.automountServiceAccountToken=false can harden the pod, but it also removes the default Kubernetes CA material used by Keycloak truststore auto-discovery.

Official References