Self-hosted SSO means running your own identity provider, so the apps your team uses sign in through one login page on a server you control. Authentik and Keycloak are the two common choices: both speak OpenID Connect and SAML, both store everything in PostgreSQL, and both run on a small VPS behind a reverse proxy.
This post compares the two, lists what each needs, shows a reverse proxy setup and the ports, explains what to back up, and sizes a plan. Versions checked are authentik 2026.8.5 and Keycloak 26.8.1, the current releases.
What is self-hosted SSO, and which apps can it front?
An identity provider holds users, groups, passwords and second factors, and issues signed tokens that apps trust. Each app is registered once as a client; users log in at the identity provider and are sent back signed in. Removing someone from one place removes them everywhere, which is the real reason to run it.
Three ways an app can use it:
- OpenID Connect or OAuth 2.0. Most current self-hosted apps support it: Grafana, Nextcloud, Gitea and Forgejo, Immich, Mastodon, and the Headscale control server, whose OIDC login turns your tailnet's devices into SSO-managed ones.
- SAML. Older business software and many paid SaaS tools.
- Forward authentication. For apps with no login integration at all, a proxy checks the session before passing the request on. authentik ships this as its proxy provider; with Keycloak you add a separate proxy such as oauth2-proxy.
Some apps need glue. Jitsi Meet's recommended login, for example, is JWT, so it needs a small token service between it and your identity provider, as our Jitsi Meet guide mentions.
Authentik vs Keycloak: how do they differ?
| authentik | Keycloak | |
|---|---|---|
| Current release | 2026.8.5 | 26.8.1 |
| Protocols as identity provider | OAuth2/OIDC, SAML, plus LDAP, RADIUS, proxy, SCIM providers | OIDC, OAuth 2.0, SAML |
| Apps without SSO support | Built-in proxy provider (outposts) | Separate proxy needed |
| Containers | PostgreSQL, server, worker | Keycloak, plus PostgreSQL |
| Documented minimum | 2 CPU cores, 2 GB RAM for the host | About 1,250 MB base memory per instance |
| Ports | 9000 HTTP, 9443 HTTPS | 8443 HTTPS, or 8080 with HTTP enabled; 9000 management |
| Licence | Open source core with an enterprise edition | Apache-2.0 |
In practice: authentik suits a homelab or small company that wants one tool to cover modern apps, legacy LDAP clients and apps with no login at all, configured through flows in a web UI. Keycloak suits teams that mostly need standard OIDC and SAML, want a long-established project with detailed sizing guidance, or already run Java services. Both have a learning curve; plan an afternoon before the first app works end to end.
Both support second factors such as TOTP apps and WebAuthn security keys, both can pull users from an existing LDAP or Active Directory, and both can let people log in with an outside account such as GitHub or Google if you allow it. The choice is less about features than about which admin model your team finds easier to keep correct.
What do Authentik and Keycloak need to run?
authentik
The Docker Compose guide, described as fit for small-scale production, asks for at least 2 CPU cores and 2 GB of RAM. The current compose file runs three services: PostgreSQL 16, the server and a worker. Setup is a download and two secrets:
wget https://docs.goauthentik.io/compose.yml
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" >> .env
echo "COMPOSE_PORT_HTTP=127.0.0.1:9000" >> .env
echo "COMPOSE_PORT_HTTPS=127.0.0.1:9443" >> .env
docker compose pull && docker compose up -d
The two port lines bind authentik to localhost, because Docker-published ports bypass ufw. The worker mounts the Docker socket to manage outposts; the docs flag that as a risk and suggest a socket proxy or deploying outposts by hand. Then open the site and set the password for the default akadmin user.
Keycloak
Keycloak's sizing guide puts base memory at about 1,250 MB, including realm caches and 10,000 cached sessions. CPU goes mostly to password hashing: one vCPU per 15 password logins a second. As an example, 500 people logging in over five minutes is under 2 logins a second, well inside one core. PostgreSQL 14 to 18 is supported. A production start behind a proxy, on a Docker network shared with a PostgreSQL container:
docker run -d --name keycloak --network sso -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=change-me-now \
-e KC_DB=postgres \
-e KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak \
-e KC_DB_USERNAME=keycloak -e KC_DB_PASSWORD=db-secret \
quay.io/keycloak/keycloak:26.8.1 start \
--hostname https://sso.example.org --http-enabled true --proxy-headers xforwarded
Create your own realm for users and apps; the docs reserve master for managing Keycloak itself.
How do you run SSO behind a reverse proxy, and which ports?
Both expect TLS to end at a proxy. authentik needs the original host, scheme and client IP forwarded, plus WebSocket upgrades for outposts; Caddy does all of that by default:
auth.example.org {
reverse_proxy 127.0.0.1:9000
}
Keycloak's reverse proxy guide recommends exposing only /realms/, /resources/ and /.well-known/, and keeping /admin/ and /realms/master/ internal. Without --proxy-headers, requests through the proxy that Keycloak origin-checks fail with 403 errors.
sso.example.org {
@internal path /admin/* /realms/master/*
respond @internal 404
reverse_proxy 127.0.0.1:8080
}
Reach the admin console over an SSH tunnel or a private network instead, with the hostname-admin option if you give it its own name.
| Port | Service | Open to |
|---|---|---|
| 22/tcp | SSH | Your IP only |
| 80/tcp, 443/tcp | Caddy: certificates and HTTPS | Everyone |
| 9000/tcp, 9443/tcp | authentik HTTP and HTTPS | Localhost only, behind the proxy |
| 8080/tcp | Keycloak HTTP | Localhost only, behind the proxy |
| 9000/tcp | Keycloak management (health, metrics) | Localhost only; clashes with authentik if both share a host |
| 5432/tcp | PostgreSQL | Never public |
Login pages attract password spraying. Every Noded VPS includes 100 Gbps of always-on Layer 3/4 DDoS protection, which stops floods, not guessing: turn on rate limits, lockout or reputation policies in the identity provider, and require a second factor for admins.
Both send mail for verification, recovery and alerts. Outbound SMTP on TCP 25 is blocked on every Noded VPS, which authentik's email docs anticipate: use a relay on port 587 with AUTHENTIK_EMAIL__USE_TLS=true, or the same relay in Keycloak's realm email settings with STARTTLS.
How do you back up the identity store?
If the identity provider is lost, nobody can log in to anything, so treat the backup as the most important one you run. authentik's backup guide calls PostgreSQL the most important part: users, policies, flows and configuration all live there. Also copy /data (icons, uploads, reports), /certs, /custom-templates and /blueprints if you use them, and keep the .env with the secret key somewhere safe.
# authentik (compose service "postgresql")
docker compose exec -T postgresql pg_dump -U authentik -d authentik -Fc > authentik.dump
# Keycloak (PostgreSQL container "postgres")
docker exec postgres pg_dump -U keycloak -d keycloak -Fc > keycloak.dump
For Keycloak, a database dump is the routine backup. Its realm export command needs the server stopped for a consistent result, so keep it for migrations rather than nightly jobs. Our VPS plans have no snapshots or backups, so send the dumps to another machine, ideally in another EU country, with restic or Borg as our backup target post describes. Test a restore before you need one.
Which Noded plan fits self-hosted SSO?
Identity providers are light on bandwidth and heavy on reliability. Prices are monthly, in euros, excluding VAT, from our live catalogue; the RAM guidance follows the projects' documented minimums.
| Use | Plan | vCPU / RAM / SSD | Bucharest | Sofia |
|---|---|---|---|---|
| Homelab or small team, either tool | VPS II | 2 / 4 GB / 40 GB | €6 | €9 |
| Company use, room for PostgreSQL growth | VPS III | 2 / 8 GB / 60 GB | €10 | €16 |
| Many apps, heavy login bursts, or both tools side by side | VPS IV | 4 / 16 GB / 80 GB | €16 | €26 |
VPS I (2 GB) only just meets authentik's minimum before PostgreSQL is counted, so start at VPS II. A second small VPS in another location as the backup target costs from €4. Our PostgreSQL sizing post covers the database side if you move it to its own machine later.
Questions people ask
Is authentik or Keycloak better for self-hosted SSO?
authentik if you want LDAP, RADIUS and proxy login for apps without SSO in the same tool. Keycloak if you mainly need OIDC and SAML and value its detailed sizing and production guidance.
How much RAM does Keycloak need?
The sizing guide puts base memory at about 1,250 MB per instance, with roughly 300 MB of that outside the Java heap. Add PostgreSQL and the operating system, which makes 4 GB a comfortable start.
Can self-hosted SSO protect apps that have no login integration?
Yes, with forward authentication. authentik's proxy provider does it directly; with Keycloak, place a proxy such as oauth2-proxy in front of the app.
Do I need Redis for authentik?
Not with the current compose file, which runs only PostgreSQL, the server and the worker.
Running your identity provider in the EU
An identity store is personal data by definition: names, email addresses, group memberships and login history. Pick Bucharest or Sofia and the VPS and any images you capture stay in the EU under GDPR; our data processing agreement is part of the Terms, and the GDPR hosting page has the details. Order VPS II from the VPS hosting page, connect one low-stakes app first, and move admin accounts over last.