> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Keycloak

> Deploy Keycloak on Control Plane using the Template Catalog. Covers clustering, PostgreSQL backing stores, access configuration, and optional database backups.

## Overview

Keycloak is an open-source identity and access management platform that provides single sign-on, OIDC/SAML, user federation, and fine-grained authorization. This template runs clustered Keycloak 26 in production mode with a highly available PostgreSQL backing store by default, delivering zero-downtime restarts and upgrades.

### What Gets Created

* **Stateful Keycloak Workload** — 2 replicas by default, clustered via embedded Infinispan (JGroups JDBC\_PING through the shared database — no extra clustering infrastructure). Setting `replicas: 1` runs a dev mode with clustering fully disabled.
* **Highly Available PostgreSQL Stack** (default) — The [PostgreSQL Highly Available template](/template-catalog/templates/postgres-highly-available) as a subchart: 3 Patroni PostgreSQL replicas, 3 etcd replicas, and an HAProxy leader-routing workload that Keycloak connects through.
* **Single-Instance PostgreSQL Workload** (optional) — The [PostgreSQL template](/template-catalog/templates/postgres) instead, for lighter dev/test deployments.
* **Volume Sets** — Persistent storage for the PostgreSQL (and etcd) data.
* **Backup Cron Workload** (optional) — Created by the backing store subchart when its backup pass-through is enabled.
* **Secrets** — A dictionary secret with the bootstrap admin credentials, an opaque startup script secret, and the database credentials secret created by the PostgreSQL subchart.
* **Identity & Policy** — An identity bound to the Keycloak workload with a least-privilege policy granting `reveal` access to exactly the secrets it mounts.

All durable state — realms, users, and active sessions — lives in PostgreSQL; the Keycloak tier is stateless on disk.

<Note>
  This template does not create a GVC. You must deploy it into an existing GVC.
</Note>

## Prerequisites

None for a default install.

If you enable the optional database backups, you need a cloud account and a bucket for the backing store's backup job. The backup configuration is a pass-through to the backing PostgreSQL template — follow the [PostgreSQL Highly Available backup prerequisites](/template-catalog/templates/postgres-highly-available#backup-prerequisites) (default store) or the [PostgreSQL backup prerequisites](/template-catalog/templates/postgres#backup-prerequisites) (dev/test store).

## Installation

To install, follow the instructions for your preferred method:

