Skip to main content

Overview

Keycloak is an open-source identity and access management platform that provides single sign-on, OIDC and SAML identity federation, user federation (LDAP and Active Directory), social login, and fine-grained authorization. This template runs Keycloak 26 in production mode, clustered across two replicas by default, with a highly available PostgreSQL backing store so that restarts and upgrades do not interrupt sign-ins. A lighter single-instance PostgreSQL store is available for dev/test. All durable state — realms, users, and active sessions — lives in PostgreSQL. The Keycloak tier itself keeps nothing on disk, so it has no volume set of its own.
This template deploys into an existing GVC. It does not create or manage one. Every resource lands in the GVC you install into, so cpln workload exec, cpln logs and uninstalling all work against that GVC.

What Gets Created

Prerequisites

Two dictionary secrets must exist before you install. The database credentials and the bootstrap admin login (which guards a login form on the public endpoint) are prerequisite secrets rather than template values. The template creates neither; it references yours by name and grants the workload reveal on exactly those secrets.
1

Create the database credentials secret

A dictionary secret holding username, password and database. The bundled PostgreSQL initializes with them and Keycloak connects with them:
Set its name in the key of the store you use: postgresHA.config.credentialsSecretName (HA, the default) or postgres.config.credentialsSecretName (single-instance). Secret names are org-wide, so give each release its own. database must be URL-safe (letters, digits, _) because Keycloak’s JDBC URL is assembled from it at container start. PostgreSQL reads the secret only when its data is first initialized — see Rotating the Database Password.
2

Create the admin credentials secret

A dictionary secret holding exactly the keys username and password. Secrets are org-level, so no GVC flag is involved:
Set admin.secretName to this name.
3

Read the password back when you need it

-o yaml is required; without it the command prints only a summary table:
A missing prerequisite secret wedges the install silently. The install exits 0, every resource is created, and the Keycloak workload then never starts. Because the container never ran, cpln logs returns zero lines, which reads as a broken platform rather than a missing step.The only place the missing secret is named is status.versions[].message:
The message names whichever secret is missing — the admin secret or the database credentials secret. It is get-deployments — plain cpln workload get has no versions key. Once you create the secret the deployment recovers on its own; to skip the wait, force a redeployment:
Optional database backups need a bucket plus a cloud account (AWS or GCP) or a MinIO credentials secret; see Backing Up.

Installation

Install from the marketplace registry, naming your two secrets:
For the single-instance store set postgres.config.credentialsSecretName instead. To install using another method, follow the instructions for your preferred tool:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Keycloak

  • replicas — 2 or more runs Keycloak as a cluster: the replicas discover each other through the shared database, so a single replica restarting does not interrupt sign-ins. 1 runs with clustering disabled, for dev/test. Clustered replicas talk to each other on ports 7800 and 57800, so replicas above 1 needs an internalAccess.type other than none — the chart refuses the combination at render.
  • resources — Per-replica CPU and memory. The JVM heap is sized to 70% of maxMemory; keep it at 1.5Gi or above. The block exposes both a reservation (minCpu, minMemory) and a limit (maxCpu, maxMemory).

Admin Bootstrap

  • admin.secretName — The dictionary secret you created in Prerequisites. Keycloak reads it on first boot only, to create a temporary bootstrap admin when no admin account exists yet. Editing the secret on a running install changes nothing; change the password in the admin console instead, and remove the temporary account once you have created a permanent one (see First Run).

Database Credentials

  • postgresHA.config.credentialsSecretName / postgres.config.credentialsSecretName — Name of the dictionary secret you created in Prerequisites, holding username, password and database. Set it in the block of the store you enable; both Keycloak and the database read that secret.

Backing Store

Exactly one of the two stores must be enabled; the chart refuses both-on and both-off at render. Highly available PostgreSQL (default). Three Patroni-managed PostgreSQL replicas, three etcd replicas and an HAProxy endpoint that routes Keycloak’s connections to the current leader, from the PostgreSQL Highly Available template:
Single-instance PostgreSQL (dev/test). One PostgreSQL replica from the PostgreSQL template:
  • postgresHA.config.credentialsSecretName / postgres.config.credentialsSecretName — Name of your database credentials secret (see Database Credentials). Secret names are organization-wide, so give each release its own.
  • postgresHA.proxy.enabled must stay true — Keycloak writes through the HAProxy leader endpoint, and the chart refuses the install at render if it is disabled.
  • Any other setting of the backing template can be set under the same prefix (postgresHA.pgbouncer.*, postgresHA.etcd.*, postgres.pgbouncer.*, volume-set autoscaling, and so on); see the backing template’s page for those keys.

