Skip to content

ADR-009: Use Keycloak for Identity Management and SSO

Status

Accepted — partially superseded by ADR-011

Date

2026-03-07

Context

The HomeKube sandbox needs a centralised identity provider (IdP) to: - Provide single sign-on across all cluster services (Grafana, future apps) - Teach OIDC/OAuth2 concepts used in production environments - Enable Kubernetes API authentication via OIDC (Phase 4) - Serve as the foundation for oauth2-proxy (protecting apps without native SSO)

The IdP must be CNCF-aligned, open source, and runnable on a MacBook with reasonable resource usage.

Decision

Use Keycloak (CNCF Foundation project) deployed via the Bitnami Helm chart with a bundled PostgreSQL database.

OIDC flow:

Browser → Grafana → redirects to Keycloak → login → token issued → Grafana grants access

Alternatives Considered

Tool Reason Not Chosen
Dex (CNCF Incubating) Lightweight federation layer, not a full IdP — no admin UI, no user management, designed to delegate to upstream providers (GitHub, Google). Better as a proxy than a standalone IdP.
Authentik Modern UI, excellent DX; not a CNCF project; smaller production footprint in enterprise
Zitadel (CNCF Sandbox) Cloud-native Go binary (lighter than Keycloak); immature compared to Keycloak; less enterprise adoption
LDAP + Dex Adds LDAP operational complexity without benefit in a single-user sandbox
No SSO Each service manages its own users — doesn't teach real-world IAM patterns

Consequences

Positive

  • CNCF Foundation project — industry standard in enterprise Kubernetes deployments
  • Full OIDC/OAuth2/SAML provider — covers all production authentication patterns
  • Admin UI for user, realm, and client management
  • Role mapping allows K8s RBAC integration (Phase 4)
  • Realm export to JSON enables GitOps-managed realm configuration
  • Grafana, ArgoCD, Weave GitOps all have native Keycloak OIDC support

Negative

  • Java-based — ~512 MB RAM minimum (heaviest component in the stack)
  • Slow startup (~60s) — makes cluster restart feel slow
  • Realm configuration done via UI on first run (bootstrapping exception — see below)
  • Admin password stored as plaintext in dev (must be replaced with SOPS in Phase 4)

Bootstrapping Exception

Keycloak realm and client configuration must be done manually via the admin UI on first deploy. Once configured, the realm should be exported to JSON and committed to the repo for reproducibility:

# Export realm (inside Keycloak container)
kubectl exec -n identity deployment/keycloak -- \
  /opt/bitnami/keycloak/bin/kc.sh export \
  --realm homekube --file /tmp/homekube-realm.json

# Copy out and commit
kubectl cp identity/$(kubectl get pod -n identity -l app.kubernetes.io/name=keycloak -o name | head -1 | cut -d/ -f2):/tmp/homekube-realm.json \
  identity/homekube-realm.json

Trade-offs

Learning completeness and production transferability are prioritised over minimal resource usage. The ~512 MB RAM cost of Keycloak is accepted for the value it provides.


Amendment — 2026-03-21: Superseded Deployment Model

The Bitnami Helm chart chosen in this ADR is no longer in use. Bitnami removed all container images from Docker Hub (now paywalled), causing ImagePullBackOff for bitnami/keycloak and bitnami/postgresql.

Current implementation (see ADR-011): - Plain Kubernetes Deployment using quay.io/keycloak/keycloak:26.3 - start-dev mode with embedded H2 database (no PostgreSQL) - Realm bootstrapped via --import-realm from identity/realm-config.sops.yaml - Admin password from SOPS-encrypted identity/keycloak-secret.sops.yaml - All manual first-run UI steps replaced by GitOps realm config

The Bootstrapping Exception section is no longer applicable — realm configuration is fully GitOps-managed.

The Negative consequence "Admin password stored as plaintext" is resolved by ADR-010.