Skip to Content

Keycloak Docker Setup Guide: Self-Hosted SSO Without the Headaches

Deploy Keycloak with Docker, connect your first app, and stop managing passwords in spreadsheets.

Keycloak Docker Setup Guide: Self-Hosted SSO Without the Headaches

Every app wants its own user database. Before long you are juggling five password policies, two-factor setups, and a spreadsheet of who has access to what. Keycloak fixes this. It is an open-source identity broker that speaks OAuth2, OpenID Connect, and SAML. This guide gives you a production-ready Keycloak Docker setup guide—from a running container to a working login flow for your application.

If you want a quicker walkthrough focused on speed, see Keycloak Docker Setup Guide: From Zero to Self-Hosted SSO in 30 Minutes.

What You Will Build

By the end of this guide you will have:

  • Keycloak running in Docker with a PostgreSQL backend
  • HTTPS access behind a reverse proxy
  • A custom realm with users and groups
  • An OIDC client registered for your application
  • A working login and logout flow you can test in a browser

Prerequisites

Before you start, make sure you have:

  • A Linux server (Ubuntu 22.04+ or Debian 12+) with at least 2 CPU cores and 4 GB RAM
  • Docker and Docker Compose installed
  • A domain or subdomain pointed at your server (for HTTPS)
  • Basic familiarity with YAML, OAuth2 concepts, and the command line
  • Ports 80, 443, and 8080 available

Check your Docker version:

docker --version
docker compose version

If Docker is missing:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker

Step 1: Deploy Keycloak with PostgreSQL

Keycloak ships with an embedded H2 database for testing. In production, you want PostgreSQL. It handles concurrency, survives restarts, and backs up cleanly.

Create a project directory:

mkdir ~/keycloak-stack && cd ~/keycloak-stack
mkdir -p postgres-data

Write a docker-compose.yml:

version: "3.8"

services:
  postgres:
    image: postgres:16
    container_name: keycloak-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: change…e-db
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
    networks:
      - keycloak-net

  keycloak:
    image: quay.io/keycloak/keycloak:26.1.0
    container_name: keycloak
    restart: unless-stopped
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: change…e-db
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: change…n123
      KC_HOSTNAME: auth.yourdomain.com
      KC_HOSTNAME_STRICT: 'false'
      KC_HTTP_ENABLED: 'true'
      KC_PROXY: edge
      KC_LOG_LEVEL: info
    ports:
      - "8080:8080"
    depends_on:
      - postgres
    networks:
      - keycloak-net
    command: start --optimized

Start the stack:

docker compose up -d

Wait about 30 seconds for the first startup, then open the Admin Console at http://your-server-ip:8080/admin. Log in with the bootstrap credentials from your Compose file.

Step 2: Create a Realm and a User

Keycloak organizes everything into realms. Think of a realm as a tenant. The master realm is for managing Keycloak itself. Create a separate realm for your applications.

Create a Realm

  1. In the Admin Console, click the realm dropdown (currently showing master)
  2. Click Create realm
  3. Name it myapp
  4. Click Create

Create a User

  1. Make sure you are in the myapp realm
  2. Click Users in the left menu, then Create new user
  3. Set username to alice
  4. Click Create
  5. Go to the Credentials tab and set a password
  6. Toggle Temporary to Off so the user is not forced to reset it

You now have a realm and a user. Next, register an application so Alice can actually log in somewhere.

Step 3: Register an OIDC Client

Keycloak calls applications clients. Each client gets a client ID and secret, plus a list of allowed redirect URLs.

  1. In the myapp realm, click Clients, then Create client
  2. Set Client type to OpenID Connect
  3. Set Client ID to my-web-app
  4. Click Next
  5. Enable Standard flow (this is the authorization code flow)
  6. Click Next
  7. Under Valid redirect URIs, add https://app.yourdomain.com/*
  8. Under Web origins, add https://app.yourdomain.com
  9. Click Save

Go to the Credentials tab and copy the Client secret. You will need it in your application.

For a deeper dive into client configuration and advanced flows, check out Keycloak Docker Setup Guide: Self-Hosted SSO That Actually Works.

Step 4: Test the Login Flow

Before wiring this into your real app, verify the flow works. You can use Keycloak's built-in account console as a smoke test.

Open this URL in a browser:

http://your-server-ip:8080/realms/myapp/account

Log in with alice and the password you set. If you see the account management page, your realm, user, and authentication flow are working.

Inspect the OpenID Configuration

Keycloak publishes its OIDC discovery document at a well-known URL:

curl http://your-server-ip:8080/realms/myapp/.well-known/openid-configuration | jq .

This JSON contains the authorization endpoint, token endpoint, and JWKS URI. Any standard OIDC library can consume it.

Step 5: Secure Keycloak with HTTPS and a Reverse Proxy

Running Keycloak on port 8080 over HTTP is fine for testing. In production, you need TLS. The cleanest path is Caddy.

