Keycloak vs authentik vs Zitadel: Which one fit VPS?
Compare Keycloak, authentik and Zitadel for one small VPS: see the real RAM floor, which one fit protect apps without SSO, and where each one falls short.
Which SSO server fit one VPS
Keycloak, authentik and Zitadel na self-hosted single sign-on (SSO) servers wey people dey use when dem want one login for every app for their server. For one small VPS, dem no be the same. authentik na the safe default for server wey dey run three or four self-hosted apps, because na only one among the three wey fit put login screen in front of app wey no get login by itself. Keycloak na the correct choice when every app wey you wan protect already support standard protocol, and you fit spare memory for Java virtual machine (JVM). Zitadel dey built for developers wey dey ship product through an API, and I no go try am with less than 4 GB.
Choose first, then install. After you choose, the practical authentik install for VPS go guide you through the setup step by step.
U dey really need how much RAM each one?
Start with the resource floor, because e go decide the shortlist before any feature matter. The numbers below na each project own published figures, as dem read am for August 2026. Na vendor guidance dem be, no be load test results.
The data behind this chart
[
{
"label": "authentik",
"vendor_min_ram_mb": 2048,
"base_containers": 3
},
{
"label": "Keycloak",
"vendor_min_ram_mb": 1250,
"base_containers": 2
},
{
"label": "Zitadel",
"vendor_min_ram_mb": 2048,
"base_containers": 4
}
]authentik Docker Compose install page dey ask for "a host with at least 2 CPU cores and 2 GB of RAM". This one be 2048 MB, and the compose file wey dem publish dey run 3 containers: PostgreSQL, the server, and the worker.
Keycloak publish the most specific figure among the 3. Its sizing guide talk say "the base memory usage for a Pod including caches of Realm data and 10,000 cached sessions is 1250 MB of RAM". Those 1250 MB na for the Java process alone, before the database. The same page explain why container limit matter well well: Keycloak dey use 70% of the memory limit as heap, then e still need around 300 MB non-heap memory. If you give the container 1 GB, e go calculate heap of about 717 MB and still need that 300 MB non-heap memory. So the limit don finish before any session data enter the cache.
Zitadel compose page too dey ask for 2 GB, the same 2048 MB, but that figure na for first run. Na the production page you suppose read. The Zitadel process itself need "approximately 512MB of RAM and can operate with less than one CPU core". The database na the costly part: "about one CPU core per 100 requests per second (req/s) and 4GB of RAM per core". Password hashing then need "4 CPU cores available for this purpose", because login burst fit turn into CPU spike. The official v4 compose dey run 4 containers before you add anything: Traefik as the proxy, the Zitadel API, separate Login UI container, and PostgreSQL. Redis and OpenTelemetry collector dey behind optional compose profiles.
So Keycloak and authentik fit run for 4 GB VPS and still leave space for the apps wey you dey protect. Zitadel go start for 2 GB, but e go compete with its own PostgreSQL for memory anytime person login. I no go run Zitadel below 4 GB. For machine wey also dey host apps, I go prefer 8.
Wetin each one actually dey run for your box
authentik na PostgreSQL plus two copies of one image: one server and one worker. The server dey answer HTTP and e get embedded outpost. The worker dey run background tasks like directory syncs and email. The published install short.
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
docker compose pull
docker compose up -dThe server dey publish ports 9000 and 9443. The first time you visit port 9000, e go run the initial setup flow, where you go set password for the default akadmin user. Put reverse proxy with real certificate in front of am before internet fit reach that port.
Keycloak na one process plus database wey you provide. The quickstart na single container.
docker run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.7.1 start-devstart-dev na for looking around. E dey run with local development database and no TLS (transport layer security), so if you start container this way and later remove am, your realm go disappear with am. For production, use start instead, with real PostgreSQL through KC_DB and public hostname through KC_HOSTNAME. Keycloak production guide also talk say all communication to and from the server must use secure channel, so HTTPS no be optional there.
Zitadel na the four-container stack wey we describe above.
mkdir zitadel-compose && cd zitadel-compose
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/.env.example
cp .env.example .env
docker compose up -d --waitSet ZITADEL_MASTERKEY inside .env before the first start. Na the 32 character key Zitadel dey use to encrypt secrets for the database, so if you lose am, you go lose access to those secrets. If you never run stacks like these before, the Docker Compose basics for a VPS explain the volume and restart-policy choices wey decide whether your identity provider go survive reboot.
Which protocols each one dey speak?
All three dey speak OpenID Connect (OIDC), the login layer wey dey on top OAuth 2.0 and modern apps dey use. All three dey also speak SAML 2.0 (security assertion markup language), the older standard wey enterprise software still dey release. The main difference na LDAP (lightweight directory access protocol), where one word dey cover two opposite jobs.
Reading from LDAP mean say the SSO server dey check passwords against directory wey you already dey run. Keycloak dey do this through user federation. Zitadel dey do am too: its documentation explain how to "connect an LDAP server as an identity provider in ZITADEL".
Serving LDAP mean say app wey dey speak only LDAP fit bind against your SSO server as if na the directory itself. Na only authentik dey support this. Its LDAP provider make "all users and groups in authentik's database searchable via the LDAP directory" through dedicated LDAP outpost, and LDAPS dey available on port 636. E dey read-only, so bind and search go work, but writes no go work. You append one-time code to the password with semicolon, like password;123456, and SMS authenticators no dey supported during bind.
If one app for your list dey speak only LDAP, comparison don end there. Keycloak and Zitadel no fit answer that bind, so you go need run second directory beside dem and keep two user lists in sync.
Wetin about apps wey no get login at all?
Forward auth na the answer, and na one common case wey self-hosted box dey face. Your reverse proxy go ask the SSO server whether request dey allowed before e pass am to upstream. The app behind am no go know anything about SSO. E go receive requests wey proxy don already check, usually with username for header.
authentik's proxy provider handle this with three documented modes. "Proxy" make authentik outpost pass traffic to the upstream app by itself. "Forward auth (single application)" leave traffic with your existing reverse proxy and use authentik only for authentication check. "Forward auth (domain level)" protect every app under one parent domain with one provider. Domain level na the convenient option, but e get documented limit: e "cannot enforce different application-level authorization rules for each protected application", so every app under that domain share one policy set.
Keycloak no get any of this. Its companion proxy, Keycloak Gatekeeper, dem rename am to Louketo Proxy and later archive am for GitHub, with its last commit for August 2023. To protect app wey no support OIDC, you go run separate component in front of am, usually oauth2-proxy, wey point to a Keycloak client. Zitadel too no get forward auth mode by itself, so the answer there na the same extra component wey you need install, monitor, and upgrade.
Na that extra hop dey make reverse proxy config stop to be simple, so read how Traefik dey front several Docker Compose apps before you wire auth middleware into am.
Upgrade path dey bad reach where?
As of August 2026, the current releases na Keycloak 26.7.1, authentik 2026.5.6, and Zitadel v4.16.3. All three dey run schema migrations against PostgreSQL, so every upgrade dey change database. Back up the database first, every time. This one habit worth pass any feature for this comparison.
authentik get the strictest rule, and e talk am clearly: "Upgrades must follow the sequence of major releases; do not skip directly from an older major version to the most recent version." You go move to the latest patch inside each version before you step go the next one, and "authentik does not support downgrading". If you fall one year behind for project wey dey use calendar version, one upgrade go turn to chain of upgrades. Each one get its own database migration.
Keycloak upgrade guide give order wey you suppose follow: review the migration changes from the previous version, upgrade the server, then upgrade the adapters. Database migration dey run automatically, or you fit export am and apply am by hand. This one useful when you want read the change before e land. The cost with Keycloak na the reading. Its release notes get deprecations and behaviour changes wey easy to skip but expensive to miss.
Zitadel separate its init and setup phases from the running server. Its production guidance recommend say you keep dem separate, so scaling no go repeat the setup work. For one VPS, this mostly mean say the setup step must finish before the API report healthy. Na why the compose file ship health checks and the start command use --wait.
Wetin each project build for, and where each one get limitation
Keycloak na Red Hat identity server, wey dem build for organisations with realms, groups, role mappings, and existing corporate directory. E get the most complete standards implementation among the three. E no too fit for 2 GB VPS wey dey run four self-hosted apps, especially when half of dem no support OIDC. You go use 1250 MB for JVM, learn realm model wey dem design for company environment, and still install oauth2-proxy for the apps wey you really need.
authentik na for self-hosting users, and the feature list show am. E ship with forward auth and LDAP provider, and e use visual editor build login flows. E no too fit when you need vendor support contract or release train wey no dey change every few weeks. Calendar versioning without downgrade path or version skipping na serious operational work. The flow editor na one complete model to learn when your actual problem na just one OIDC client.
Zitadel na for developers wey dey put authentication inside product wey dem release, with strong API and multi-tenancy as first-class features. E no too fit for this exact case. Four containers, no forward auth, and database wey dem size at 4 GB per core get wrong shape for one VPS wey get password manager and wiki behind am.
Wetin I go run for one VPS, and how
For one VPS wey get three or four self-hosted apps, run authentik. All three dey give you login screen. The main deciding factor na say some of your apps no go ever support OIDC, and authentik solve this with forward auth wey e get built in, instead of another component beside am.
Give am 4 GB if you fit, and 2 GB only if the apps wey dey beside am small. Keep port 9000 away from public internet and terminate TLS for reverse proxy wey dey in front. Take PostgreSQL dumps every night and store dem outside the box, because identity provider wey no get backup na single point of failure for every app behind am. Run the stack with dedicated unprivileged account instead of root: how to set up least privilege users for VPS cover the account and file ownership wey this setup need.
Choose Keycloak instead when every app wey you wan protect already support OIDC or SAML, or when you need the fine-grained role model wey Keycloak realms provide. Choose Zitadel when you dey build application wey other people go sign up to, and you want its API and tenant model. None of the two fit the three-apps-on-one-box case wey this post dey talk about.
Di failure wey you go first meet
Keycloak no get data after restart. You start am with start-dev, and e dey use local development database. For container wey no get volume, if you remove the container, the realm go comot. Move go start, with KC_DB=postgres pointing to real database.
authentik worker container dey disappear for small box. The worker and server dey run the same image, and both hold Python processes. PostgreSQL too need its own share of 2 GB host memory. Run docker compose ps to confirm which service exit, then check dmesg for out-of-memory kill before you start dey find bug for the app.
Zitadel's Console no dey work behind your proxy. Zitadel API dey use gRPC, and e need HTTP/2 reach the upstream all through. The requirements page ask for reverse proxy wey support HTTP/2 upstream connections. E name tested versions of Traefik v3.x, NGINX v1.x, Caddy v2.x and Apache httpd 2.4.x. If proxy downgrade the upstream connection to HTTP/1.1, login page go load but Console go fail.
Every app dey send you back to login screen. The SSO server public URL and the URL wey you configure for the app must match exactly, including scheme and port. Keycloak call am hostname setting, while Zitadel call am external domain. If dem no match, the app go redirect to login wey the server no recognise as its own, and browser go dey bounce between the two.
FAQ
Which of the three fit work for a 2 GB VPS?
authentik and Keycloak. authentik requirement na host wey get at least 2 CPU cores and 2 GB of RAM, while Keycloak sizing guide put base memory for the server at 1250 MB before database join. Both tight for 2 GB once you add the apps wey dem dey protect, so treat 4 GB as the comfortable minimum. Zitadel publish 2 GB for first run, but e production guidance ask for 4 CPU cores for password hashing and 4 GB of RAM per database core, so 2 GB no be real deployment.
Keycloak or Zitadel fit protect app wey no get login by itself?
No, dem no fit do am by themselves. Neither one release forward auth component. Keycloak old companion proxy, Louketo Proxy, dey archived for GitHub, and e last commit na August 2023, so no build on top of am. Put oauth2-proxy or similar component between your reverse proxy and the app, then point am to OIDC client for the SSO server. authentik dey do this natively with its proxy provider for "Forward auth (single application)" or "Forward auth (domain level)" mode.
Which one fit act as LDAP server for app wey only dey speak LDAP?
authentik. Its LDAP provider dey run for an outpost and make users and groups for authentik searchable through LDAP, with LDAPS available for port 636. E read-only, so bind and search go work, but writes no go work. Keycloak and Zitadel dey do the opposite: both read from existing LDAP directory as user source, and neither one answer LDAP bind from application.
I fit skip versions when I dey upgrade authentik?
No. The documentation talk say upgrades must follow the sequence of major releases, and say you no suppose jump from older major version straight to the latest one. Move to the latest patch release inside each version first, then move up one version at a time. Back up PostgreSQL before each step, because authentik no support downgrade and migrations only run forward.