Access

  • publicAccess.enabled — Exposes Keycloak over HTTPS at the automatically assigned canonical *.cpln.app endpoint. It is on by default because Keycloak is the identity provider your applications’ users sign in to: browsers must reach its login and OIDC endpoints for SSO to work at all. Disable it only for pure service-to-service deployments; internal access keeps working. An access change can take a couple of minutes to take effect, so re-test rather than trusting the first response.
  • internalAccess.type — Which workloads can reach Keycloak inside Control Plane (none, same-gvc, same-org, or workload-list). With workload-list, list the allowed workloads in internalAccess.workloads.

Connecting

To verify the server is answering without depending on public access, forward its HTTP port and fetch the master realm’s discovery document:

First Run

1

Open the admin console

Browse to https://CANONICAL_ENDPOINT/admin. With publicAccess.enabled: false, forward the port as above and use http://localhost:8080/admin instead.
2

Sign in with the bootstrap admin

Use the username and password from your admin secret (cpln secret reveal ADMIN_SECRET_NAME -o yaml).
3

Create a permanent admin and remove the temporary one

The bootstrap account is temporary by design. In the master realm, create a permanent admin user with the admin role, sign in as that user, and delete the bootstrap account. Keycloak shows a warning in the console until the temporary account is gone.
4

Create a realm for your applications

Keep the master realm for administration and register your applications as clients in a realm of their own. Their OIDC discovery URL is the one in the table above, with REALM_NAME replaced.

Operations

Backing Up

Backups are a pass-through to the backing store’s backup feature: everything under postgresHA.backup is handed to the PostgreSQL Highly Available template, and everything under postgres.backup to the PostgreSQL template. Set enabled: true and fill in the section for your provider.
Provider setup. The bucket, cloud account and IAM policy for aws and gcp are set up exactly as for the backing template — follow the PostgreSQL Highly Available backup prerequisites for the default store or the PostgreSQL backup prerequisites for the single-instance store, and use the key names shown above (prefixed with postgresHA. or postgres.). Backup modes and schedules are described under PostgreSQL Highly Available — Backup and PostgreSQL — Backup. MinIO is the one provider with a prerequisite secret. Before enabling provider: minio, create a dictionary secret holding exactly the keys accessKey and secretKey, and set postgresHA.backup.minio.credentialsSecretName (or postgres.backup.minio.credentialsSecretName) to its name. For the MinIO template these are its admin.username and admin.password:
Like the admin and database credentials secrets, a MinIO credentials secret that does not exist at install time wedges the backup workload silently; the diagnostic is the same status.versions[].message described in Prerequisites.

Restoring a Backup

Keycloak keeps no state outside PostgreSQL, so restoring Keycloak means restoring its database. Follow the backing store’s restore procedure — PostgreSQL Highly Available — Restoring a Backup for the default store, PostgreSQL — Restoring a Backup for the single-instance store — against the database host and credentials listed under Connecting. Then restart Keycloak so every replica reloads from the restored database rather than from its in-memory caches:
This restore path has not been verified end to end against a live Keycloak install. The database steps are the backing template’s, verified there; the Keycloak-specific part is only the restart above. Test a restore on a non-production release before relying on it.

Rotating the Database Password

PostgreSQL keeps the password it was initialized with, and a running workload keeps the values it started with, so on the single-instance store a rotation takes three steps in order (on the HA store follow Rotating the Database Password in HA Mode instead):
  1. Change it in the database: ALTER ROLE DB_USERNAME PASSWORD 'NEW_PASSWORD'; (DB_USERNAME is the secret’s username; connect as that user from inside the GVC).
  2. Update the secret — export it, change only password, and apply it:
  3. Right away, redeploy Keycloak:
Do not redeploy the database: PostgreSQL reads the secret only when it is first initialized, so restarting it only adds downtime.

Rotating the Database Password in HA Mode

The HA store (postgresHA) needs its own order. The replication role repl shares the password, and a member that restarts with the old password can be promoted to leader, which splits the cluster. Move the leader to member -0 first, so it restarts last, after every other member already has the new password. pgx runs a command in one member (LOCATION is your GVC’s location, DB_USERNAME the secret’s username):
Then force-redeploy RELEASE_NAME-keycloak, which keeps the old password until it restarts. The database drops connections briefly at each switchover, and Keycloak cannot open new connections from the ALTER ROLE until its own redeploy finishes, so rotate in a quiet window. If a member stays starting, or running without streaming, re-copy it from the leader. This copies the whole database, so it costs network traffic in proportion to its size:

