Skip to the content.

Keycloak

Information

Keycloak is an open-source identity and access management platform for applications, APIs, microservices, and internal portals. In practice it is commonly used as a self-hosted alternative to managed identity platforms when teams need control over login flows, federation, client configuration, token settings, and realm-level security policies.

When it is deployed with a persistent database, proper TLS, reverse proxy or ingress protection, controlled secrets, and operational monitoring, Keycloak is a reasonable choice for live production use, including public-facing identity services reachable by users and applications from the public internet.

Keycloak Structure and Terminology

Keycloak is organized as a hierarchy of configuration and identity objects. Understanding this structure makes it easier to design realms, onboard applications, and explain where users, permissions, and integrations live.

Typical hierarchy:

In practice, a useful mental model is:

Keycloak instance -> realm -> users / groups / roles / clients / identity providers / flows

Important glossary notes:

Main Functionalities and Features

Common Developer Use Cases

Supported Protocols and Token Algorithms

Keycloak supports a broad set of classical cryptographic algorithms for signing and validating tokens:

Notes:

Post-Quantum Cryptography (PQC) Notes

Keycloak is still primarily deployed with classical cryptography. For Keycloak, PQC readiness is mainly an edge, transport, and crypto-agility planning topic, not a sign that realm keys and token signatures are already natively post-quantum.

Key points:

Making a Keycloak Deployment More PQC-Ready

The practical meaning of “make Keycloak PQC-ready” usually is:

  1. Keep token verification logic standards-based and algorithm-agile.
  2. Use modern TLS configurations at the reverse proxy / ingress layer and track hybrid PQC support there.
  3. Automate key rotation, certificate rotation, and realm export / import procedures.
  4. Keep inventories of which apps depend on which issuers, audiences, scopes, and signature algorithms.
  5. Avoid hard-coding public keys into applications when discovery or JWKS endpoints can be used instead.

Installation

CentOS, Rocky Linux

  1. Install Java:
    sudo dnf install -y java-21-openjdk-devel
    
  2. Download Keycloak:
    curl -L -o keycloak-25.0.2.tar.gz https://github.com/keycloak/keycloak/releases/download/25.0.2/keycloak-25.0.2.tar.gz
    tar -xzf keycloak-25.0.2.tar.gz
    sudo mv keycloak-25.0.2 /opt/keycloak
    
  3. Optional first run check:
    /opt/keycloak/bin/kc.sh --help
    

Fedora

  1. Install Java:
    sudo dnf install -y java-21-openjdk-devel
    
  2. Download and extract the Keycloak tarball similarly to CentOS / Rocky Linux.

macOS

If a suitable package is available in your environment, a package-manager install may be convenient, but many teams still prefer the official distribution archive for version-pinned setups.

Example with Homebrew:

brew install keycloak

If you need a version that exactly matches your server estate, download the official archive and run it locally instead.

FreeBSD

  1. Install Java:
    pkg install openjdk21
    
  2. Download and extract the Keycloak tarball to a suitable path such as /usr/local/share/keycloak.

OpenIndiana

  1. Install Java:
    pkg install jdk-21
    
  2. Download and extract the Keycloak tarball.

Setup with Docker for Developer

For developer machines, the simplest path is usually a disposable start-dev deployment. This is ideal for local testing, login flow experiments, CLI automation, and app integration work.

Docker Compose

Create a docker-compose.yaml file:

version: '3.8'
services:
    keycloak:
        image: quay.io/keycloak/keycloak:25.0.2
        container_name: keycloak-dev
        environment:
            KC_BOOTSTRAP_ADMIN_USERNAME: admin
            KC_BOOTSTRAP_ADMIN_PASSWORD: admin
        ports:
            - "8080:8080"
        command:
            - start-dev

Start it:

docker compose up -d

Developer notes:

Developer Setup with PostgreSQL

If you want a local environment that behaves more like production, use a separate PostgreSQL container:

