Skip to main content

Keycloak Docker Compose Generator

Pick a database, a mode, and how users reach the server, and get a working docker-compose.yml and .env for Keycloak or the Phase Two image — with healthchecks, a TLS reverse proxy, and generated passwords. Everything runs in your browser.

Generated entirely in your browser. Passwords are created locally with the Web Crypto API and never leave this page.
Image
Keycloak image
Bundled extensions

Organizations, Events & webhooks, Magic Link, Themes, Admin Portal, IdP Wizards, User Migration, SCIM Server, Atomic Auth Flows, Sign in with Apple. Same Keycloak, same KC_* options, plus CockroachDB support. Image docs.

Version
Pin a full 26.8.1.<build-timestamp> tag in production. See the tag list.
Runtime
Mode
Database
Network
How users reach Keycloak
Host port
Path prefix
Phase Two clusters and docs use /auth. Upstream Keycloak defaults to the root.
Extras
JVM memory
# Keycloak development stack.
# Generated with phasetwo.io/tools/keycloak-docker-compose-generator
# Secrets are read from the .env file next to this one.
name: keycloak

services:
  db:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test:
        - CMD-SHELL
        - "pg_isready -U keycloak -d keycloak"
      interval: 5s
      timeout: 5s
      retries: 20

  keycloak:
    image: quay.io/phasetwo/phasetwo-keycloak:26.8.1
    restart: unless-stopped
    command:
      - start-dev
      - --spi-email-template--provider=freemarker-plus-mustache
      - --spi-email-template--freemarker-plus-mustache--enabled=true
      - --spi-theme--cache-themes=false
    environment:
      KC_BOOTSTRAP_ADMIN_USERNAME: "${KC_BOOTSTRAP_ADMIN_USERNAME}"
      KC_BOOTSTRAP_ADMIN_PASSWORD: "${KC_BOOTSTRAP_ADMIN_PASSWORD}"
      KC_DB: postgres
      KC_DB_URL_HOST: db
      KC_DB_URL_PORT: 5432
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: "${DB_PASSWORD}"
      KC_HTTP_RELATIVE_PATH: /auth
      KC_HTTP_ENABLED: "true"
      KC_HEALTH_ENABLED: "true"
      KC_METRICS_ENABLED: "true"
    ports:
      - "8080:8080"
      - "127.0.0.1:9000:9000"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test:
        - CMD
        - bash
        - -c
        - "exec 3<>/dev/tcp/127.0.0.1/9000 && printf 'GET /auth/health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && grep -q 'HTTP/1.1 200' <&3"
      interval: 10s
      timeout: 5s
      retries: 30
      start_period: 30s

volumes:
  postgres_data: {}
Run it
  1. Save both files in an empty directory. The downloaded keycloak.env must be renamed to .env. Keep it out of version control.
  2. Start the stack and follow the logs:
    docker compose up -d
    docker compose logs -f keycloak
  3. Open http://localhost:8080/auth/ and sign in as admin with the password from .env.
What this does
  • Runs the Phase Two Keycloak image (quay.io/phasetwo/phasetwo-keycloak:26.8.1): upstream Keycloak plus the Phase Two extensions.
  • Starts Keycloak with start-dev (development mode): relaxed hostname checks and no theme caching. Not for production.
  • Stores data in PostgreSQL on the named volume postgres_data. Keycloak waits for the database healthcheck to pass before starting.
  • Publishes Keycloak on http://localhost:8080.
  • Serves Keycloak under /auth, as Phase Two clusters do, for example http://localhost:8080/auth/realms/master/.
  • Enables health checks and Prometheus metrics on the management port 9000, bound to 127.0.0.1 only (/auth/health/ready, /auth/metrics). Docker uses the readiness check to mark the container healthy.
  • Reads the admin and database passwords from .env, so the compose file is safe to commit and the .env file is not.

Running Keycloak with Docker Compose

Keycloak needs a database and, in production, a hostname and TLS. A single docker run gets you a throwaway server; Docker Compose lets you describe the whole stack — Keycloak, its database, a reverse proxy, and helpers like a mail catcher — in one file and bring it up with one command. This generator writes that file for the current Keycloak 26 configuration options (KC_DB, KC_HOSTNAME, KC_PROXY_HEADERS, KC_HTTP_ENABLED, KC_BOOTSTRAP_ADMIN_USERNAME, and friends), so you do not have to piece it together from old blog posts that still use KEYCLOAK_ADMIN and PROXY_ADDRESS_FORWARDING.