Upgrading From 1.0.x

Version 1.1.0 moved the bootstrap admin login out of Helm values and into the prerequisite dictionary secret described in Prerequisites. Earlier versions shipped a username and a working default password as values. An upgrade that still carries either removed key is refused at render, before anything is applied, with a message naming the replacement — for example keycloak: admin.password was REMOVED in 1.1.0. Put it in a dictionary secret (key: password) together with username, and set admin.secretName to that secret's name. Leaving admin.secretName empty is refused the same way. There is no compatibility fallback.
Your existing admin password does not change on upgrade. Keycloak reads the bootstrap credentials only when no admin account exists yet, so the account already in the database is untouched by whatever the secret holds. Create the secret so the release renders, then change the password in the admin console. If the install still uses the published 1.0.x default password (change-me-keycloak-admin), treat it as compromised and change it in the console now.
Installs created on 1.0.0 with the default HA store also predate the etcd history compaction that the bundled PostgreSQL Highly Available template now turns on. Upgrading stops further growth of the etcd backend but cannot shrink one that has already grown; see that template’s page for the symptoms and recovery.

Upgrading From 1.1.0

Version 1.2.0 adopted the postgres 3.4.1 template for the single-instance store, which no longer takes credentials as values: An upgrade carrying an old key is refused at render with the PostgreSQL template’s message. Put the values your database was initialized with into the database credentials secret under Prerequisites, set postgres.config.credentialsSecretName to its name, and remove the old keys. The HA path was unchanged in 1.2.0.

Upgrading From 1.2.x and 1.3.0

Version 1.3.0 adopted the postgres-highly-available 2.5.0 template for the default store, which likewise no longer takes credentials as values. The HA store reads the database credentials secret named by postgresHA.config.credentialsSecretName: An upgrade carrying a removed key is refused at render with the PostgreSQL Highly Available template’s message. Put the same values your install already uses under postgresHA.postgres.* into the database credentials secret under Prerequisites — the database volume was initialized with those credentials and keeps them — set postgresHA.config.credentialsSecretName to its name, and delete the postgresHA.postgres block. Do not change the database password as part of this upgrade.

Upgrading From 1.3.1

Version 1.4.0 renamed the Keycloak resource limits so it is unambiguous which number is the ceiling. Rename the two keys in your values; nothing else changed. An upgrade that still carries a bare resources.cpu or resources.memory is refused at render: keycloak: resources.cpu was RENAMED to resources.maxCpu. The reservation keys minCpu and minMemory are unchanged.

Upgrading From 1.4.0

Version 1.5.0 removed postgres.credentials: the database credentials are now a prerequisite secret, the same one the database templates require on their own. Carrying the old key forward fails the render. 1.4.0 created the database secret itself, so this upgrade deletes it. Before upgrading, create a secret under a new name holding the values your database was initialized with — not new ones, or Keycloak loses its database connection:
Your current values are in postgres.credentials of the 1.4.0 release, and also in the old secret (cpln secret reveal my-keycloak-db-credentials -o yaml, or the name your release set in credentialsSecretName) until the upgrade deletes it. Then upgrade with postgresHA.config.credentialsSecretName (HA store) or postgres.config.credentialsSecretName (single-instance store) set to the new name, and postgres.credentials removed. The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so Keycloak can briefly lose its database; it reconnects by itself. The new version of a workload can also wait several minutes with is not allowed to reveal the secret in get-deployments while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.

Upgrading From 1.5.0

Version 1.5.1 moves Keycloak from 26.6.3 to 26.8.1, which fixes an unauthenticated account-takeover vulnerability in the reset-credentials flow (CVE-2026-18963). No values change: upgrade with the values you already use. If your values set image (installs made through the Console wizard store it), change it to quay.io/keycloak/keycloak:26.8.1 or remove it; the chart refuses an official Keycloak image older than 26.7.2. The first 26.8.1 server to start migrates the database schema, and Keycloak cannot run an older version against a migrated database, so this upgrade cannot be rolled back to 1.5.0: a rolled-back release reports healthy, but every login page fails with HTTP 500. Take a database backup before upgrading (see Backing Up); to recover from a rollback, upgrade to 1.5.1 again or restore the backup. With 2 or more replicas Keycloak keeps serving throughout the upgrade, apart from the brief interruption of database writes in HA mode described below. With replicas: 1, Keycloak is unavailable while its single server restarts. In HA mode, 1.5.1 also moves the database to controlplanecorporation/patroni-postgres:0.8, which carries pgvector 0.8.7 (CVE-2026-103484). The database members restart, so expect a brief interruption of writes while the leader changes, even with 2 or more Keycloak replicas. Upgrade in a quiet window. Keycloak does not use the vector extension; if you created it yourself in a database, run ALTER EXTENSION vector UPDATE; there after the upgrade. If your values set postgresHA.image, set it to the 0.8 tag: tags 0.1 to 0.7 are refused at render.

