Keycloak’s default configuration is surprisingly fragile, and a production setup often requires understanding how it’s actually designed to run, not just how it boots up.

Let’s get Keycloak humming in a way that won’t crumble under load. Imagine we’re setting up a cluster, because that’s the only way to get real resilience.

Here’s a typical production setup using Docker Compose. We’ll spin up Keycloak itself, a PostgreSQL database for its persistent data, and Redis for distributed session caching.

version: '3.8'

services:
  keycloak-db:
    image: postgres:13
    environment:
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
      POSTGRES_DB: keycloak
    volumes:
      - keycloak_db_data:/var/lib/postgresql/data
    networks:
      - keycloak-net

  keycloak-redis:
    image: redis:6
    command: redis-server --requirepass password --loglevel warning
    volumes:
      - keycloak_redis_data:/data
    networks:
      - keycloak-net

  keycloak:
    image: quay.io/keycloak/keycloak:21.1.1
    environment:
      KC_DB: postgres
      KC_DB_URL_HOST: keycloak-db
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password
      KC_HOSTNAME: keycloak.example.com
      KC_HTTP_ENABLED: 'true'
      KC_PROXY: 'edge'
      KC_FEATURES: 'scripts,token-exchange'
      KC_CACHE: redis
      KC_CACHE_REDIS_HOST: keycloak-redis
      KC_CACHE_REDIS_PASSWORD: password
      KC_RESTART_ENABLED: 'true'
      KC_HEALTH_ENABLED: 'true'
      KC_METRICS_ENABLED: 'true'
      KC_LOG_LEVEL: INFO
    ports:
      - "8080:8080"
    depends_on:
      - keycloak-db
      - keycloak-redis
    networks:
      - keycloak-net
    command: start --optimized

networks:
  keycloak-net:

volumes:
  keycloak_db_data:
  keycloak_redis_data:

Let’s break down what’s happening here. We’re using PostgreSQL for robust persistence, Redis for lightning-fast session replication, and Keycloak itself. The KC_HOSTNAME is crucial – it’s how Keycloak will identify itself to clients. KC_PROXY: 'edge' tells Keycloak it’s behind a reverse proxy (like Nginx or Traefik), which is standard practice for SSL termination and load balancing. KC_FEATURES enables essential capabilities. KC_CACHE: redis and its associated KC_CACHE_REDIS_* settings wire up the distributed session store, meaning if one Keycloak instance dies, user sessions aren’t lost. KC_RESTART_ENABLED and KC_HEALTH_ENABLED are vital for orchestration tools to manage the lifecycle. start --optimized is the key command for production, enabling performance optimizations.

The mental model here is a clustered Keycloak deployment. Each Keycloak instance is stateless from a session perspective, relying entirely on the shared Redis cache. The database is the single source of truth for configuration, users, and realms. This design allows you to scale out horizontally by running multiple keycloak service instances, each connecting to the same keycloak-db and keycloak-redis. Load balancers distribute traffic across these instances.

When you access Keycloak via keycloak.example.com, the reverse proxy terminates SSL, forwards the request to one of the Keycloak instances. If a user logs in, their session is stored in Redis. If that instance fails, another instance can pick up the request and retrieve the session data from Redis, providing a seamless experience. The database ensures that all instances see the same realm configuration, users, and clients.

A common point of confusion is how Keycloak’s caching works. It’s not just for reducing database load; it’s fundamentally about session replication in a clustered environment. Without a shared cache like Redis, each Keycloak instance would only know about its own user sessions. If a user logged into Instance A and then their next request hit Instance B, they’d be logged out. Redis makes the cluster appear as a single, highly available entity.

The KC_HOSTNAME setting is critical. If it doesn’t match the actual hostname clients will use to access Keycloak, you’ll encounter redirect loops and authentication failures.

This setup provides a solid foundation, but the next logical step is often integrating it with a robust reverse proxy like Nginx or Traefik for SSL termination, load balancing, and advanced routing.

Want structured learning?

Take the full Keycloak course →