Running Keycloak with Postgres in Docker

April 2, 2023 · 3 min read

I was recently tasked with evaluating Laravel-based and external SSO workflows and stumbled on Keycloak in several places. There is a Socialite provider as well as Supabase auth so I needed a way to make a quick evaluation. I chose to use Docker as I didn't want to dive into the world of Java installations. There is some extensive documentation, but like some OSS projects, it can be a firehose at times.

Fortunately, Google came to the rescue with many resources I've included at the bottom of this post for reference. A Docker compose file to spin up the Keycloak container and Postgres to store its data. The docker image needs environment variables set and the best way I know to do that is through direnv and specifically the asdf version manager plugin for it.

docker-compose.yml

I lifted this template from this StackOverflow post and surgically altered it for my purposes. I commented out the parts that weren't relevant and stripped away the backend and frontend services since I no longer needed them.

---
version: "3.8"

services:
  database:
    image: postgres:14
    container_name: database
    environment:
    # add multiple schemas
      # POSTGRES_MULTIPLE_DATABASES: ${DB_DATABASE},${KEYCLOAK_DATABASE}
      POSTGRES_DB: ${DB_DATABASE}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      # POSTGRES_KEYCLOAK_USER: ${KEYCLOAK_USER}
      # POSTGRES_KEYCLOAK_PASSWORD: ${KEYCLOAK_PASSWORD}
      # POSTGRES_DB2: ${KEYCLOAK_DATABASE}
    hostname: local
    restart: always
    volumes:
      - ./db-data:/var/lib/postgresql/data/
      - ./sql:/docker-entrypoint-initdb.d/:ro
      # - ./sql/access_attempt.sql:/docker-entrypoint-initdb.d/A.sql
      # - ./sql/bceid.sql:/docker-entrypoint-initdb.d/B.sql
      # - ./sql/lookup_activitytype.sql:/docker-entrypoint-initdb.d/C.sql
      # - ./sql/lookup_gender_pronoun.sql:/docker-entrypoint-initdb.d/D.sql
      # - ./sql/client.sql:/docker-entrypoint-initdb.d/E.sql
    ports: 
      - "5439:5432"
    networks:
      - db-keycloak

  keycloak:
    image: quay.io/keycloak/keycloak:21.0.1
    command: ["start-dev"]
    container_name: keycloak
    environment:
      DB_VENDOR: ${DB_VENDOR}
      DB_ADDR: database
      DB_PORT: 5432
      DB_SCHEMA: public
      DB_DATABASE: ${DB_DATABASE}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      KEYCLOAK_USER: ${KEYCLOAK_USER}
      KEYCLOAK_PASSWORD: ${KEYCLOAK_PASSWORD}
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
      KC_PROXY_MODE: edge
      KC_METRICS_ENABLED: true
      KC_HTTP_ENABLED: true
    ports:
      - "8089:8080"
      - "8443:8443"
    depends_on:
      - database
    restart: always
    links: 
      - database
    networks:
      - db-keycloak

networks:
  db-keycloak:
    driver: bridge

.env

This sets the environment variables used by both Postgres and Keycloak.

APP_DOMAIN="localhost"
DB_VENDOR="postgres"
DB_DATABASE="keycloak"
DB_USER="keycloak"
DB_PASSWORD="keycloak"
KEYCLOAK_USER="developer"
KEYCLOAK_PASSWORD="developer"
KEYCLOAK_ADMIN="admin"
KEYCLOAK_ADMIN_PASSWORD="admin"
KC_DB="postgres"
KC_DB_URL="jdbc:postgresql://database/keycloak"
# KC_HOSTNAME_FRONTEND_URL=""
# KC_HOSTNAME_ADMIN_URL=""

Usage Instructions

  1. Run docker compose up to run the containers in interactive mode.

    1. Upon first run your db-data directory should fill up with files and directories.
  2. Run docker compose up -d to run your containers in the background.
  3. Run docker-compose down --rmi all to completely cleanup all containers.
  4. Modify the command command: ["start-dev"] to start keycloak in the other modes. This is necessary as the entrypoint isn't specific enough.

It may be useful to create an optimized Keycloak image, but that wasn't necessary for my purposes.

  1. Modify the image section image: quay.io/keycloak/keycloak:21.0.1 to keycloak-custom:latest to use the custom image.

    1. We can publish this image to our own registry if we wanted but this is fine for now. I doubt we'll want to use this outside of production.
  2. To build the optimized Keycloak image independently, run the following: docker build -t keycloak-custom:latest ..

Resources