Common failures, what causes them, and how to fix them.
The schuly theme jar isn't loaded, or the realm isn't using it.
- Confirm the realm's
loginThemeisschuly(realms/schuly-realm.json). - If you changed theme code, rebuild the image - the theme is baked in at build time,
not loaded at runtime:
docker compose -f compose.dev.yml up --build. See Theme development.
Keycloak doesn't know its public URL or isn't trusting the proxy headers.
- Set
KC_HOSTNAMEto the full public URL (e.g.https://auth.schuly.dev). - Behind a TLS-terminating proxy, set
KC_PROXY_HEADERS=xforwardedandKC_HTTP_ENABLED=true, and make sure the proxy forwardsX-Forwarded-*headers. - See Self-hosting the full stack.
The realm is imported only on the first start; afterwards an existing realm is left as-is.
- Local dev: reset the data volume to re-import -
docker compose -f compose.dev.yml down -v && docker compose -f compose.dev.yml up --build. - Production: the realm already exists in Postgres; apply changes in the admin console and snapshot them back with the export script (see Realm management). Don't expect the bundled JSON to overwrite a live realm.
Health and metrics are on the management port 9000, not 8080.
- Hit
http://<host>:9000/health/readyfrom inside the network (it's intentionally not proxied to the internet).
KC_BOOTSTRAP_ADMIN_USERNAME / KC_BOOTSTRAP_ADMIN_PASSWORD only create an account
on the first start of a fresh database. If the database already had an admin,
those variables do nothing - use the existing admin, or reset via the admin REST API.
- Verify
KC_DB_URL,KC_DB_USERNAME,KC_DB_PASSWORDand that Postgres is reachable and accepting connections (wait for its healthcheck before Keycloak starts). - The image is built for Postgres only - don't override
KC_DB.
2FA enrollment behavior is defined by the browser-2fa flow and required actions -
see the 2FA section in Realm management, including the
migration note for pre-existing users.