version: '3.8'
services:
    postgres:
        image: postgres:16
        environment:
            POSTGRES_DB: keycloak
            POSTGRES_USER: keycloak
            POSTGRES_PASSWORD: keycloak-dev-password
        volumes:
            - postgres_data:/var/lib/postgresql/data

    keycloak:
        image: quay.io/keycloak/keycloak:25.0.2
        depends_on:
            - postgres
        environment:
            KC_BOOTSTRAP_ADMIN_USERNAME: admin
            KC_BOOTSTRAP_ADMIN_PASSWORD: admin
            KC_DB: postgres
            KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
            KC_DB_USERNAME: keycloak
            KC_DB_PASSWORD: keycloak-dev-password
        ports:
            - "8080:8080"
        command:
            - start-dev

volumes:
    postgres_data:

This pattern is useful if developers want to keep local realms, clients, groups, users, themes, and test data between restarts.

The same network-exposure warning applies here: 8080:8080 publishes the port on the Docker host. For local-only use, bind explicitly to 127.0.0.1:8080:8080.

Configuration

Live Environment Setup

For live environments, Keycloak should run against a persistent external database, behind proper TLS, with explicit hostname configuration, controlled secrets handling, and repeatable deployment automation.

Storage Requirements (PostgreSQL)

Keycloak supports several databases, but PostgreSQL is commonly the preferred live choice for stability, performance, and operational familiarity.

Typical live requirements:

Production Docker Setup (docker-compose.prod.yaml)

version: '3.8'
services:
    postgres:
        image: postgres:16
        restart: unless-stopped
        environment:
            POSTGRES_DB: keycloak
            POSTGRES_USER: keycloak
            POSTGRES_PASSWORD: change-this-db-password
        volumes:
            - postgres_data:/var/lib/postgresql/data

    keycloak:
        image: quay.io/keycloak/keycloak:25.0.2
        restart: unless-stopped
        depends_on:
            - postgres
        environment:
            KC_DB: postgres
            KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
            KC_DB_USERNAME: keycloak
            KC_DB_PASSWORD: change-this-db-password
            KC_HOSTNAME: sso.example.com
            KC_PROXY_HEADERS: xforwarded
            KC_HTTP_ENABLED: true
            KC_BOOTSTRAP_ADMIN_USERNAME: admin
            KC_BOOTSTRAP_ADMIN_PASSWORD: change-this-admin-password
        ports:
            - "8080:8080"
        command:
            - start

volumes:
    postgres_data:

Production notes:

Rocky Linux (systemd service)

  1. Create a dedicated user:
    sudo useradd -r -s /sbin/nologin keycloak
    sudo chown -R keycloak:keycloak /opt/keycloak
    sudo mkdir -p /etc/keycloak
    sudo chown root:keycloak /etc/keycloak
    
  2. Create an environment file: sudo nano /etc/keycloak/keycloak.conf

    KC_DB=postgres
    KC_DB_URL=jdbc:postgresql://db.example.internal:5432/keycloak
    KC_DB_USERNAME=keycloak
    KC_DB_PASSWORD=change-this-db-password
    KC_HOSTNAME=sso.example.com
    KC_PROXY_HEADERS=xforwarded
    KC_HTTP_ENABLED=true
    KC_BOOTSTRAP_ADMIN_USERNAME=admin
    KC_BOOTSTRAP_ADMIN_PASSWORD=change-this-admin-password
    
  3. Create the service file: sudo nano /etc/systemd/system/keycloak.service

    [Unit]
    Description=Keycloak Server
    After=network.target
    
    [Service]
    Type=simple
    User=keycloak
    Group=keycloak
    WorkingDirectory=/opt/keycloak
    Environment=JAVA_HOME=/usr/lib/jvm/java-21-openjdk
    EnvironmentFile=/etc/keycloak/keycloak.conf
    ExecStart=/opt/keycloak/bin/kc.sh start --optimized
    Restart=always
    RestartSec=10
    LimitNOFILE=65536
    
    [Install]
    WantedBy=multi-user.target
    
  4. Enable and start:
    sudo systemctl daemon-reload
    sudo systemctl enable --now keycloak
    
  5. Review startup logs:
    sudo journalctl -u keycloak -f
    

