Why deploy Keycloak on your VPS
Where some IdPs aim for simplicity, Keycloak aims for completeness: multi-tenant isolation through realms, LDAP/Active Directory directory federation, brokering to external providers (Google, GitHub, another OIDC), full theming of login pages and fine-grained authorization. For an agency managing several clients or an IT department with compliance requirements, this level of control is decisive. Self-hosting on a VPS spares you the per-user costs of managed IdP offerings and keeps the entire identity base under your jurisdiction, a critical point for sensitive data you would rather not entrust to a third party.
The strengths of a self-hosted Keycloak
- Multiple realms to fully isolate several clients or environments on a single instance.
- Native, complete support for OpenID Connect, OAuth2 and SAML 2.0.
- LDAP / Active Directory federation to reuse an existing directory without migration.
- Identity brokering: sign-in via Google, GitHub or any external IdP in a few clicks.
- Fully customizable login pages (themes, languages, per-realm branding).
- Fine-grained authentication policies: conditional MFA, session duration, role-based constraints.
Prerequisites for this deployment
Keycloak runs on the JVM (Quarkus since v17+) and remains more resource-hungry than a lightweight IdP. Plan for a VPS with at least 2 vCPUs and 4 GB of RAM, ideally 6 GB if you expect load or several active realms. An external PostgreSQL database is strongly recommended in production (never use the embedded H2 database beyond testing). Docker and Docker Compose, a dedicated domain (sso.mydomain.com) and a reverse proxy handling SSL round out the prerequisites, because Keycloak expects to be told that it is served behind an HTTPS proxy.
Deploy Keycloak with PostgreSQL
Define Keycloak and PostgreSQL in Compose
In your docker-compose.yml, declare the postgres service (image postgres:16) and the keycloak service (image quay.io/keycloak/keycloak). Connect Keycloak to the database via KC_DB=postgres, KC_DB_URL, KC_DB_USERNAME and KC_DB_PASSWORD.
Configure production mode and the proxy
Launch with the start command (not start-dev). Set KC_HOSTNAME=sso.mydomain.com and KC_PROXY_HEADERS=xforwarded so that Keycloak generates correct URLs behind the reverse proxy. Define the initial admin via KC_BOOTSTRAP_ADMIN_USERNAME / KC_BOOTSTRAP_ADMIN_PASSWORD.
Start and complete the build
Run docker compose up -d. On first start, Keycloak performs an optimized Quarkus build and then migrates the PostgreSQL schema. Follow docker compose logs -f keycloak until the message indicating that the server is listening.
Set up the SSL reverse proxy
Route sso.mydomain.com to port 8080 of the Keycloak container via Caddy, Traefik or Nginx, with a Let's Encrypt certificate. Be sure to pass the X-Forwarded-* headers, otherwise login redirects will break.
Create a realm and a client
Log in to the admin console, create a dedicated realm (never the master realm for your apps), then an OIDC client. Set your application's Valid redirect URIs and retrieve the Client ID and secret in the Credentials tab.
Connect an application and test the flow
Configure your app with the discovery URL https://sso.mydomain.com/realms/mon-realm/.well-known/openid-configuration. Create a test user in the realm, start a login and verify the token issuance in the console's Sessions.
Keycloak or Authentik: which to choose?
| Criterion | Keycloak | Authentik |
|---|---|---|
| Target use case | Enterprise SSO, multi-tenant, compliance | Self-hosting, homelab, agencies |
| Protocols | OIDC, OAuth2, SAML, LDAP | OIDC, OAuth2, SAML, LDAP, forward auth |
| Forward auth (proxy) | Not native (via extensions) | Native and central to the tool |
| Resource footprint | High (JVM, 4 GB+ RAM) | Moderate (2-4 GB RAM) |
| Multi-realm / tenants | Excellent (isolated realms) | Tenants supported, less advanced |
| Flow customization | Themes and Java SPI | Visual step-based flows |
| Learning curve | Steep, very complete | More accessible |
| Directory federation | Mature (LDAP/AD) | Good (LDAP) |
In production, never serve Keycloak with the embedded H2 database and never expose the master realm to the public: create a dedicated realm per application or per client, and reserve master for administration. To harden the instance, disable free registration, enable brute force detection in Realm Settings > Security Defenses, and provision everything via realm import files (--import-realm) so you version your configuration rather than clicking through the UI.