Docker Compose: Keycloak + PostgreSQL
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read135 words

Docker Compose: Keycloak + PostgreSQL

The fastest way to get Keycloak running locally is Docker Compose with a PostgreSQL backend. This setup mirrors a real production configuration and takes under 5 minutes.

Prerequisites

docker-compose.yml

Create docker-compose.yml in your project root:

version: '3.9'
services:
postgres:
image: postgres:16-alpine
container_name: keycloak_db
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: keycloak_secret
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
networks:
- keycloak_net
keycloak:
image: quay.io/keycloak/keycloak:25.0
container_name: keycloak
command: start-dev
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak_secret
KC_HOSTNAME: localhost
KC_HOSTNAME_PORT: 8080
KC_HOSTNAME_STRICT: false
KC_HOSTNAME_STRICT_HTTPS: false
KC_HTTP_ENABLED: true
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin_password
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
networks:
- keycloak_net
volumes:
postgres_data:
networks:
keycloak_net:
driver: bridge

Starting Keycloak

# Start in background
docker compose up -d
# Watch logs until ready
docker compose logs -f keycloak
# Look for: "Keycloak 25.0 on JVM ... started in Xs"

Access the admin console at: http://localhost:8080

Login with: admin / admin_password

Stopping and Data Persistence

# Stop (data preserved in postgres_data volume)
docker compose stop
# Stop and remove containers but keep data
docker compose down
# Stop and DESTROY all data
docker compose down -v

Importing a Realm on Startup

Place a realm export file at ./realm-export.json and add to keycloak service:

keycloak:
command: start-dev --import-realm
volumes:
- ./realm-export.json:/opt/keycloak/data/import/realm-export.json

Healthcheck

Verify Keycloak is healthy:

curl -s http://localhost:8080/health/ready
# {"status":"UP","checks":[...]}
# Get the OIDC discovery document for your realm
curl -s http://localhost:8080/realms/master/.well-known/openid-configuration | jq .

Common Issues

Port 8080 in use:

ports:
- "8090:8080"  # Change host port

Slow startup on first run: Keycloak initializes the database schema on first boot. This takes 30-60 seconds — wait for the "started" log message.

Database connection refused: The depends_on with service_healthy ensures Postgres is ready before Keycloak starts. If it still fails, check Postgres logs: docker compose logs postgres.