Post-Startup Checks for Live Environments

After the service is up, confirm at least the following:

  1. The login page resolves at the expected public hostname.
  2. Admin login works with the intended credentials.
  3. The configured realm issuer matches the externally visible URL.
  4. /.well-known/openid-configuration is reachable for the realm.
  5. Token issuance works for at least one test client.
  6. Reverse-proxy and forwarded-header behavior does not generate wrong redirect URLs.

IAM Delivery Planning Checklist

Before building or changing a Keycloak-based IAM landscape, decide the target operating model first. Many production issues do not come from Keycloak itself, but from unclear ownership, incomplete client onboarding rules, inconsistent role design, or missing recovery processes.

Plan at least the following:

High-Availability and Platform Architecture Notes

Keycloak itself is stateless enough to run multiple application instances, but overall availability depends on the full stack: reverse proxy / ingress, PostgreSQL, DNS, certificates, secrets, monitoring, and restore procedures.

High-Availability Design Basics

For a live HA deployment, aim for:

Important considerations:

Reverse Proxy, Load Balancer, and Network Expectations

When deploying behind Nginx, HAProxy, Traefik, a cloud load balancer, or Kubernetes ingress:

Upgrade and Maintenance Strategy

For long-lived IAM platforms, document upgrade practice up front:

Use Cases and CLI Tools

Setting up Auth0-like functionality with kcadm.sh

Keycloak provides the kcadm.sh CLI tool in the bin directory for server automation and scripting.

1. Authenticate CLI

/opt/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin

2. Create a New Realm (like an Auth0 Tenant)

/opt/keycloak/bin/kcadm.sh create realms -s realm=my-webapp -s enabled=true

3. Create a Web App Client (Public)

Equivalent to a typical SPA / browser-based frontend client:

/opt/keycloak/bin/kcadm.sh create clients -r my-webapp -s clientId=frontend -s publicClient=true -s 'redirectUris=["http://localhost:3000/*"]' -s 'webOrigins=["http://localhost:3000"]' -s enabled=true

4. Create a Machine-to-Machine (M2M) Client

For backend services communicating without an end-user session:

/opt/keycloak/bin/kcadm.sh create clients -r my-webapp -s clientId=microservice-client -s serviceAccountsEnabled=true -s secret=my-client-secret -s enabled=true

5. Get M2M Token via curl

curl --request POST \
  --url http://localhost:8080/realms/my-webapp/protocol/openid-connect/token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=client_credentials \
  --data client_id=microservice-client \
  --data client_secret=my-client-secret

6. Get User Token via Password Grant for Testing

For local testing only, some teams temporarily use direct username / password token requests. Avoid this flow for real frontend design unless you explicitly understand the security and standards implications.

curl --request POST \
  --url http://localhost:8080/realms/my-webapp/protocol/openid-connect/token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=password \
  --data client_id=frontend \
  --data username=test.user \
  --data password=test-password \
  --data scope=openid

7. Revoke Token (Blacklist) via curl

Keycloak supports RFC 7009 token revocation, typically applied to refresh tokens. Revoking a refresh token usually invalidates the associated session path.

curl --request POST \
  --url http://localhost:8080/realms/my-webapp/protocol/openid-connect/revoke \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data token=YOUR_REFRESH_TOKEN \
  --data client_id=microservice-client \
  --data client_secret=my-client-secret

8. Logout (End Session) via curl

curl --request POST \
  --url http://localhost:8080/realms/my-webapp/protocol/openid-connect/logout \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data refresh_token=YOUR_REFRESH_TOKEN \
  --data client_id=microservice-client \
  --data client_secret=my-client-secret

9. Export a Realm

For migration, backup, or local promotion workflows, realm export is often useful:

/opt/keycloak/bin/kc.sh export --dir /tmp/keycloak-export --realm my-webapp

Simple Microservice-Oriented Setup Flow