Add Caddy to Your Compose File

  caddy:
    image: caddy:2.8
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config
    networks:
      - keycloak-net

volumes:
  caddy-data:
  caddy-config:

Create Caddyfile:

auth.yourdomain.com {
    reverse_proxy keycloak:8080
}

Update the Keycloak environment to use the public hostname:

      KC_HOSTNAME: auth.yourdomain.com
      KC_HOSTNAME_STRICT: 'false'
      KC_HTTP_ENABLED: 'true'
      KC_PROXY: edge

Restart the stack:

docker compose up -d

Caddy fetches a certificate from Let's Encrypt automatically. Your SSO is now at https://auth.yourdomain.com.

Step 6: Wire Keycloak Into Your Application

Now that Keycloak is running and secured, connect your application. Here is a minimal Node.js Express example using passport-openidconnect.

Install dependencies:

npm install express express-session passport passport-openidconnect

Create server.js:

const express = require('express');
const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');

const app = express();

app.use(session({ secret: 'change-me', resave: false, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());

passport.use('oidc', new Strategy({
  issuer: 'https://auth.yourdomain.com/realms/myapp',
  authorizationURL: 'https://auth.yourdomain.com/realms/myapp/protocol/openid-connect/auth',
  tokenURL: 'https://auth.yourdomain.com/realms/myapp/protocol/openid-connect/token',
  userInfoURL: 'https://auth.yourdomain.com/realms/myapp/protocol/openid-connect/userinfo',
  clientID: 'my-web-app',
  clientSecret: 'your-client-secret-here',
  callbackURL: 'https://app.yourdomain.com/auth/callback',
  scope: 'openid profile'
}, (issuer, profile, done) => {
  return done(null, profile);
}));

passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((obj, done) => done(null, obj));

app.get('/auth/login', passport.authenticate('oidc'));

app.get('/auth/callback',
  passport.authenticate('oidc', { failureRedirect: '/' }),
  (req, res) => res.redirect('/profile')
);

app.get('/profile', (req, res) => {
  if (!req.isAuthenticated()) return res.redirect('/auth/login');
  res.json(req.user);
});

app.listen(3000, () => console.log('App running on port 3000'));

Start the app, visit /auth/login, and you will be redirected to Keycloak. After authenticating, you land back on /profile with Alice's user info.

If you are struggling with the mental model behind realms, clients, and flows, Keycloak Docker Setup Guide: Self-Hosted SSO and Auth That Doesn't Make You Want to Quit breaks it down in plain language.

Tips and Troubleshooting

Keycloak fails to start with database connection errors

  • Ensure PostgreSQL is healthy before Keycloak starts. The depends_on in Compose helps, but Postgres still needs time to initialize
  • Verify the KC_DB_URL matches the Postgres service name: jdbc:postgresql://keycloak-db:5432/keycloak
  • Check that the Postgres password matches in both services

Infinite redirect loops after login

  • Confirm Valid redirect URIs in the client settings includes your callback URL exactly, including the path
  • Make sure KC_PROXY=edge is set so Keycloak trusts headers from the reverse proxy
  • Verify KC_HOSTNAME matches the public URL users see

HTTPS required error in production mode

  • Keycloak production mode disables HTTP by default. Either terminate TLS at the reverse proxy with KC_PROXY=edge, or provide certificates directly to Keycloak
  • Do not set KC_HTTP_ENABLED=true in production without understanding the risk

Admin console shows a blank page

  • This usually means the hostname is misconfigured. Check KC_HOSTNAME and browser dev tools for mixed-content errors
  • If running behind a proxy, ensure KC_PROXY=edge is set and the proxy forwards X-Forwarded-Proto

Sessions not persisting across restarts

  • Without a persistent database, sessions live in memory and vanish on restart. The PostgreSQL setup in this guide fixes that
  • For distributed setups, configure Infinispan caching with KC_CACHE=ispn

Users cannot log in after password change

  • If you left Temporary password on, Keycloak forces a reset at first login. Set it to Off for service accounts or initial user setup

Next Steps

You now have a working Keycloak Docker setup guide in action: a secured realm, a registered client, and a tested login flow. That is the foundation. From here, consider:

  • Adding social identity providers (Google, GitHub, Microsoft) so users can log in with existing accounts
  • Configuring groups and role mappings for fine-grained access control
  • Enabling two-factor authentication via OTP or WebAuthn
  • Setting up email verification and password reset flows with SMTP
  • Using Keycloak's LDAP federation to sync with an existing corporate directory

SSO is not just about convenience. It is about owning your auth stack, auditing access, and sleeping better at night.

Need Help at Scale?

If you are running production workloads and need Keycloak clustering, custom themes, advanced federation, or migration from Auth0 / Okta, we can help. Contact the Sysbrix team and we will build an identity layer that fits your architecture.

MinIO Self-Host Setup: Build S3-Compatible Object Storage in 30 Minutes
Deploy MinIO with Docker, configure buckets and policies, and replace AWS S3 for a fraction of the cost—without losing compatibility.