ADR-011: Switch Keycloak to Official quay.io Image¶
Status¶
Accepted
Date¶
2026-03-21
Supersedes¶
Parts of ADR-009 — the deployment mechanism and database choice. The core decision to use Keycloak as the identity provider remains unchanged.
Context¶
ADR-009 chose the Bitnami Helm chart for Keycloak with a bundled PostgreSQL database. Thirteen days after deployment, both pods entered ImagePullBackOff with the error:
manifest for bitnami/keycloak:26.3.2-debian-12-r0 not found: manifest unknown
manifest for bitnami/postgresql:17.4.0-debian-12-r17 not found: manifest unknown
Root cause: Bitnami removed all container images from Docker Hub. Images are now only available via a paid Bitnami subscription. The chart (bitnami/keycloak:24.9.0) still specifies docker.io/bitnami/keycloak as the image repository, but those tags no longer exist publicly.
A replacement deployment model is needed that uses publicly available images without changing the identity provider choice.
Decision¶
Replace the Bitnami HelmRelease with a plain Kubernetes Deployment using the official Keycloak image from quay.io/keycloak/keycloak:26.3 (Red Hat / Keycloak project's own registry).
Run mode: start-dev — Keycloak's development mode uses an embedded H2 database, eliminating the PostgreSQL dependency entirely.
Realm bootstrapping: The --import-realm flag reads all *.json files from /opt/keycloak/data/import/ on startup. The realm-config.sops.yaml ConfigMap (SOPS-encrypted) is mounted there, so the homekube realm, clients, roles, and users are automatically imported every time Keycloak starts.
containers:
- image: quay.io/keycloak/keycloak:26.3
args:
- start-dev
- --import-realm
volumeMounts:
- name: realm-config
mountPath: /opt/keycloak/data/import
Files:
| Old | New |
|---|---|
identity/sources.yaml (Bitnami HelmRepository) |
Removed |
identity/keycloak.yaml (HelmRelease) |
Removed |
| — | identity/keycloak-deployment.yaml (Deployment + Service + Ingress) |
identity/realm-config.yaml (plaintext ConfigMap) |
identity/realm-config.sops.yaml (SOPS-encrypted ConfigMap) |
Alternatives Considered¶
| Option | Reason Not Chosen |
|---|---|
| Pay for Bitnami subscription | Adds cost and vendor dependency to a free learning environment |
| Codecentric Keycloak Helm chart | Uses the official Keycloak image internally; adds Helm chart indirection for no benefit in a simple deployment |
| Switch to Authentik or Dex | Different products — would negate the Keycloak learning investment already made |
| Pin to an older Bitnami tag | The tags are gone — Docker Hub returns manifest unknown for all Bitnami tags |
Consequences¶
Positive¶
- Uses the official Keycloak project image — no third-party packaging layer
- Eliminates PostgreSQL dependency — fewer pods, less RAM (~256 MB saved)
- Simpler manifest — plain Deployment instead of HelmRelease with subchart
- Realm config is fully GitOps via
--import-realm— no manual UI setup required quay.iois a stable, publicly available registry with no rate limits
Negative¶
start-devmode: realm data is not persisted — Keycloak rebuilds its database from the GitOps realm JSON on every pod restart. Any changes made via the admin UI are lost unless committed torealm-config.sops.yamlstart-devmode is not suitable for production — this is an explicit local-sandbox trade-off- H2 embedded database has no external access (acceptable since realm config is in Git)
Updating the Realm Config¶
Because realm data is ephemeral, all realm changes must go through Git:
# Edit the encrypted realm JSON
sops identity/realm-config.sops.yaml
# Commit and push
git add identity/realm-config.sops.yaml
git commit -m "feat: add new OIDC client"
git push origin main
# Restart Keycloak to reimport
kubectl rollout restart deployment/keycloak -n identity
Trade-offs¶
Simplicity and zero cost are prioritised over data persistence. For a local sandbox where minikube delete already resets all state, an ephemeral H2 database is no worse than a persistent PostgreSQL that would also be deleted.