A common local workflow for Spring Boot or other API teams is:

  1. Start local Keycloak.
  2. Create a realm such as my-webapp.
  3. Create a public frontend client.
  4. Create a confidential backend / service-account client.
  5. Create realm roles such as user, admin, service-api.
  6. Assign roles to test users or service accounts.
  7. Configure backend applications with the realm issuer URL.
  8. Call the token endpoint and test protected APIs with the resulting bearer tokens.

Spring Boot Integration (JWT)

To use Keycloak as an OAuth2 Resource Server in a Spring Boot application:

  1. Add dependency:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>
    
  2. Configure issuer (application.yml):
    spring:
      security:
        oauth2:
          resourceserver:
            jwt:
              issuer-uri: http://localhost:8080/realms/my-webapp
    
  3. Security logic: Spring Security will automatically use the realm .well-known/openid-configuration and JWKS endpoint to validate incoming JWTs.

Java Example with WebClient

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

public class KeycloakClientCredentialsExample {
    public static void main(String[] args) {
        WebClient client = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

        String tokenResponse = client.post()
            .uri("/realms/my-webapp/protocol/openid-connect/token")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .body(BodyInserters
                .fromFormData("grant_type", "client_credentials")
                .with("client_id", "microservice-client")
                .with("client_secret", "my-client-secret"))
            .retrieve()
            .bodyToMono(String.class)
            .block();

        System.out.println(tokenResponse);
    }
}

Python Example with requests

import requests

response = requests.post(
    "http://localhost:8080/realms/my-webapp/protocol/openid-connect/token",
    headers={"Content-Type": "application/x-www-form-urlencoded"},
    data={
        "grant_type": "client_credentials",
        "client_id": "microservice-client",
        "client_secret": "my-client-secret",
    },
    timeout=30,
)

response.raise_for_status()
print(response.json())

IAM Design and Integration Guidance

Roles, Groups, Permissions, and Client Scope Design

Keycloak can become difficult to operate if teams mix business meaning, application entitlements, and technical scopes without structure. A simple design model prevents later cleanup work.

Recommended approach:

Practical rules:

  1. Do not assign most permissions directly to users; prefer group membership and role mapping.
  2. Keep role names stable, explicit, and automation-friendly.
  3. Separate human-user permissions from service-account permissions.
  4. Minimize token claims to what downstream services actually need.
  5. Document which teams are allowed to create new roles, scopes, and protocol mappers.

Example Access-Modeling Pattern

One maintainable pattern is:

This keeps the assignment model understandable for auditors, platform engineers, and application teams.

Secure Authentication and Authorization Flow Choices

In Keycloak projects, selecting the correct flow matters more than simply making login work.

Use these defaults unless there is a strong reason not to:

Avoid or tightly restrict:

Security review checklist:

User Federation and Directory Integration Guidance

Keycloak often sits in front of enterprise directories rather than replacing them. In those setups, federation design must be explicit because it affects onboarding, attribute quality, group mapping, and incident handling.

Common federation questions to decide early:

Practical guidance:

SSSD Considerations

SSSD is usually relevant on Linux hosts and platform operations rather than inside Keycloak itself. The important design point is to understand where identity is being consumed.

Typical pattern:

What to check when SSSD appears in the architecture:

Do not assume Keycloak replaces SSSD, or vice versa. Usually they are adjacent components in the same IAM landscape.

Kubernetes Deployment Guidance

Running Keycloak on Kubernetes is common, but the hard part is not just creating pods. The hard part is designing a secure, reproducible, observable identity platform.

Recommended baseline:

Operational considerations:

Using OpenBao for Kubernetes Secrets

If Keycloak is deployed to live Kubernetes environments, document explicitly how secrets are sourced. Keycloak usually does not read secrets directly from OpenBao by itself. The normal production pattern is that Kubernetes receives the secret from OpenBao through an approved integration and then exposes it to the Keycloak container.

Typical patterns:

For Keycloak, this is especially relevant for:

Important operational note:

