How to Deploy Keycloak on a VPS: Self-Hosted Single Sign-On and Identity Management

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

  1. Visit https://auth.yourdomain.com/admin → log in with admin credentials
  2. Top-left dropdown → Create realm
  3. 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)

  1. Clients → Create client
  2. Client ID: my-nextjs-app
  3. Client Protocol: OpenID Connect
  4. Root URL: https://yourdomain.com
  5. Valid redirect URIs: https://yourdomain.com/api/auth/callback/keycloak
  6. Web origins: https://yourdomain.com
  7. 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.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!