<CardGroup cols={2}>
  <Card title="UI" href="/template-catalog/install-manage/ui" icon="laptop">
    Browse, install, and manage templates visually
  </Card>

  <Card title="CLI" href="/template-catalog/install-manage/cli" icon="terminal">
    Manage templates from your terminal
  </Card>

  <Card title="Terraform" href="/template-catalog/install-manage/terraform" icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M77.941 44.5v36.836L46.324 62.918V26.082zm0 0" fill="#5c4ee5"/><path d="M81.41 81.336l31.633-18.418V26.082L81.41 44.5zm0 0" fill="#4040b2"/><path d="M11.242 42.36L42.86 60.776V23.941L11.242 5.523zm0 0M77.941 85.375L46.324 66.957v36.82l31.617 18.418zm0 0" fill="#5c4ee5"/></g></svg>}>
    Declare templates in your Terraform configurations
  </Card>

  <Card
    title="Pulumi"
    href="/template-catalog/install-manage/pulumi"
    icon={<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" id="Pulumi-Icon--Streamline-Svg-Logos" height="24" width="24">
    <desc>
        Pulumi Icon Streamline Icon: https://streamlinehq.com
    </desc>
    <path fill="#f26e7e" d="M4.683025 13.3318c0.869125 -0.5018 0.870575 -2.1264 0.003225 -3.62865s-2.27504 -2.313275 -3.1441725 -1.811475C0.672945 8.3935 0.6715 10.0181 1.53885 11.52035c0.86735 1.502275 2.27505 2.313275 3.144175 1.81145Zm0.0052 3.2167c0.86735 1.502275 0.865925 3.126875 -0.003225 3.628675 -0.86915 0.5018 -2.2768275 -0.309225 -3.144175 -1.81145 -0.8673525 -1.50225 -0.8659075 -3.126875 0.003225 -3.628675 0.8691325 -0.5018 2.276825 0.309225 3.144175 1.81145Zm5.922875 3.4243c0.86735 1.50225 0.8659 3.126775 -0.003225 3.62875 -0.869125 0.501775 -2.27685 -0.309325 -3.1442 -1.81155 -0.867325 -1.50225 -0.865875 -3.12685 0.00325 -3.628675 0.869125 -0.5018 2.276825 0.309225 3.144175 1.811475Zm-0.001925 -6.845275c0.86735 1.50225 0.8659 3.12685 -0.003225 3.628675 -0.869125 0.5018 -2.276825 -0.309225 -3.144175 -1.811475 -0.86735 -1.50225 -0.8659 -3.12685 0.003225 -3.62865 0.869125 -0.501825 2.276825 0.3092 3.144175 1.81145Z" stroke-width="0.25"></path>
    <path fill="#8a3391" d="M22.45775 11.524125c0.86725 -1.502225 0.865925 -3.12685 -0.003225 -3.62865 -0.869125 -0.501825 -2.276825 0.3092 -3.144175 1.811475 -0.86735 1.50225 -0.8659 3.126825 0.003225 3.62865 0.869125 0.501825 2.276825 -0.3092 3.144175 -1.811475Zm0.000175 3.2151c0.869075 0.5018 0.870625 2.1264 0.003225 3.62865 -0.86735 1.50225 -2.27505 2.313275 -3.144175 1.81145 -0.869125 -0.5018 -0.870575 -2.126425 -0.003225 -3.62865 0.86735 -1.50225 2.27505 -2.313275 3.144175 -1.81145ZM16.536225 18.157875c0.86915 0.501825 0.8706 2.126425 0.00325 3.628675 -0.86735 1.502125 -2.275075 2.313225 -3.1442 1.81145 -0.869125 -0.50175 -0.870575 -2.126425 -0.003225 -3.62865 0.867375 -1.502275 2.27505 -2.3133 3.144175 -1.811475Zm-0.003325 -6.843775c0.869125 0.5018 0.870575 2.126425 0.003225 3.628675s-2.27505 2.313275 -3.1442 1.811475c-0.869125 -0.501825 -0.870575 -2.126425 -0.003225 -3.628675 0.86735 -1.502275 2.27505 -2.313275 3.1442 -1.811475Z" stroke-width="0.25"></path>
    <path fill="#f7bf2a" d="M15.138225 2.06721c0 1.003615 -1.40625 1.817215 -3.14095 1.817215 -1.7347 0 -3.14095 -0.8136 -3.14095 -1.817215C8.856325 1.06359 10.262575 0.25 11.997275 0.25c1.7347 0 3.14095 0.81359 3.14095 1.81721ZM9.2166 5.482375c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172s1.40625 -1.817225 3.14095 -1.817225c1.7347 0 3.14095 0.8136 3.14095 1.817225Zm8.71005 1.8172c1.7347 0 3.14095 -0.813575 3.14095 -1.8172s-1.40625 -1.817225 -3.14095 -1.817225c-1.7347 0 -3.14095 0.8136 -3.14095 1.817225s1.40625 1.8172 3.14095 1.8172Zm-2.788425 1.605625c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172 0 -1.0036 1.40625 -1.8172 3.14095 -1.8172 1.7347 0 3.14095 0.8136 3.14095 1.8172Z" stroke-width="0.25"></path>
    </svg>}
  >
    Declare templates in your Pulumi programs
  </Card>
</CardGroup>

## Configuration

The default `values.yaml` for this template:

```yaml theme={null}
image: quay.io/keycloak/keycloak:26.6.3

# Replica count. 2+ (default) forms an Infinispan cluster over JGroups JDBC_PING
# for zero-downtime restarts. 1 = dev mode: clustering fully disabled (local cache).
replicas: 2

resources: # per replica
  cpu: 1000m
  memory: 2Gi # JVM heap is sized to 70% of this limit; do not set below 1.5Gi
  minCpu: 500m
  minMemory: 1Gi

# ─── Admin Bootstrap ──────────────────────────────────────────────────────────
# Creates a temporary admin on first boot. Log in and create a permanent admin,
# then remove the temporary one (Keycloak warns until you do).
admin:
  username: admin
  password: change-me-keycloak-admin # change before installing

# ─── Backing Store: Highly Available PostgreSQL (default) ────────────────────
# Deploys the postgres-highly-available template: 3 Patroni replicas, 3 etcd
# replicas, and an HAProxy leader-routing endpoint Keycloak connects to.
postgresHA:
  enabled: true
  postgres:
    username: keycloak
    password: change-me-keycloak-db # change before installing
    database: keycloak
  replicas: 3
  volumeset:
    capacity: 10 # initial capacity in GiB per replica (minimum is 10)

  backup: # pass-through to the postgres-highly-available template's backup feature
    enabled: false
    mode: logical # logical or wal-g
    resources:
      cpu: 100m
      memory: 128Mi
    logical:
      image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0
      schedule: "0 2 * * *" # daily at 2am UTC
    walg:
      intervalSeconds: 21600 # every 6 hours
    provider: aws # Options: aws, gcp, or minio
    aws:
      bucket: my-backup-bucket
      region: us-east-1
      cloudAccountName: my-backup-cloudaccount
      policyName: my-backup-policy
      prefix: postgres/backups
    gcp:
      bucket: my-backup-bucket
      cloudAccountName: my-backup-cloudaccount
      prefix: postgres/backups
    minio: # any S3-compatible endpoint, e.g. an internal MinIO template deployment
      endpoint: http://my-minio-workload:9000
      bucket: my-backup-bucket
      accessKey: my-minio-username
      secretKey: my-minio-password
      prefix: postgres/backups

# ─── Backing Store: Single-instance PostgreSQL (dev/test) ────────────────────
# Enable this and disable postgresHA for a lighter non-HA deployment.
postgres:
  enabled: false
  config:
    username: keycloak
    password: change-me-keycloak-db # change before installing
    database: keycloak
  volumeset:
    capacity: 10 # initial capacity in GiB (minimum is 10)

  backup: # pass-through to the postgres template's backup feature (Postgres 17+)
    enabled: false
    image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0
    schedule: "0 2 * * *" # daily at 2am UTC
    resources:
      cpu: 100m
      memory: 128Mi
    provider: aws # Options: aws, gcp, or minio
    aws:
      bucket: my-backup-bucket
      region: us-east-1
      cloudAccountName: my-backup-cloudaccount
      policyName: my-backup-policy
      prefix: postgres/backups
    gcp:
      bucket: my-backup-bucket
      cloudAccountName: my-backup-cloudaccount
      prefix: postgres/backups
    minio:
      endpoint: http://my-minio-workload:9000
      bucket: my-backup-bucket
      accessKey: my-minio-username
      secretKey: my-minio-password
      prefix: postgres/backups

# ─── Access ───────────────────────────────────────────────────────────────────
publicAccess:
  enabled: true # HTTPS via the canonical *.cpln.app endpoint

internalAccess:
  type: same-gvc # options: none, same-gvc, same-org, workload-list
  workloads: [] # note: replicas > 1 requires type != none
  # workloads:
  #   - //gvc/GVC_NAME/workload/WORKLOAD_NAME
```

### Replicas and Clustering

* `replicas` — `2` or more (default) runs Keycloak in a cluster: the replicas form an Infinispan cluster over JGroups JDBC\_PING through the shared database, so restarts and upgrades are zero-downtime and in-flight logins and sessions survive a replica restart. `1` runs a dev mode with clustering fully disabled (local cache only).
* Clustered replicas must be able to reach each other on ports 7800/57800, so `replicas > 1` requires `internalAccess.type` other than `none` — the chart enforces this at render.
* Scaling is operator-driven: change `replicas` via a template upgrade. There is deliberately no autoscaling, so cluster membership only changes intentionally.

### Keycloak Resources

* `resources` — CPU and memory per replica. The JVM heap is sized to 70% of the memory limit; do not set `memory` below `1.5Gi`.

### Admin Bootstrap

* `admin.username` / `admin.password` — Credentials for a temporary bootstrap admin created on first boot. **Change the password before installing.** After the first login, create a permanent admin account and remove the temporary one — Keycloak warns until you do.