Example pattern with a Kubernetes Secret populated from OpenBao:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: keycloak
spec:
    replicas: 2
    template:
        spec:
            containers:
                -   name: keycloak
                    image: quay.io/keycloak/keycloak:25.0.2
                    env:
                        -   name: KC_BOOTSTRAP_ADMIN_USERNAME
                            valueFrom:
                                secretKeyRef:
                                    name: keycloak-bootstrap-admin
                                    key: username
                        -   name: KC_BOOTSTRAP_ADMIN_PASSWORD
                            valueFrom:
                                secretKeyRef:
                                    name: keycloak-bootstrap-admin
                                    key: password
                        -   name: KC_DB
                            value: postgres
                        -   name: KC_DB_USERNAME
                            valueFrom:
                                secretKeyRef:
                                    name: keycloak-db
                                    key: username
                        -   name: KC_DB_PASSWORD
                            valueFrom:
                                secretKeyRef:
                                    name: keycloak-db
                                    key: password
                    args:
                        - start

Planning and security checks for OpenBao integration:

  1. authenticate Kubernetes workloads to OpenBao with a controlled workload identity or service account mapping,
  2. decide whether secrets may be copied into Kubernetes Secret objects or must stay ephemeral,
  3. limit which namespaces and service accounts can read the secret material,
  4. document bootstrap behavior for multi-replica startup,
  5. define how secret rotation is handled for database credentials, client secrets, and break-glass accounts,
  6. and audit who can read or update the OpenBao paths used by the Keycloak platform.

Kubernetes Pre-Go-Live Checklist

Before calling a Kubernetes deployment ready, verify:

  1. ingress resolves to the correct public hostname,
  2. certificates renew correctly,
  3. replica loss does not break login,
  4. database failover behavior is understood and tested as far as feasible,
  5. logs and metrics are collected centrally,
  6. admin access is limited and audited,
  7. realm import / bootstrap automation is repeatable,
  8. OpenBao integration for bootstrap, database, and client secrets works on clean pod startup,
  9. secret rotation procedures are documented and tested where feasible,
  10. and backup plus restore procedures work outside the cluster.

Usage, tips and tricks

Coding Tips and Tricks

Operational Tips and Common Pitfalls

Handover and Coaching Checklist

If the goal is to enable internal teams to work independently, make sure the final documentation package includes:

Recommended coaching topics for internal teams:

  1. how to choose the right client type and flow,
  2. how to request roles and scopes correctly,
  3. how to debug issuer, audience, and token-claim problems,
  4. how to onboard new applications safely,
  5. and how to respond to user-lockout, federation, and certificate incidents.

Backup and Restore Notes

Unlike Vault / OpenBao, Keycloak is usually not backed up by taking application-level snapshots of an internal encrypted storage engine. In most real deployments, the critical backup scope is the database plus configuration and custom artifacts.

Developer / Local Machine Backups

For local environments, first decide whether the setup is disposable:

Recommended local backup scope:

Example local database backup:

pg_dump -h 127.0.0.1 -U keycloak -d keycloak > keycloak-dev-$(date +%F).sql

Example local realm export:

/opt/keycloak/bin/kc.sh export --dir ./backup/keycloak-export --realm my-webapp

Live / Production Backups

For live environments, define a standard backup set:

  1. Scheduled PostgreSQL backups or storage snapshots with tested consistency.
  2. Realm export procedures for important realms where operationally appropriate.
  3. Backup of custom themes, providers, startup configuration, environment files, and reverse-proxy configuration.
  4. Secure custody of bootstrap material, admin recovery process, and any secrets required for external identity providers.
  5. Infrastructure-as-code or deployment manifests so the service can be rebuilt consistently.

Recommended planning decisions before backing up a live Keycloak database:

For most Keycloak installations, a good operational baseline is:

  1. run scheduled logical backups with pg_dump,
  2. back up global roles with pg_dumpall --globals-only when self-managing the database cluster,
  3. use storage or managed-service snapshots where available for faster large-scale recovery,
  4. archive WAL if point-in-time recovery is required,
  5. and test restore regularly in a separate environment.

This combination gives teams both a portable logical backup and a faster infrastructure-level recovery path.

