Skip to content

Dev vs Production Mode

Keycloak has two startup modes: start-dev for development and start for production. They are not interchangeable — start-dev makes several convenience compromises that are unsafe in a real environment. Understanding the difference is important before you move beyond local learning.

start-dev is optimised for getting started quickly. It uses an embedded H2 in-memory database (data is lost on restart), enables HTTP (no TLS), disables hostname verification, and relaxes several security checks. Use it only on localhost when learning Keycloak.

Terminal window
docker run --name keycloak -p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest start-dev

start is the production command. It requires an external database, a fixed hostname, and HTTPS (or a configured reverse proxy). Keycloak will refuse to start in production mode without a valid hostname and TLS configuration. Key environment variables:

Environment variablePurposeExample
KC_DBDatabase vendorpostgres
KC_DB_URLJDBC connection URLjdbc:postgresql://db:5432/keycloak
KC_DB_USERNAMEDB usernamekeycloak
KC_DB_PASSWORDDB passwordchangeme
KC_HOSTNAMEPublic hostname for Keycloakauth.example.com
KC_PROXY_HEADERSTrust proxy headers (xforwarded or forwarded)xforwarded
KC_HTTP_ENABLEDAllow HTTP (needed behind a TLS-terminating proxy)true
docker run --name keycloak -p 8080:8080 \
  -e KC_DB=postgres \
  -e KC_DB_URL=jdbc:postgresql://db:5432/keycloak \
  -e KC_DB_USERNAME=keycloak \
  -e KC_DB_PASSWORD=changeme \
  -e KC_HOSTNAME=auth.example.com \
  -e KC_PROXY_HEADERS=xforwarded \
  -e KC_HTTP_ENABLED=true \
  quay.io/keycloak/keycloak:latest start

For production, Keycloak recommends running kc.sh build (or kc.bat build) inside the image before starting. This pre-processes configuration and significantly reduces startup time. A typical production Dockerfile uses a two-stage build: one stage to run kc.sh build, another to copy the built distribution and run kc.sh start.

Terminal window
# Stage 1: build
FROM quay.io/keycloak/keycloak:latest AS builder
RUN /opt/keycloak/bin/kc.sh build --db=postgres
# Stage 2: run
FROM quay.io/keycloak/keycloak:latest
COPY --from=builder /opt/keycloak/ /opt/keycloak/
ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"]
What database does start-dev use?
Which environment variable sets the public hostname in production mode?
What is the purpose of running kc.sh build before kc.sh start in production?
Which KC_PROXY_HEADERS value should you use when Keycloak is behind an Nginx or load balancer that sets X-Forwarded-* headers?