### Backing Store

Exactly one of the two stores must be enabled — the chart enforces this at render.

* `postgresHA` (default) — A highly available PostgreSQL cluster from the [PostgreSQL Highly Available template](/template-catalog/templates/postgres-highly-available): 3 Patroni replicas, 3 etcd replicas, and an HAProxy endpoint that routes Keycloak's connections to the current leader. Do not disable the HA proxy (`postgresHA.proxy.enabled`) — Keycloak writes through the HAProxy leader endpoint, and the chart enforces this at render.
* `postgres` — A single-instance PostgreSQL from the [PostgreSQL template](/template-catalog/templates/postgres), for lighter dev/test deployments. Set `postgresHA.enabled: false` and `postgres.enabled: true`.
* `postgresHA.postgres.*` / `postgres.config.*` — Database credentials and database name. **Change the password before installing.**
* `postgresHA.volumeset.capacity` / `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10, per replica for the HA store).

### Access

* `publicAccess.enabled` — Exposes Keycloak over HTTPS at the automatically assigned canonical `*.cpln.app` endpoint. Keep it enabled for browser-based SSO — end-user browsers must reach Keycloak's login endpoints. Disable it only for pure service-to-service deployments; internal access keeps working.
* `internalAccess.type` — Controls 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`.

### Backup

The `postgresHA.backup` and `postgres.backup` sections are pass-throughs to the backup feature of the corresponding backing store template. Set `backup.enabled: true` and fill in the provider section (`aws`, `gcp`, or `minio`) to schedule database backups to object storage.

For provider setup, backup modes, and restore procedures, see the backing store's documentation:

* Default HA store: [PostgreSQL Highly Available — Backup](/template-catalog/templates/postgres-highly-available#backup)
* Dev/test store: [PostgreSQL — Backup](/template-catalog/templates/postgres#backup)

## Connecting

| What              | Value                                                                          |
| ----------------- | ------------------------------------------------------------------------------ |
| Public URL        | `status.canonicalEndpoint` from `cpln workload get {release}-keycloak -o yaml` |
| Admin console     | `https://{canonical-endpoint}/admin`                                           |
| OIDC discovery    | `https://{canonical-endpoint}/realms/{realm}/.well-known/openid-configuration` |
| In-GVC (internal) | `http://{release}-keycloak.{gvc}.cpln.local:8080`                              |
| Admin credentials | `admin.username` / `admin.password` values                                     |

<Note>
  On a first install of the default HA stack, the Keycloak container waits for the PostgreSQL cluster to come up (logging `Waiting for PostgreSQL...`) before starting. Expect the Keycloak workload to become ready roughly 4–5 minutes after install; the dev-mode store (`postgres` with `replicas: 1`) is ready in about 2 minutes.
</Note>

## Important Notes

* **Change both default passwords before installing** — the admin bootstrap password and the database password. The bootstrap admin is temporary by design: log in, create a permanent admin, then remove it.
* **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 connects through the HAProxy leader endpoint for writes; the chart enforces this at render.
* **Scaling is operator-driven** — change `replicas` via a template upgrade; there is deliberately no autoscaling, so cluster membership only changes intentionally.
* **Database volumes survive reinstalls** — uninstalling and reinstalling under the same release name reuses the persisted data unless the volume sets are deleted.

## External References

<CardGroup cols={2}>
  <Card title="Keycloak Documentation" icon="book" href="https://www.keycloak.org/documentation">
    Official Keycloak documentation
  </Card>

  <Card title="Server Configuration Reference" icon="gear" href="https://www.keycloak.org/server/all-config">
    All Keycloak server configuration options
  </Card>

  <Card title="Caching and Clustering" icon="server" href="https://www.keycloak.org/server/caching">
    How Keycloak's Infinispan caching and clustering work
  </Card>

  <Card title="Cloud Accounts" icon="cloud" href="https://docs.controlplane.com/guides/create-cloud-account">
    Create a Control Plane Cloud Account for backup storage access
  </Card>

  <Card title="Keycloak Template" icon="github" href="https://github.com/controlplane-com/templates/tree/main/keycloak">
    View the source files, default values, and chart definition
  </Card>
</CardGroup>