Pre-Backup Checklist for Live Databases

Before running a production backup, check at least the following:

  1. the database host, port, database name, and backup target path are correct,
  2. the backup account has the required read permissions,
  3. there is enough free space locally and on the remote backup target,
  4. the destination is protected with correct filesystem and access-control permissions,
  5. retention cleanup will not accidentally delete the new backup,
  6. the Keycloak and database versions are recorded with the backup metadata,
  7. and alerts exist for failed, incomplete, or unusually small backup files.

Preferred Logical Backup Commands

For live environments, prefer a timestamped file name and explicit output format.

Custom-format backup:

export BACKUP_TS=$(date +%F-%H%M)
pg_dump \
  -h db.example.internal \
  -p 5432 \
  -U keycloak \
  -d keycloak \
  -Fc \
  -f /secure-backup/keycloak-${BACKUP_TS}.dump

Plain SQL backup:

export BACKUP_TS=$(date +%F-%H%M)
pg_dump \
  -h db.example.internal \
  -p 5432 \
  -U keycloak \
  -d keycloak \
  --no-owner \
  --no-privileges \
  -f /secure-backup/keycloak-${BACKUP_TS}.sql

Why teams often prefer -Fc for live backups:

If you manage the full PostgreSQL cluster yourself, also consider backing up globals:

export BACKUP_TS=$(date +%F-%H%M)
pg_dumpall \
  -h db.example.internal \
  -p 5432 \
  -U postgres \
  --globals-only \
  > /secure-backup/postgres-globals-${BACKUP_TS}.sql

This is useful for cluster-level roles and grants that may matter during full-environment recovery.

Typical live database backup example:

pg_dump -h db.example.internal -U keycloak -d keycloak > /secure-backup/keycloak-$(date +%F-%H%M).sql

Example Backup Script Pattern

For repeated operations, use a small non-interactive script rather than ad-hoc terminal history.

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR=/secure-backup
BACKUP_TS=$(date +%F-%H%M)
BACKUP_FILE="${BACKUP_DIR}/keycloak-${BACKUP_TS}.dump"

mkdir -p "${BACKUP_DIR}"

pg_dump \
  -h db.example.internal \
  -p 5432 \
  -U keycloak \
  -d keycloak \
  -Fc \
  -f "${BACKUP_FILE}"

pg_restore --list "${BACKUP_FILE}" > "${BACKUP_FILE}.manifest"
sha256sum "${BACKUP_FILE}" > "${BACKUP_FILE}.sha256"

This pattern helps because it produces the backup, a quick-readable manifest, and an integrity hash in one run.

Kubernetes and Container-Oriented Notes

If Keycloak runs on Kubernetes, the preferred backup target is still the external PostgreSQL service rather than the Keycloak pod itself.

Operational rules:

Example Kubernetes job-style command from a toolbox pod:

PGPASSWORD='strong-password' pg_dump \
  -h postgres.database.svc.cluster.local \
  -U keycloak \
  -d keycloak \
  -Fc \
  -f /backup/keycloak-$(date +%F-%H%M).dump

If credentials are delivered through OpenBao, Kubernetes secrets, or mounted files, make sure the backup job follows the same approved secret-handling path as the main service.

What to Record Together with Each Backup

Each production backup should have enough context so another engineer can restore it correctly later. Record at least:

Immediate Post-Backup Validation

Do not stop at “command finished successfully”. Validate that:

  1. the backup file exists and is non-trivially sized,
  2. the checksum was generated,
  3. the dump can be listed or inspected,
  4. backup logs show no permission or connection warnings,
  5. and the backup was copied to the intended off-host or off-cluster location.

Example quick validation commands:

ls -lh /secure-backup/keycloak-2026-06-11-0100.dump
pg_restore --list /secure-backup/keycloak-2026-06-11-0100.dump | head
sha256sum -c /secure-backup/keycloak-2026-06-11-0100.dump.sha256

Important live backup notes:

What to Verify During Restore Tests

At minimum, verify that you can:

See also