How to Deploy Keycloak on a VPS: Self-Hosted Single Sign-On and Identity Management
Keycloak is an open-source identity and access management system from Red Hat — it provides Single Sign-On (SSO), OAuth 2.0 and OpenID Connect (OIDC) authentication, SAML support, user federation (LDAP/Active Directory), social login, and a user management dashboard. Instead of building authentication into each application, applications delegate to Keycloak. Self-hosting on a VPS replaces Auth0 ($23+/month) or Okta for teams that need control over their identity infrastructure.
What Keycloak Provides
- Single Sign-On: Log in once, access all integrated applications without re-authenticating
- OAuth 2.0 / OIDC: Standard protocols for modern web and mobile app authentication
- Social login: Google, GitHub, Microsoft, Facebook identity providers out of the box
- User management: Self-service registration, password reset, profile management
- Two-factor auth: TOTP (Google Authenticator), WebAuthn, email OTP
- Roles and groups: Fine-grained authorization passed as JWT claims to applications
- LDAP/AD federation: Sync users from Active Directory or OpenLDAP
Server Requirements
- Minimum 2 GB RAM (Keycloak JVM needs ~512 MB, total with DB ~1.5 GB)
- KVM virtualization (Docker required)
- Ubuntu 22.04 or 24.04
Step 1: Docker Compose Setup
mkdir -p /opt/keycloak && cd /opt/keycloak
nano docker-compose.yml
version: '3.8'
services:
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
restart: always
command: start
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_HOSTNAME: auth.yourdomain.com
KC_HOSTNAME_STRICT: "false"
KC_HTTP_ENABLED: "true"
KC_PROXY: edge # Nginx handles SSL
KEYCLOAK_ADMIN: ${KC_ADMIN}
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
KC_HEALTH_ENABLED: "true"
KC_METRICS_ENABLED: "true"
ports:
- "127.0.0.1:8080:8080"
depends_on:
keycloak-db:
condition: service_healthy
keycloak-db:
image: postgres:16-alpine
container_name: keycloak-db
restart: always
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- keycloak_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
volumes:
keycloak_db:
nano .env
POSTGRES_PASSWORD=StrongKeycloakDbPassword!
KC_ADMIN=admin
KC_ADMIN_PASSWORD=YourSecureAdminPassword!
chmod 600 .env
docker compose up -d
# Keycloak takes 60–90 seconds to start (JVM warm-up)
docker compose logs -f keycloak
Step 2: Nginx with SSL
sudo nano /etc/nginx/sites-available/keycloak
server {
listen 80;
server_name auth.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name auth.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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_read_timeout 300s;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
}
}
sudo ln -s /etc/nginx/sites-available/keycloak /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d auth.yourdomain.com
Step 3: Create Your First Realm
- Visit
https://auth.yourdomain.com/admin→ log in with admin credentials - Top-left dropdown → Create realm
- Realm name:
myapp→ Create
A realm is an isolated tenant — it has its own users, clients, and settings. Use one realm per application or one realm for all your applications.
Step 4: Create a Client (Your Application)
- Clients → Create client
- Client ID:
my-nextjs-app - Client Protocol: OpenID Connect
- Root URL:
https://yourdomain.com - Valid redirect URIs:
https://yourdomain.com/api/auth/callback/keycloak - Web origins:
https://yourdomain.com - Save → Credentials tab → copy Client Secret
Step 5: Integrate with Next.js (NextAuth)
npm install next-auth
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import KeycloakProvider from 'next-auth/providers/keycloak';
export default NextAuth({
providers: [
KeycloakProvider({
clientId: process.env.KEYCLOAK_CLIENT_ID,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
issuer: process.env.KEYCLOAK_ISSUER,
}),
],
callbacks: {
async jwt({ token, account, profile }) {
if (account) {
token.roles = profile?.realm_access?.roles ?? [];
}
return token;
},
async session({ session, token }) {
session.user.roles = token.roles;
return session;
},
},
});
# .env.local
KEYCLOAK_CLIENT_ID=my-nextjs-app
KEYCLOAK_CLIENT_SECRET=your_client_secret
KEYCLOAK_ISSUER=https://auth.yourdomain.com/realms/myapp
NEXTAUTH_URL=https://yourdomain.com
NEXTAUTH_SECRET=your_nextauth_secret
Step 6: Add Social Login Providers
In Keycloak Admin → Identity Providers:
- Google: Add → Google → enter Client ID and Secret from Google Console
- GitHub: Add → GitHub → enter Client ID and Secret from GitHub OAuth Apps
Users can now log in with their Google or GitHub accounts, and Keycloak maps them to local users.
Step 7: Role-Based Access Control
# In Keycloak Admin:
# Realm Roles → Create Role: "admin", "editor", "viewer"
# Assign roles to users:
# Users → select user → Role mapping → Assign role
# In your application — check roles from JWT:
const session = await getServerSession(authOptions);
if (!session?.user?.roles.includes('admin')) {
return res.status(403).json({ error: 'Access denied' });
}
Getting Started
Keycloak requires 2 GB RAM — a 2 GB KVM VPS at VPS.DO is the minimum; 4 GB provides comfortable headroom for multiple applications and high user counts. For organizations running multiple internal applications (Grafana, Gitea, n8n, custom apps), centralizing authentication in Keycloak eliminates per-application user management and provides a single audit log.
Conclusion
Self-hosted Keycloak replaces Auth0, Okta, and AWS Cognito for teams needing full control over identity infrastructure. Single Sign-On across applications, OAuth 2.0 / OIDC integration, social login providers, two-factor authentication, and role-based access control are all available at VPS infrastructure cost. The Docker Compose setup is production-ready, and the admin dashboard makes user management accessible without database queries.