Keycloak Docker Compose: A Complete Setup and Configuration Guide
Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.
Running Keycloak in a container is the fastest way to get a production-grade identity and access management server working on a VPS or local machine. This guide walks through a complete keycloak docker compose setup, from the minimal file to a hardened, database-backed configuration you can actually run in production.
Keycloak is an open-source identity provider that handles authentication, authorization, single sign-on, and user federation for your applications. Instead of installing Java, downloading a distribution archive, and wiring up a database by hand, a keycloak docker compose file lets you define the entire stack — Keycloak plus its database — in one declarative document and bring it up with a single command. This article covers the base configuration, database persistence, environment variables, reverse proxy setup, networking, and common troubleshooting steps.
Why Use Docker Compose for Keycloak
Keycloak ships an official container image, and running it directly with docker run works fine for a five-minute test. But real deployments need a database, persistent volumes, environment configuration, and usually a reverse proxy in front for TLS termination. A keycloak docker compose file captures all of that in one place, checked into version control, reproducible on any host.
Compared to a Kubernetes-based deployment, Docker Compose is simpler to reason about for a single-node setup. You don’t need a control plane, ingress controller, or Helm chart — just a docker-compose.yml and a .env file. If you eventually outgrow a single node, migrating from Compose to Kubernetes is a well-understood path; see Kubernetes vs Docker Compose: Which Should You Use? for a breakdown of when that migration is worth it.
Who Should Use This Approach
This setup is a good fit for:
If you’re already running other services with Compose — a database, an automation tool like n8n, or a monitoring stack — adding Keycloak to the same host follows the same pattern you’re used to.
Building the Base Keycloak Docker Compose File
The minimal keycloak docker compose configuration needs two services: Keycloak itself and a Postgres database. Keycloak ships with an embedded H2 database, but that’s explicitly documented as unsuitable for production, so a real database is not optional for anything beyond a quick local test.
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- keycloak_pg_data:/var/lib/postgresql/data
networks:
- keycloak_net
keycloak:
image: quay.io/keycloak/keycloak:25.0
restart: unless-stopped
command: start
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_HOSTNAME: ${KEYCLOAK_HOSTNAME}
KC_HOSTNAME_STRICT: "false"
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USER}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_PROXY: edge
ports:
- "127.0.0.1:8080:8080"
depends_on:
- postgres
networks:
- keycloak_net
volumes:
keycloak_pg_data:
networks:
keycloak_net:
This is the shape almost every keycloak docker compose setup converges on: one database service, one Keycloak service, a named volume for durability, and a private network so the two containers can talk to each other without exposing Postgres to the host.
Choosing the Right Keycloak Image Tag
Always pin the Keycloak image to a specific version (quay.io/keycloak/keycloak:25.0 above, not latest). Keycloak’s release notes regularly include breaking changes to configuration keys and startup behavior between major versions, and an unpinned tag means your stack can change behavior on a routine docker compose pull without warning. Check the Keycloak documentation for the migration notes before jumping multiple versions.
Running in Production Mode vs Development Mode
The start command in the example above runs Keycloak in production mode, which enforces hostname configuration and expects TLS to be handled somewhere (either by Keycloak itself or, more commonly, by a reverse proxy in front of it). Keycloak also has a start-dev command intended purely for local testing — it disables several production safeguards and should never appear in a keycloak docker compose file meant for anything beyond a laptop experiment.
Configuring Environment Variables and Secrets
Hardcoding admin passwords and database credentials directly in docker-compose.yml is a common mistake. Instead, use a .env file alongside the compose file and reference variables with ${VARIABLE_NAME} syntax, as shown above.
# .env
POSTGRES_PASSWORD=change_me_to_a_real_secret
KEYCLOAK_ADMIN_USER=admin
KEYCLOAK_ADMIN_PASSWORD=change_me_to_a_real_secret
KEYCLOAK_HOSTNAME=auth.example.com
Docker Compose automatically loads a .env file in the same directory, so no extra flags are needed. Add .env to .gitignore so secrets never end up in version control. For a deeper look at variable precedence, override files, and multi-environment setups, see Docker Compose Env: Manage Variables the Right Way.
For anything beyond a small personal deployment, consider moving secrets out of plain .env files entirely and into Docker Compose’s native secrets support, which mounts values as files inside the container rather than as environment variables visible in docker inspect output. The pattern is covered in detail in Docker Compose Secrets: Secure Config Management Guide.
Setting Up the Keycloak Admin User
The KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD environment variables only take effect on the very first startup, when Keycloak initializes its database schema and creates the bootstrap admin account. If you change these variables after the first run, Keycloak won’t retroactively update the admin account — you’ll need to create additional admin users through the admin console or the kc.sh CLI inside the container instead.
Persisting Data and Managing the Database
Every keycloak docker compose setup needs a persistence strategy for two things: the Postgres data directory and, if you use file-based realm exports, the realm configuration itself.
The named volume keycloak_pg_data in the example above ensures the database survives container restarts and docker compose down (without the -v flag). If you’re new to how volumes interact with the container lifecycle, Docker Compose Down: Full Guide to Stopping Stacks explains exactly what gets removed and what persists under each variant of the down command.
For a closer look at tuning Postgres itself as a Compose service — connection limits, resource constraints, backup volumes — see Postgres Docker Compose: Full Setup Guide for 2026 or the community-maintained image details covered in PostgreSQL Docker Compose: Full Setup Guide 2026.
Exporting and Importing Realm Configuration
Realms, clients, roles, and identity provider mappings can be exported to JSON and mounted into the container at startup, which is useful for reproducible environments (dev, staging, production all starting from the same baseline realm).
docker compose exec keycloak /opt/keycloak/bin/kc.sh export
--dir /opt/keycloak/data/export --realm myrealm
To import on a fresh container, mount the exported directory as a volume and add --import-realm to the startup command, pointing Keycloak at the mounted path.
Backing Up the Database Independently
Even with a named volume, you should take regular logical backups of the Postgres database rather than relying solely on the volume surviving. A simple cron-driven pg_dump against the postgres service, piped to a file outside the container, is enough for most small deployments:
docker compose exec -T postgres pg_dump -U keycloak keycloak > keycloak_backup.sql
Setting Up a Reverse Proxy in Front of Keycloak
Running Keycloak behind a reverse proxy handles TLS termination and lets you serve it on the standard HTTPS port without giving the container access to port 443 directly. Nginx, Caddy, and Traefik are all common choices; Cloudflare in front of the proxy adds another layer of caching and DDoS protection, which is covered from a different angle in Cloudflare Page Rules: Complete Setup & Optimization Guide.
A minimal Nginx server block proxying to the Keycloak container looks like this:
server {
listen 443 ssl;
server_name auth.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
The KC_PROXY: edge setting in the compose file’s environment block tells Keycloak to trust the X-Forwarded-* headers from the proxy rather than expecting TLS to terminate inside the container itself. Without this setting, Keycloak generates incorrect redirect URLs (http:// instead of https://), which is one of the most common issues reported after a first keycloak docker compose deployment goes live behind a proxy.
Health Checks and Startup Ordering
depends_on in Compose only waits for the dependent container to start, not for Postgres to be ready to accept connections. Keycloak retries its database connection internally, so a bare depends_on is usually tolerable, but adding an explicit health check makes startup ordering deterministic and failures easier to diagnose:
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 5s
timeout: 5s
retries: 5
Then reference it with depends_on: { postgres: { condition: service_healthy } } on the Keycloak service so it genuinely waits.
Networking, Ports, and Scaling Considerations
In the base example, Keycloak binds to 127.0.0.1:8080, meaning the port is only reachable from the host itself — the reverse proxy handles all external traffic. This is deliberate: never expose Keycloak’s admin console directly to the public internet without a proxy and TLS in front of it.
If you’re deploying multiple containers on the same host — Keycloak, its database, and unrelated services like an automation engine — keep each stack on its own Compose network to avoid unintended cross-talk. The general pattern for managing multiple services this way is the same one used in n8n Self Hosted: Full Docker Installation Guide 2026, where a similar database-plus-app pairing runs in isolation on its own bridge network.
For debugging container startup issues or watching Keycloak’s Java stack traces during a failed boot, docker compose logs -f keycloak is the first command to reach for — a full walkthrough of filtering, following, and exporting logs from a Compose stack is in Docker Compose Logs: The Complete Debugging Guide.
Where to Host Your Keycloak Compose Stack
Keycloak with Postgres is not a heavy workload for small-to-medium user bases, but the JVM has a meaningful baseline memory footprint — plan for at least 2GB of RAM dedicated to the stack, more as realm and session counts grow. A small managed VPS is enough for most self-hosted identity setups; providers like DigitalOcean and Hetzner both offer VPS tiers suitable for a keycloak docker compose deployment serving a handful of applications, and either lets you resize the instance later if session volume grows.
Upgrading Keycloak Versions Safely
When bumping the image tag, always check the release notes for database migration steps first — Keycloak automatically migrates its schema on startup when the version changes, but rolling back afterward is not supported. Take a database backup before any version bump, apply the change to a staging copy of the stack first if you have one, and only then update production. If you need to rebuild the stack after changing the compose file itself (not just the image tag), Docker Compose Rebuild: Complete Guide & Best Tips covers the difference between up, up --build, and a full recreate.
Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.
FAQ
Does a keycloak docker compose setup need a separate database container?
Technically no — Keycloak includes an embedded H2 database for quick testing — but Keycloak’s own documentation is explicit that H2 is unsupported for production. Any real deployment should run Postgres, MySQL, or another supported database as a separate service in the same compose file.
Why does Keycloak redirect to http:// instead of https:// behind my reverse proxy?
This almost always means the KC_PROXY environment variable isn’t set, or your proxy isn’t forwarding the X-Forwarded-Proto header. Set KC_PROXY: edge in the Keycloak service’s environment and confirm your proxy config passes that header through, as shown in the reverse proxy section above.
Can I run Keycloak and my application in the same docker-compose.yml?
Yes, and it’s common for smaller deployments — just put both services in the same file on the same network so the application can reach Keycloak by its service name (e.g., http://keycloak:8080) for token validation, while external users still go through the reverse proxy.
How do I reset the Keycloak admin password after the first startup?
The KEYCLOAK_ADMIN_PASSWORD variable only applies on initial database creation. After that, reset it via docker compose exec keycloak /opt/keycloak/bin/kc.sh bootstrap-admin commands, or through the admin console itself if you still have console access.
Conclusion
A keycloak docker compose file gives you a reproducible, version-controlled way to run a full identity provider — Keycloak plus its database — with a single docker compose up. The pieces that matter most are using a real database instead of the embedded H2 store, keeping secrets out of the compose file itself, configuring KC_PROXY correctly when running behind a reverse proxy, and treating the Postgres volume and regular pg_dump backups as equally important. Start with the minimal two-service file above, then layer in secrets management, health checks, and a reverse proxy as your deployment moves from a local test toward production. For official configuration reference beyond what’s covered here, the Keycloak Server Administration Guide and Docker’s Compose file reference are the two primary sources worth bookmarking.
Leave a Reply