Scaling and Availability

  • Keycloak replicas. Change replicas and upgrade the release; there is deliberately no autoscaling, so cluster membership only changes when you change it. Clustered replicas (2 or more) share realm, user and session state through the database, so one replica restarting does not interrupt sign-ins. With replicas: 1 a restart is a short outage.
  • Rolling upgrades are not guaranteed to restart one replica at a time. Realms, users and sessions live in PostgreSQL and survive regardless, but plan for a brief gap in availability during a template upgrade rather than assuming a strictly serialized rollout.
  • The first helm upgrade after an install can restart the bundled database even when none of its values changed; Keycloak waits for it and resumes.
  • Database tier. The default store runs three Patroni replicas with automatic leader failover; Keycloak always connects through the HAProxy leader endpoint, which is why postgresHA.proxy.enabled must stay true. Adjust postgresHA.replicas and postgresHA.volumeset.capacity for size; the single-instance store (postgres.enabled: true) trades failover for a smaller footprint.
  • Uninstalling deletes the database volume sets. The database volume set (and, in HA mode, etcd’s) is deleted with the release, so data does not survive a reinstall — enable backups.

Troubleshooting

Cause: the admin secret named by admin.secretName, the database credentials secret named by postgresHA.config.credentialsSecretName or postgres.config.credentialsSecretName (or, with MinIO backups, the secret named by backup.minio.credentialsSecretName) does not exist. The container never ran, so there is nothing to log.Fix: read the message that names the missing secret, create it, then force a redeployment to skip the wait:
Every refusal below happens before anything is applied, so a running release is untouched.
Cause: on a first install of the default HA store, the Keycloak container waits until the database actually answers as PostgreSQL — not merely accepts connections — before starting. That wait is normal while the Patroni and etcd replicas initialise, and the lines look like this:
Fix: none needed while the wait is progressing. If the container restarts repeatedly while waiting for the database, check the database tier itself:
Cause: the bootstrap credentials are read on first boot only, when no admin account exists yet. Afterwards the account lives in the database, and neither editing the secret nor redeploying the workload changes it.Fix: sign in with the current password and change it in the admin console. If you have lost it, use Keycloak’s own bootstrap admin recovery procedure.
Cause: with publicAccess.enabled: false there is no canonical endpoint to browse to; only workloads inside Control Plane can reach Keycloak.Fix: forward the HTTP port and open http://localhost:8080/admin:
Browser SSO for end users still needs a reachable login endpoint, so re-enable public access (or put Keycloak behind a domain you control) before pointing applications at it.

Important Notes

  • Create the admin and database credentials secrets before installing — a missing prerequisite secret wedges the deployment with zero log lines; status.versions[].message is the only diagnostic (see Prerequisites).
  • Rotating the database password needs ALTER ROLE plus a forced redeployment of Keycloak (and a leader switchover first on the HA store) — see Rotating the Database Password.
  • Give each release its own database credentials secret — secret names are organization-wide.
  • The bootstrap admin is temporary by design — sign in, create a permanent admin, then delete the bootstrap account. Its credentials are read on first boot only, so change the password in the console, not by editing the secret.
  • Keep publicAccess enabled for browser SSO — end-user browsers must reach Keycloak’s login endpoints; disable it only for pure service-to-service deployments.
  • Do not disable the HA proxy (postgresHA.proxy.enabled) — Keycloak writes through the HAProxy leader endpoint; the chart refuses the install otherwise.
  • replicas above 1 needs internal access — set internalAccess.type to anything but none, or the install is refused.
  • Scaling is operator-driven — change replicas via a template upgrade; there is deliberately no autoscaling.
  • Uninstalling deletes the database volume sets — the database (and, in HA mode, etcd) volume sets go with the release, so data does not survive a reinstall. Enable backups.

External References

Keycloak Documentation

Official Keycloak documentation

Server Configuration Reference

All Keycloak server configuration options

Caching and Clustering

How Keycloak’s Infinispan caching and clustering work

Bootstrap Admin Recovery

Recovering access when the admin password is lost

Keycloak Template

View the source files, default values, and chart definition