Keycloak can feel like a complex beast, but running it locally with Docker Compose is surprisingly straightforward once you see it in action.

Let’s get Keycloak up and running in a Docker Compose setup. Here’s a minimal docker-compose.yml to get you started:

version: '3.8'

services:
  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    ports:
      - "8080:8080"
    environment:
      KC_HTTP_PORT: 8080
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8080
      KC_DB_URL: jdbc:h2:mem:keycloak
      KC_ADMIN_USER: admin
      KC_ADMIN_PASSWORD: admin
    command: start-dev

To launch this, save it as docker-compose.yml and run docker compose up -d in the same directory. You should see Keycloak start up. Access the admin console at http://localhost:8080/admin/.

Now, let’s break down what’s happening and why it works. The core of this setup is the docker-compose.yml file.

The services block defines the containers we want to run. Here, we have one service named keycloak.

The image: quay.io/keycloak/keycloak:24.0 line tells Docker to pull the official Keycloak image tagged with version 24.0 from Quay.io. Using specific tags is crucial for reproducibility.

The ports section, - "8080:8080", maps port 8080 on your host machine to port 8080 inside the Keycloak container. This is how you’ll access Keycloak from your browser.

The environment block is where we configure Keycloak.

  • KC_HTTP_PORT: 8080 explicitly sets the HTTP port Keycloak listens on inside the container.
  • KC_HOSTNAME: localhost tells Keycloak to advertise itself as localhost.
  • KC_HOSTNAME_PORT: 8080 specifies the port Keycloak should use in its generated URLs.
  • KC_DB_URL: jdbc:h2:mem:keycloak configures Keycloak to use an in-memory H2 database. This is perfect for local development because it requires no external database setup and data is lost on container restart, keeping your local environment clean.
  • KC_ADMIN_USER: admin and KC_ADMIN_PASSWORD: admin set the credentials for the initial administrator account.

Finally, command: start-dev is the magic command that tells the Keycloak container to start in development mode. This mode is optimized for local development, enabling features like auto-discovery of realms and a simplified startup process.

This setup demonstrates a fundamental aspect of modern application deployment: containerization. By packaging Keycloak and its dependencies into a Docker image, we ensure that it runs consistently regardless of your local machine’s environment. Docker Compose then orchestrates this container, making it easy to bring up and manage the service with a single command.

The start-dev command is particularly interesting. It’s not just a startup script; it’s a mode that enables several development-friendly features. For instance, it automatically creates a master realm if one doesn’t exist and allows you to log in with the admin credentials without any prior configuration. It also disables certain security checks that would be necessary in production, such as requiring specific HTTPS configurations.

To illustrate the flexibility, imagine you want to use a PostgreSQL database instead of the in-memory H2. You’d add a PostgreSQL service to your docker-compose.yml and update the KC_DB_URL and add KC_DB_USERNAME, KC_DB_PASSWORD, and KC_DB_DRIVER environment variables for the Keycloak service.

version: '3.8'

services:
  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    ports:
      - "8080:8080"
    environment:
      KC_HTTP_PORT: 8080
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8080
      KC_DB_URL: jdbc:postgresql://db:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password
      KC_DB_DRIVER: org.postgresql.Driver
      KC_ADMIN_USER: admin
      KC_ADMIN_PASSWORD: admin
    command: start-dev
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password

Then, you’d run docker compose up -d again. The depends_on directive ensures the database starts before Keycloak, and the KC_DB_URL now points to the db service running on port 5432.

The initial setup you saw relies on Keycloak’s ability to bind to localhost. While convenient for local testing, in a more distributed or production-like environment, you’d need to configure KC_HOSTNAME_STRICT_HTTPS and KC_HOSTNAME_STRICT to false or set KC_HOSTNAME to your actual domain. The start-dev command simplifies this by assuming a local, non-production context.

The next step in exploring Keycloak’s capabilities would be to integrate it with an application, perhaps using the built-in examples or setting up your own client.

Want structured learning?

Take the full Keycloak course →