Keycloak Common — Shared Application Configuration
Keycloak_Common is the shared application layer for Keycloak. It is not deployed on its own; instead it supplies the Keycloak-specific configuration that both Keycloak_GKE and Keycloak_CloudRun build on, so the two platform variants behave identically where it matters. End users never configure this layer directly — it has no deployment UI inputs of its own — but understanding what it provides explains the defaults you see in the platform docs.
For the infrastructure that actually provisions and runs Keycloak, see the platform guides (Keycloak_GKE, Keycloak_CloudRun) and the foundation guides (App_GKE, App_CloudRun, App_Common).
1. What this layer provides
| Area | Provided by Keycloak_Common | Where it surfaces |
|---|---|---|
| Container image | Pins quay.io/keycloak/keycloak and builds a production-optimized custom variant (kc.sh build → start --optimized) via a multi-stage Dockerfile | container_image output of the platform deployment |
| Custom entrypoint | Maps the foundation-injected DB_* env vars onto Keycloak's KC_DB_* variables, assembles the JDBC KC_DB_URL, and auto-detects the public URL for KC_HOSTNAME | Application behaviour in the platform guides |
| Database engine | Fixes Cloud SQL for PostgreSQL 15 as the only supported engine | §Database in the platform guides |
| Database bootstrap | Defines the first-deploy db-init job that creates the role and database and grants schema privileges (idempotent, PostgreSQL 15+-aware) | initialization_jobs output |
| Secrets | Creates the bootstrap admin password secret in Secret Manager (KC_BOOTSTRAP_ADMIN_PASSWORD) | secret_ids output |
| Core environment | Injects KC_DB=postgres, KC_PROXY_HEADERS=xforwarded, KC_HTTP_ENABLED, KC_HEALTH_ENABLED, KC_METRICS_ENABLED, KC_BOOTSTRAP_ADMIN_USERNAME=admin | §Environment in the platform guides |
| Object storage | None — Keycloak stores all state in PostgreSQL (storage_buckets = []) | storage_buckets output |
| Health checks | Supplies TCP startup (30 s delay, 30 failures) and TCP readiness (60 s delay) probe defaults on port 8080 — Keycloak's /health lives on the unexposed management port 9000 | §Observability in the platform guides |
2. Secrets in Secret Manager
Keycloak's only app-level secret is the bootstrap admin password — a 20-character random password stored as secret-<prefix>-keycloak-admin-password and injected into the container as KC_BOOTSTRAP_ADMIN_PASSWORD (username admin via KC_BOOTSTRAP_ADMIN_USERNAME). The database password is generated by the foundation and injected as DB_PASSWORD; the entrypoint maps it to KC_DB_PASSWORD at runtime.
# List the Keycloak secrets
gcloud secrets list --project "$PROJECT" --filter="name~keycloak"
# Retrieve the bootstrap admin password for first login
gcloud secrets versions access latest \
--secret="$(gcloud secrets list --project "$PROJECT" \
--filter='name~keycloak-admin-password' --format='value(name)' --limit=1)" \
--project "$PROJECT"
The bootstrap admin is temporary by design — log in at <service-url>/admin, create a permanent administrator, then remove or rotate the bootstrap user.
3. Container image and custom entrypoint
Keycloak_Common builds a custom image with Cloud Build (a cloudbuild.yaml + Dockerfile in scripts/). The multi-stage Dockerfile:
- Builder stage — runs
/opt/keycloak/bin/kc.sh buildwithKC_DB=postgres,KC_HEALTH_ENABLED=true, andKC_METRICS_ENABLED=true, baking the DB vendor and features into a production-optimized build. - Runtime stage — copies the optimized build, overlays
/keycloak-entrypoint.sh(dropping back to the unprivileged UBI user1000), and starts withkc.sh start --optimizedso the slow build step never runs at boot.
The entrypoint performs these actions on every container start:
- JDBC URL assembly. Builds
KC_DB_URL = jdbc:postgresql://<host>:<port>/<db>from the platform-injectedDB_HOST/DB_PORT/DB_NAME. IfDB_HOSTis a Cloud SQL Unix-socket directory (starts with/), it falls back toDB_IP— the PostgreSQL JDBC driver cannot use Unix sockets, which is also why the Cloud Run variant defaultsenable_cloudsql_volume = false. - Credential mapping. Maps
DB_USER→KC_DB_USERNAMEandDB_PASSWORD→KC_DB_PASSWORD, only when not already set, so operators can override anyKC_DB_*variable viaenvironment_variables. - Hostname auto-detection. Queries the Cloud Run metadata/Admin API for the service URL and exports it as
KC_HOSTNAME(falling back toSERVICE_URLon GKE), withKC_HOSTNAME_STRICT=falsebehind the TLS-terminating front end. - Launch. Prints a configuration summary and
execskc.sh start --optimized.
# Check the entrypoint's configuration summary in the logs
gcloud run services logs read <service-name> --project "$PROJECT" --region "$REGION" \
--limit 50 | grep -E "KC_DB_URL|KC_HOSTNAME|Keycloak"
4. Database engine and bootstrap
Keycloak requires PostgreSQL; the engine is fixed at POSTGRES_15 inside Keycloak_Common. On every apply a one-shot db-init job (postgres:15-alpine, up to 3 retries) idempotently:
- Creates the application role (or updates its password if it exists) and grants it to
postgresso the superuser can manage its objects. - Creates the Keycloak database owned by that role (or fixes ownership if it exists).
- Grants all privileges on the database and on
SCHEMA public— required for PostgreSQL 15+, wherepublicis no longer world-writable. - Sends a
POST /quitquitquitshutdown signal to the Cloud SQL Proxy sidecar so the Job exits cleanly on GKE.
Keycloak itself creates and migrates the schema on first boot. Inspect the database directly:
gcloud sql connect <instance-name> --user=<db-user> --project "$PROJECT"
The instance, database, and user names are in the platform deployment outputs (tenant-prefixed at deploy time).
5. Core environment defaults
Keycloak_Common establishes the baseline environment so Keycloak comes up correctly in production mode behind a TLS-terminating front end:
KC_DB = postgres— the DB vendor, also baked into the optimized build.KC_PROXY_HEADERS = xforwardedandKC_HTTP_ENABLED = true— Cloud Run / the GKE load balancer terminate TLS and forwardX-Forwarded-*headers; Keycloak speaks plain HTTP on 8080 behind them.KC_HEALTH_ENABLED = trueandKC_METRICS_ENABLED = true— expose/healthand/metricson the management port 9000.KC_BOOTSTRAP_ADMIN_USERNAME = admin— paired with the Secret Manager password above.KC_DB_URL,KC_DB_USERNAME,KC_DB_PASSWORD, andKC_HOSTNAMEare resolved at runtime by the entrypoint, never hardcoded — the foundation tenant-prefixes the real role and database names, so hardcoding them would authenticate as a non-existent role.
6. Health probe behaviour
Keycloak 25+ serves /health, /health/ready, /health/live, and /metrics on the separate management port 9000 — not on the HTTP port 8080 the platforms probe. An HTTP probe against 8080/health would always 404, so the defaults are TCP checks that confirm the HTTP listener is accepting connections:
- Startup probe — TCP on 8080, initial delay 30 s, period 10 s, failure threshold 30 (up to ~330 s for JVM start + first-boot schema migrations).
- Readiness probe — TCP on 8080, initial delay 60 s, period 15 s, failure threshold 3.
- Liveness — the Cloud Run variant overrides to HTTP
/(60 s delay), which answers on 8080 once Keycloak is up.
Do not tighten the startup budget: on first boot Keycloak must connect to PostgreSQL and create its entire schema before the listener opens.
7. Object storage
None. Keycloak keeps realms, clients, users, and sessions entirely in PostgreSQL — Keycloak_Common declares storage_buckets = [] and no GCS volumes, so a database backup captures the full deployment state.
For the Keycloak-specific, user-facing configuration (variables by group, outputs, and how to explore each service from the Console and CLI), see the platform guides: Keycloak_GKE and Keycloak_CloudRun.