Development vs. production

In development mode (start-dev) Keycloak serves plain HTTP on localhost, relaxes hostname checks, and reloads theme changes immediately. It is the fastest way to try a realm or build a login theme. In production mode (start) Keycloak requires KC_HOSTNAME, expects HTTPS — terminated either by Keycloak or by a proxy in front of it — and enables caching. The generator warns you when a combination is unsafe, such as production over plain HTTP or with the admin/admin bootstrap password.

Before going live, work through the Keycloak production readiness checklist.

Keycloak behind a reverse proxy

The most common production layout puts a proxy in front of Keycloak to terminate TLS. With Caddy or Traefik, the generated stack gets and renews a Let's Encrypt certificate on its own, or uses a self-signed certificate for local testing. Traefik reads its routes from an inline file, so the Docker socket is never mounted. With nginx, you mount your own certificate. In every case Keycloak listens on HTTP only on the internal Docker network, trusts the proxy's X-Forwarded-* headers (KC_PROXY_HEADERS=xforwarded), and is not published on the host, so the proxy is the only way in. If you would rather terminate TLS in Keycloak itself, choose Direct TLS and mount a certificate and key.

The Phase Two Keycloak image

The default image is quay.io/phasetwo/phasetwo-keycloak, the same image that runs Phase Two's managed clusters. It is a drop-in replacement for the official image with the Organizations, Events, Magic Link, Themes, Admin Portal, and IdP Wizard extensions built in, plus CockroachDB support. Phase Two serves Keycloak under /auth, so the generator does too when you pick this image. Tags follow <keycloak-version>.<build-timestamp>; pin the full tag in production. See Database for PostgreSQL and CockroachDB settings, and Kubernetes when you outgrow a single host.

Frequently asked questions

Are the generated passwords safe to use?

Yes. The admin and database passwords are created in your browser with the Web Crypto API and are never sent to Phase Two or anywhere else. They go into the .env file, which Docker Compose reads automatically, so the docker-compose.yml itself contains no secrets and is safe to commit.

What is the difference between start-dev and start?

start-dev runs Keycloak in development mode: HTTP is allowed, the hostname is not enforced, and themes are not cached. start runs production mode, which requires a hostname, expects TLS (directly or at a proxy), and turns on caching. Use start-dev on your laptop and start for anything other people will use.

Which database should I use with Keycloak in Docker?

PostgreSQL is the most common choice and the one we recommend for self-hosting. MySQL and MariaDB are fully supported. The Phase Two image also supports CockroachDB, which is what Phase Two's managed clusters run on. The embedded H2 database is only for development and loses data when the container is removed.

How do I run Keycloak behind a reverse proxy in Docker Compose?

Choose Reverse proxy + TLS. The generator adds a Caddy, Traefik, or nginx service that terminates TLS and forwards to Keycloak over the internal Docker network, enables HTTP on Keycloak only inside that network, sets KC_PROXY_HEADERS=xforwarded so Keycloak builds correct https URLs, and sets KC_HOSTNAME to your public hostname. Keycloak itself is not published on the host.

Why does the Keycloak healthcheck use bash instead of curl?

Neither the official Keycloak image nor the Phase Two image ships curl. The healthcheck opens a TCP connection to the management port (9000) with bash's /dev/tcp and checks that /health/ready returns HTTP 200. That lets other services wait for Keycloak with depends_on: condition: service_healthy.

How do I import a realm on startup?

Turn on realm import, put one or more realm export JSON files in a ./realms directory next to docker-compose.yml, and start the stack. Keycloak runs with --import-realm and imports each file. Realms that already exist are skipped, so the import is safe to leave on.

What is the Phase Two Keycloak image?

quay.io/phasetwo/phasetwo-keycloak is a drop-in replacement for the official image: the same kc.sh entrypoint and KC_* options, plus the Phase Two extensions for organizations, webhooks, magic links, themes, SCIM, and more, on a hardened non-root runtime. It is the same image that runs Phase Two's managed clusters.

Why does the first production start take longer?

The images are not pre-built for a specific database, so the first start in production mode runs Keycloak's build step for the database you chose before starting. To skip it, build your own image from a pinned tag with kc.sh build and run start --optimized.

Prefer not to run it yourself?

Phase Two runs this same Keycloak image for you, with the database, backups, upgrades, TLS, and monitoring handled. Start a cluster in minutes.