Keycloak Installation for Single Sign-On

Keycloak is a powerful open-source identity and access management platform that provides Single Sign-On (SSO), OAuth2/OIDC, and SAML support, allowing you to centralize authentication for all your applications from a self-hosted server. This guide covers deploying Keycloak, configuring realms and clients, setting up OAuth2/OIDC and SAML, and integrating user federation with existing directories.

Prerequisites

  • Ubuntu 20.04+ or CentOS/Rocky Linux 8+
  • Docker (for containerized deployment) or Java 17+
  • PostgreSQL or MySQL for production (H2 embedded for testing only)
  • 2GB+ RAM (4GB+ recommended for production)
  • A domain name for the Keycloak server

Installing Keycloak with Docker

mkdir -p ~/keycloak && cd ~/keycloak

cat > docker-compose.yml << 'EOF'
services:
  postgres:
    image: postgres:16
    container_name: keycloak-db
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak_db_password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    restart: unless-stopped

  keycloak:
    image: quay.io/keycloak/keycloak:latest
    container_name: keycloak
    depends_on:
      - postgres
    command: start
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak_db_password
      KC_HOSTNAME: auth.example.com
      KC_PROXY: edge              # Trust reverse proxy headers
      KC_HTTP_ENABLED: "true"    # HTTP for behind-proxy setup
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: change_this_admin_password
    ports:
      - "8080:8080"
    restart: unless-stopped

volumes:
  postgres-data:
EOF

docker compose up -d
docker compose logs -f keycloak

Wait for Keycloak to start (about 60-90 seconds), then access:

  • Admin console: http://your-server:8080/admin
  • Account portal: http://your-server:8080/realms/master/account

Installing Keycloak on Bare Linux

# Install Java 17
sudo apt-get install -y openjdk-17-jdk

# Download Keycloak
KEYCLOAK_VERSION=24.0.4
wget "https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.tar.gz"
sudo tar -xzf keycloak-${KEYCLOAK_VERSION}.tar.gz -C /opt/
sudo ln -s /opt/keycloak-${KEYCLOAK_VERSION} /opt/keycloak
sudo useradd -r -s /sbin/nologin keycloak
sudo chown -R keycloak:keycloak /opt/keycloak*

# Configure database and settings
sudo tee /opt/keycloak/conf/keycloak.conf << 'EOF'
db=postgres
db-url=jdbc:postgresql://localhost/keycloak
db-username=keycloak
db-password=keycloak_db_password

hostname=auth.example.com
proxy=edge
http-enabled=true
http-port=8080
EOF

# Build Keycloak (required before first start)
sudo -u keycloak /opt/keycloak/bin/kc.sh build

# Create systemd service
sudo tee /etc/systemd/system/keycloak.service << 'EOF'
[Unit]
Description=Keycloak Identity Provider
After=network.target postgresql.service

[Service]
User=keycloak
Group=keycloak
EnvironmentFile=-/etc/default/keycloak
ExecStart=/opt/keycloak/bin/kc.sh start
Restart=on-failure
RestartSec=10
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

# Set admin credentials as environment variables
sudo tee /etc/default/keycloak << 'EOF'
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=change_this_admin_password
EOF

sudo chmod 600 /etc/default/keycloak

sudo systemctl daemon-reload
sudo systemctl enable keycloak
sudo systemctl start keycloak

Realm Configuration

A realm is an isolated namespace for users, clients, and identity providers. Create separate realms per organization or environment:

  1. Log into the Admin Console at http://your-server:8080/admin
  2. Click the realm dropdown (top-left, shows "master") > Create Realm
  3. Enter name: myapp and click Create

Via CLI (kcadm.sh):

# Log into admin CLI
/opt/keycloak/bin/kcadm.sh config credentials \
  --server http://localhost:8080 \
  --realm master \
  --user admin \
  --password change_this_admin_password

# Create a realm
/opt/keycloak/bin/kcadm.sh create realms \
  -s realm=myapp \
  -s displayName="My Application" \
  -s enabled=true \
  -s registrationAllowed=false \
  -s resetPasswordAllowed=true \
  -s loginWithEmailAllowed=true

Creating Clients for Applications

A client represents an application that uses Keycloak for authentication:

# Create an OIDC client for a web app
/opt/keycloak/bin/kcadm.sh create clients \
  -r myapp \
  -s clientId=my-webapp \
  -s name="My Web Application" \
  -s enabled=true \
  -s publicClient=false \
  -s 'redirectUris=["https://app.example.com/*"]' \
  -s 'webOrigins=["https://app.example.com"]' \
  -s serviceAccountsEnabled=true

In the UI:

  1. Go to Clients > Create Client
  2. Select OpenID Connect
  3. Set Client ID: my-webapp
  4. Set Valid redirect URIs: https://app.example.com/*
  5. Under Credentials, copy the Client Secret

