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: development mode
Section titled “start-dev: development mode”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.
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-devstart: production mode
Section titled “start: production mode”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 variable | Purpose | Example |
|---|---|---|
KC_DB | Database vendor | postgres |
KC_DB_URL | JDBC connection URL | jdbc:postgresql://db:5432/keycloak |
KC_DB_USERNAME | DB username | keycloak |
KC_DB_PASSWORD | DB password | changeme |
KC_HOSTNAME | Public hostname for Keycloak | auth.example.com |
KC_PROXY_HEADERS | Trust proxy headers (xforwarded or forwarded) | xforwarded |
KC_HTTP_ENABLED | Allow 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 startOptimized build
Section titled “Optimized build”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.
# Stage 1: buildFROM quay.io/keycloak/keycloak:latest AS builderRUN /opt/keycloak/bin/kc.sh build --db=postgres
# Stage 2: runFROM quay.io/keycloak/keycloak:latestCOPY --from=builder /opt/keycloak/ /opt/keycloak/ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"]Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
start-dev (development mode) | Fast setup — no TLS certificate, hostname, or external database needed. Good for learning and local development | No TLS, skips hostname checks, uses an in-memory H2 database that loses data on every restart — cannot be used in production |
start (production mode) | Forces TLS/hostname/database to be configured correctly before it will actually start, preventing an accidentally insecure deployment | Requires an external database, a certificate, and reverse proxy configuration up front — setup takes longer |
Common mistakes
Section titled “Common mistakes”- Running
start-devin production because setup is easier — teams in a hurry to deploy often forget to switch tostart, leaving the system without TLS and with data in an in-memory database that disappears on every container restart. - Forgetting to set
KC_HOSTNAMEto match the real domain — a mismatched hostname makes the issued tokens carry the wrong issuer URL, so any app that validates tokens strictly rejects all of them. - Enabling
KC_HTTP_ENABLED=truewith no real reverse proxy doing TLS termination — this setting should only be used when a proxy such as Nginx or a load balancer is already handling TLS, never left on for a bare public endpoint.
Real-world example
An organisation consolidating SSO — companies with dozens of internal apps commonly move all of them onto a single Keycloak instance in production mode, with external PostgreSQL and TLS through a load balancer, so employees log in once for every system.
Red Hat build of Keycloak (RH-SSO) — Red Hat’s supported build enforces the same principle: production-mode configuration (database, hostname, TLS) is required before a real deployment, to stop customers from accidentally running an insecure setup.