Identity and access management compose example.
Educational example only. Review every volume path, port, and credential before you run this. Do not copy production secrets into your wiki or git repo.
_shared/.env for non-secrets, _shared/.secret for credentialsmkdir -p /path/to/docker/stacks/keycloak
cd /path/to/docker/stacks/keycloak
# paste compose.yaml below, then:
docker compose pull
docker compose up -d
---
services:
keycloak:
image: quay.io/keycloak/keycloak:26.4.7 # Keycloak image
container_name: keycloak
hostname: keycloak
env_file:
- /path/to/docker/stacks/_shared/.env
- /path/to/docker/stacks/_shared/.secret
environment:
# ───────────────────────────────────────────────────────────────
# 1) Database — KC_DB_USERNAME/KC_DB_PASSWORD come from
# _shared/.env + .secret via env_file (names already match)
# ───────────────────────────────────────────────────────────────
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://YOUR_HOST.YOUR_DOMAIN:5432/keycloak
# ───────────────────────────────────────────────────────────────
# 2) Admin user — KC_BOOTSTRAP_ADMIN_USERNAME/PASSWORD come from
# _shared/.env + .secret via env_file (only used on first start)
# ───────────────────────────────────────────────────────────────
KC_HTTP_MANAGEMENT_PORT: 9001
# ───────────────────────────────────────────────────────────────
# 3) Hostname / proxy settings — KC_HOSTNAME comes from
# _shared/.env via env_file (very important behind reverse proxy!)
# ───────────────────────────────────────────────────────────────
KC_HOSTNAME_STRICT: true
KC_HTTP_ENABLED: true # TLS terminates at Apache, on this same host
KC_PROXY_HEADERS: xforwarded
KC_PROXY_TRUSTED_ADDRESSES: 127.0.0.1/32 # Apache runs on this host
# ───────────────────────────────────────────────────────────────
# 4) LDAPS / Active Directory truststore (your .p12 file)
# ───────────────────────────────────────────────────────────────
KC_TRUSTSTORE_PATHS: /opt/keycloak/conf/truststores
# KC_TRUSTSTORE_PASSWORD comes from _shared/.secret via env_file (not interpolated here)
# Optional – relaxes hostname check (only if SAN mismatch)
KC_TLS_HOSTNAME_VERIFIER: DEFAULT #ANY/DEFAULT
# ───────────────────────────────────────────────────────────────
# 5) Logging / health / features
# ───────────────────────────────────────────────────────────────
KC_LOG_LEVEL: info
KC_HEALTH_ENABLED: true
KC_METRICS_ENABLED: true
# KC_FEATURES: token-exchange,organization # add if needed
logging:
driver: json-file
options:
max-file: ${DOCKERLOGGING_MAXFILE:-10} # Max number of log files
max-size: ${DOCKERLOGGING_MAXSIZE:-200k} # Max file size
ports:
- 38080:8080
volumes:
- /path/to/docker/data/keycloak/certs:/opt/keycloak/conf/truststores:ro
command:
- start
restart: unless-stopped
# Set default directory permissions (rwxr-xr-x)
#sudo find /path/to/docker/keycloak -type d -exec chmod 755 {} +
# Set default file permissions (rw-r--r--)
#sudo find /path/to/docker/keycloak -type f -exec chmod 644 {} +
# Tighten sensitive certs/secrets (rw-------)
#sudo chmod 600 /path/to/docker/keycloak/certs/*
_shared/.secret.