OAuth2 and OIDC Integration

Integration endpoints for your realm:

# Discovery document (contains all endpoint URLs)
curl https://auth.example.com/realms/myapp/.well-known/openid-configuration

# Key endpoints:
# Authorization:    /realms/myapp/protocol/openid-connect/auth
# Token:            /realms/myapp/protocol/openid-connect/token
# Userinfo:         /realms/myapp/protocol/openid-connect/userinfo
# JWKS:             /realms/myapp/protocol/openid-connect/certs
# Logout:           /realms/myapp/protocol/openid-connect/logout

Example authorization code flow:

# Step 1: Redirect user to authorization endpoint
# https://auth.example.com/realms/myapp/protocol/openid-connect/auth?
#   client_id=my-webapp&
#   response_type=code&
#   scope=openid email profile&
#   redirect_uri=https://app.example.com/callback&
#   state=random-state-value

# Step 2: Exchange authorization code for tokens
curl -X POST https://auth.example.com/realms/myapp/protocol/openid-connect/token \
  -d "grant_type=authorization_code" \
  -d "client_id=my-webapp" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=https://app.example.com/callback"

# Step 3: Use access token to call your API
curl https://api.example.com/protected \
  -H "Authorization: Bearer ACCESS_TOKEN"

Client credentials flow (service-to-service):

curl -X POST https://auth.example.com/realms/myapp/protocol/openid-connect/token \
  -d "grant_type=client_credentials" \
  -d "client_id=my-service" \
  -d "client_secret=YOUR_CLIENT_SECRET"

SAML Integration

For applications that require SAML 2.0 instead of OIDC:

  1. Create a client with Client Protocol: saml
  2. Set Valid Redirect URIs to your application's SAML endpoint
  3. Download the SP metadata from Keycloak and import into your app
# Get Keycloak's SAML IDP metadata
curl https://auth.example.com/realms/myapp/protocol/saml/descriptor

# Key SAML endpoints:
# SSO:      /realms/myapp/protocol/saml
# SLO:      /realms/myapp/protocol/saml  (with LogoutRequest)

User Federation with LDAP

Connect Keycloak to an existing LDAP/AD directory:

  1. In the realm, go to User Federation > Add Provider > LDAP
  2. Configure:
    • Vendor: Active Directory / OpenLDAP / Other
    • Connection URL: ldaps://ldap.example.com
    • Bind DN: cn=readonly,dc=example,dc=com
    • Bind Credential: readonly password
    • Users DN: ou=users,dc=example,dc=com
  3. Click Test Connection and Test Authentication
  4. Click Save, then Sync All Users

Via CLI:

/opt/keycloak/bin/kcadm.sh create components \
  -r myapp \
  -s name="ldap-provider" \
  -s providerId=ldap \
  -s providerType=org.keycloak.storage.UserStorageProvider \
  -s 'config.vendor=["other"]' \
  -s 'config.connectionUrl=["ldaps://ldap.example.com"]' \
  -s 'config.bindDn=["cn=readonly,dc=example,dc=com"]' \
  -s 'config.bindCredential=["readonly_password"]' \
  -s 'config.usersDn=["ou=users,dc=example,dc=com"]'

Nginx Reverse Proxy

sudo tee /etc/nginx/sites-available/keycloak << 'EOF'
server {
    listen 80;
    server_name auth.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name auth.example.com;

    ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
    }
}
EOF

sudo ln -s /etc/nginx/sites-available/keycloak /etc/nginx/sites-enabled/
sudo certbot --nginx -d auth.example.com
sudo nginx -t && sudo systemctl reload nginx

Troubleshooting

Admin console not accessible

# Check Keycloak is running
docker logs keycloak --tail 50
sudo systemctl status keycloak

# Verify port is listening
ss -tlnp | grep 8080

"Invalid redirect_uri" error

# Add the exact redirect URI to the client's valid redirect URIs
# Wildcards: https://app.example.com/* — note trailing /*

Database connection failed

# Test PostgreSQL connection
psql -h localhost -U keycloak -d keycloak -c "\l"

# Check Docker network
docker exec keycloak ping postgres

LDAP sync fails

# Test LDAP connection from Keycloak
# Admin Console > User Federation > your-ldap-provider > Test Connection

# Check LDAP bind credentials
ldapwhoami -x -H ldaps://ldap.example.com \
  -D "cn=readonly,dc=example,dc=com" -W

Conclusion

Keycloak provides a production-ready SSO platform that handles OAuth2, OIDC, and SAML for all your self-hosted applications through a single identity provider. With realm isolation, LDAP federation, and a comprehensive admin API, it scales from simple single-application deployments to organization-wide identity management serving dozens of applications.