Production setup
In production, run the published image
ghcr.io/schulydev/schulykeycloak:latest (or a pinned :<semver> tag — see
Release). The image is an optimized Keycloak build (kc.sh build
runs at image-build time), so the runtime entrypoint starts with
start --optimized --import-realm for fast startup. It is pre-built for Postgres
(KC_DB=postgres), with health and metrics enabled.
Deploying the whole stack (Postgres + reverse proxy + TLS) from scratch? Follow Self-hosting the full stack instead — it has a complete docker-compose and first-admin walkthrough.
Run
docker run -p 8080:8080 \
-e KC_DB_URL=jdbc:postgresql://db:5432/keycloak \
-e KC_DB_USERNAME=keycloak \
-e KC_DB_PASSWORD=... \
-e KC_HOSTNAME=https://auth.schuly.dev \
-e KC_PROXY_HEADERS=xforwarded \
-e KC_HTTP_ENABLED=true \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=... \
ghcr.io/schulydev/schulykeycloak:latest
The essential variables are the database connection (KC_DB_*), the public hostname
(KC_HOSTNAME), the proxy settings when behind a TLS-terminating proxy
(KC_PROXY_HEADERS, KC_HTTP_ENABLED), and a first-start bootstrap admin
(KC_BOOTSTRAP_ADMIN_*). The full list — every variable, port, and baked-in default
— is in the Configuration reference.
Security: the bootstrap admin is temporary — create a real admin and remove the
KC_BOOTSTRAP_ADMIN_*variables after first start. Never commit secrets or place them inrealms/schuly-realm.json, keep TLS terminated at the proxy, and don't expose the management port9000publicly.