Self-Hosted SSO with Keycloak and Docker
• DevOps, Security, Authentication
Self-hosted OpenID Connect provider using Keycloak for SSO testing and development.
Requirements
- Docker and Docker Compose
- 1Password CLI for secure credential storage
- Reverse proxy (Caddy) for SSL termination
- Domain with DNS management access
Architecture
- Keycloak: Identity provider container with persistent storage
- Caddy: Reverse proxy handling SSL and external access
- 1Password CLI: Secure credential retrieval
Docker Compose Configuration
version: '3.8'
services:
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak-sso
command: start-dev
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
KC_HTTP_PORT: 9090
KC_HOSTNAME: auth.yourdomain.com
KC_HOSTNAME_STRICT: false
KC_HTTP_ENABLED: true
KC_PROXY: edge
ports:
- "9090:9090"
volumes:
- keycloak_data:/opt/keycloak/data
restart: unless-stopped
volumes:
keycloak_data:
driver: local
Key settings: KC_PROXY: edge for reverse proxy, start-dev for development mode, persistent volume for data retention.
Secure Credential Management with 1Password
Store the Keycloak admin password in 1Password and retrieve it programmatically. Create an item called "keycloak-admin" with a strong generated password.
Startup Script
#!/bin/bash
# start-keycloak.sh
set -e # Exit on any error
echo "Starting Keycloak with secure credentials..."
# Retrieve admin password from 1Password
echo "Retrieving admin credentials from 1Password..."
ADMIN_PASSWORD=$(op item get "keycloak-admin" --vault dev --reveal --field password)
if [ -z "$ADMIN_PASSWORD" ]; then
echo "Error: Failed to retrieve admin password from 1Password"
exit 1
fi
# Export password as environment variable for docker-compose
export ADMIN_PASSWORD
# Verify Keycloak container isn't already running
if docker ps | grep -q "keycloak-sso"; then
echo "Stopping existing Keycloak container..."
docker-compose down
fi
# Start the containers with password in environment
echo "Starting Keycloak container..."
docker-compose up -d
# Clear password from environment immediately
unset ADMIN_PASSWORD
# Wait for container to start
echo "Waiting for Keycloak to initialize..."
sleep 5
# Verify container is running
if docker ps | grep -q "keycloak-sso"; then
echo "✅ Keycloak started successfully"
echo "Admin console: http://localhost:9090/admin/"
echo "External URL: https://auth.yourdomain.dev/admin/"
else
echo "❌ Failed to start Keycloak"
docker-compose logs keycloak
exit 1
fi
This approach ensures passwords never appear in configuration files and provides secure credential retrieval through the 1Password CLI.
Initial Setup
Create OIDC Client
Access the admin console at http://localhost:9090/admin/ and login with admin and your 1Password-generated password.
- Navigate to Clients → Create client
- Set Client type to "OpenID Connect"
- Enter Client ID (e.g., "test-app")
- Click Next, then Save
Configure Client Settings
In the client's Settings tab:
- Valid redirect URIs: Add your application's callback URL
- Web origins: Add your application's domain
- Client authentication: Enable if you need a client secret
Get Client Credentials
In the Credentials tab, copy the Client Secret for your application configuration.
Create Test User
- Navigate to Users → Add user
- Set Username and Email
- Click Create
- Go to Credentials tab
- Click Set password
- Enter password and set Temporary to "Off"
OIDC Endpoints
Your application will need these URLs:
- Authorization:
https://auth.yourdomain.dev/realms/master/protocol/openid-connect/auth - Token:
https://auth.yourdomain.dev/realms/master/protocol/openid-connect/token - UserInfo:
https://auth.yourdomain.dev/realms/master/protocol/openid-connect/userinfo
Reverse Proxy with Caddy
Configure Caddy to provide SSL termination and external access:
auth.yourdomain.com {
encode zstd gzip
tls {
issuer acme {
disable_http_challenge
}
}
reverse_proxy 127.0.0.1:9090
}
Caddy automatically handles:
- Let's Encrypt certificate provisioning
- HTTPS redirects
- HTTP/2 support
- Automatic certificate renewal
Client Configuration Gotchas
When integrating with applications, the redirect URI configuration is critical and often misunderstood:
Understanding OAuth2 Flow
Many applications use a three-party flow:
- Application redirects user to your Keycloak
- After authentication, Keycloak redirects to an intermediary (like AWS Cognito)
- Intermediary processes the response and redirects to final application
Redirect URI Configuration
The redirect URI in your Keycloak client must point to where the application expects the callback, not necessarily the application itself:
# Example: Application using AWS Cognito as intermediary
Valid Redirect URI: https://company-app.auth.us-east-2.amazoncognito.com/oauth2/idpresponse
# Not the application URL:
# Valid Redirect URI: https://app.company.com/callback
Domain Matching is Critical
SSO providers often route based on email domain. If your test email is user@company.com, configure the SSO domain as company.com, not your auth server hostname.