Your Boss Said “SSO” and Now You Own a Directory
Someone in a meeting said “we should have single sign-on,” and by Friday you are staring at three Docker images that all claim to solve it. Keycloak, Authentik and Authelia all log people into things. They differ in how much of an identity department they drag into your closet.
Short version: pick Authelia if you want a login wall for a handful of services and you never need SAML. Pick Authentik if you want a friendly admin UI, SAML, and a flow editor, and you are fine running a server plus a worker. Pick Keycloak if you need realms, deep LDAP or Active Directory federation, fine-grained authorization, or a real high-availability story. If you only need to lock five apps, our earlier post on lightweight options covers that end, and the two-way home lab comparison covers setup of the other two.
Versions checked on 2026-10-20 from GitHub releases: Keycloak 26.8.1, authentik 2026.8.4, Authelia 4.39.28.
Full example: Clone the working Keycloak files at github.com/KingPin/sumguy-examples/security/keycloak-vs-authentik-vs-authelia
The Comparison Table
| Keycloak | Authentik | Authelia | |
|---|---|---|---|
| Language / runtime | Java (Quarkus) | Python (Django) plus Rust and Go components | Go |
| Needs a database | Yes (Postgres, MySQL, MariaDB, MSSQL or Oracle) | Yes (Postgres) | Yes (SQLite, Postgres or MySQL) |
| Redis | No | No (removed in 2025.10) | Optional, for sessions |
| OIDC provider | Yes | Yes | Yes (documented as open beta, also OpenID Certified) |
| SAML 2.0 provider | Yes | Yes | No (on the roadmap as planned) |
| Multi-tenancy | Realms, plus Organizations | Brands (one user pool); Tenancy is alpha and Enterprise-only | None |
| LDAP / AD as user source | Built-in federation | LDAP source sync | LDAP auth backend |
| Acts as an LDAP server | No | Yes (LDAP provider via outpost) | No |
| Fine-grained authorization | Authorization Services | Policies and bindings | Access control rules |
| Clustering | Built-in, documented HA architectures | Scale server and worker on shared Postgres | Stateless with external storage |
| Admin UX | Dense, powerful | Friendly, flow-based | YAML files, no admin UI |
Sources: the Keycloak server guides, the authentik docs and the Authelia docs and roadmap, all read on the date above.
Keycloak: The Forklift
Keycloak is the forklift in this story. It can lift a pallet of 40,000 users, but you do not use it to move a couch.
Realms. A realm is an isolated pool of users, clients, roles and settings. Run one realm for the family lab and another for a client company, and the two never see each other. Newer releases also ship Organizations, which let you group users inside one realm, with “managed” and “unmanaged” members. Realms isolate hard. Organizations group softly.
Federation. Keycloak has a built-in LDAP and Active Directory user federation provider. It can import users on a schedule, pass authentication through to the directory, and (depending on edit mode) write changes back. It also brokers other identity providers, including SAML ones.
Protocols. It speaks OIDC and SAML 2.0 as a provider, and the admin guide documents SAML identity brokering too. If a vendor app only accepts SAML, Keycloak handles it without a second thought.
Authorization. Keycloak has Authorization Services: resources, scopes, policies and permissions that an application can query for decisions. This goes well past “is this user in group X.” Most home labs will never touch it. A company with a policy document will.
Clustering. The docs describe single-cluster deployments and multi-cluster deployments. The older multi-cluster v1 (multi-site) is deprecated in favor of v2, and v2 needs no external Infinispan cluster. It does need an external load balancer and a database that can take roughly twice the write load, per the docs, with the caveat that results vary.
Footprint. Keycloak is a JVM app. The container docs say the heap is sized relative to container memory (70 percent max, 50 percent initial), so you should always set a memory limit. The documented floor is at least 750 MB, and the recommended limit for smaller production deployments is 2 GB. Plan on that, not on whatever it idles at on your laptop.
Keycloak Compose Behind a Reverse Proxy
This runs the production start command, not start-dev, with Postgres. The proxy terminates TLS and talks plain HTTP to Keycloak on 8080.
services: db: image: postgres:17-alpine restart: unless-stopped environment: POSTGRES_DB: keycloak POSTGRES_USER: keycloak POSTGRES_PASSWORD: ${KC_DB_PASSWORD:?set in .env} volumes: - kc_db:/var/lib/postgresql/data
keycloak: image: quay.io/keycloak/keycloak:26.8.1 restart: unless-stopped command: start depends_on: - db mem_limit: 2g environment: KC_DB: postgres KC_DB_URL: jdbc:postgresql://db:5432/keycloak KC_DB_USERNAME: keycloak KC_DB_PASSWORD: ${KC_DB_PASSWORD} # Public URL users see. Required in production mode. KC_HOSTNAME: https://sso.example.com # Proxy terminates TLS, Keycloak listens on plain HTTP 8080. KC_HTTP_ENABLED: "true" # Trust X-Forwarded-* from the proxy. Only safe if the proxy overwrites them. KC_PROXY_HEADERS: xforwarded KC_HEALTH_ENABLED: "true" # First-run admin only. Replace it with a permanent admin, then remove these. KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD:?set in .env} expose: - "8080"
volumes: kc_db:Three gotchas live in that file.
- Production mode refuses to boot without
KC_HOSTNAME(orKC_HOSTNAME_STRICT=false). That is on purpose. - If you put a proxy in front and skip
KC_PROXY_HEADERS, the docs say requests that do origin checking get 403 Forbidden. - Port 9000 serves health and metrics. Do not publish it or proxy it. The docs say to proxy only 8080 (or 8443).
The old KEYCLOAK_ADMIN variables are gone from the current docs. Use the KC_BOOTSTRAP_ADMIN_* pair, which creates a temporary admin. Make a real admin and delete the temporary one.
Federating LDAP With kcadm.sh
The admin UI works, but a script survives rebuilds. This creates a realm, then attaches an LDAP provider. The component keys come from the example in the Keycloak server admin guide. Wait for Bootstrap completed in the Keycloak logs first, because requests before that get a 503.
docker compose exec keycloak bashKCADM=/opt/keycloak/bin/kcadm.sh$KCADM config credentials --server http://localhost:8080 --realm master --user admin
$KCADM create realms -s realm=home -s enabled=trueREALM_ID=$($KCADM get realms/home --fields id --format csv --noquotes)
$KCADM create components -r home \ -s name=corp-ldap -s providerId=ldap \ -s providerType=org.keycloak.storage.UserStorageProvider \ -s parentId="$REALM_ID" \ -s 'config.vendor=["other"]' \ -s 'config.connectionUrl=["ldap://ldap.example.com:389"]' \ -s 'config.usersDn=["ou=people,dc=example,dc=com"]' \ -s 'config.bindDn=["cn=svc-keycloak,dc=example,dc=com"]' \ -s 'config.bindCredential=["change-me"]' \ -s 'config.authType=["simple"]' \ -s 'config.usernameLDAPAttribute=["uid"]' \ -s 'config.rdnLDAPAttribute=["uid"]' \ -s 'config.uuidLDAPAttribute=["entryUUID"]' \ -s 'config.userObjectClasses=["inetOrgPerson, organizationalPerson"]' \ -s 'config.editMode=["READ_ONLY"]'For Active Directory, set vendor to ad, usernameLDAPAttribute to sAMAccountName (the admin UI default is cn), rdnLDAPAttribute to cn, uuidLDAPAttribute to objectGUID, and userObjectClasses to person, organizationalPerson, user. kcadm.sh does not fill in the vendor defaults the admin UI applies, so set every one. Test the bind from the admin UI before you trust it.
Authentik: The Friendly Mid-Size Truck
Authentik is the one people enjoy using. The admin UI is clear, and the “flows” system lets you build login, enrollment and recovery pages by stacking stages.
Protocols. It ships OIDC, SAML and proxy providers, plus an LDAP provider that lets legacy apps bind to authentik as if it were a directory. Keycloak does not do that last one natively.
Directory. Authentik has an LDAP source that syncs users and groups into authentik. The docs say sync only creates or updates objects in authentik, not on the LDAP server, and password writeback is a separate feature.
Multi-tenancy. You get brands, which change the look and domain but share one user pool. Authentik also has a Tenancy feature that gives each tenant its own Postgres schema, but the docs mark it alpha, it needs an Enterprise license per extra tenant, and you manage it through the API only. For hard isolation without those strings, Keycloak realms are the mature option.
Dependencies. Authentik dropped Redis in release 2025.10. The current compose is Postgres, a server container and a worker container. Older tutorials with a Redis service are out of date.
Footprint. The official Docker Compose docs list a host with at least 2 CPU cores and 2 GB of RAM, and call the method suited to test setups and small-scale production. That is the same ballpark as Keycloak’s 2 GB advice, so do not choose authentik to save memory.
Pricing. An Enterprise tier exists. I am not quoting a price because it changes, so check the pricing page yourself.
The official compose file is short, and the key bits are these. Fetch the full file from the authentik install docs rather than copying a blog post, including this one.
services: postgresql: image: docker.io/library/postgres:16-alpine environment: POSTGRES_DB: authentik POSTGRES_USER: authentik POSTGRES_PASSWORD: ${PG_PASS:?database password required} volumes: - database:/var/lib/postgresql/data
server: image: ghcr.io/goauthentik/server:2026.8.4 command: server environment: AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__USER: authentik AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} ports: - "9000:9000" - "9443:9443" volumes: - ./data:/data
worker: image: ghcr.io/goauthentik/server:2026.8.4 command: worker # same environment block as server volumes: - ./data:/data
volumes: database:The official file also mounts the Docker socket into the worker and runs the worker as user: root. That socket is root on your host. Know that before you copy it.
Authelia: The Hand Truck
Authelia is a single Go service built as an extension of your reverse proxy. Forward-auth is its home turf, and it also runs an OIDC provider.
The SAML answer is no. Authelia lists a SAML 2.0 provider under “Planning” on its roadmap, so it is not shipped. If any app on your list is SAML-only, Authelia is out.
No realms, no admin UI. Users come from a YAML file or an LDAP server. Authelia does not act as an LDAP server and has no user admin screen. You edit files.
LDAP and AD. It binds to your directory for first factor. The docs list implementations for custom, activedirectory, rfc2307bis, freeipa, lldap and glauth.
Authorization. You get access control rules per domain, path and group, plus OIDC authorization policies. That covers “admins only for Grafana.” It does not cover resource-level permissions.
Footprint. It is a single Go binary with no worker container, so expect the smallest footprint of the three. The docs publish no RAM figure, so I will not invent one. Measure it yourself with docker stats.
This is an LDAP backend plus one OIDC client. The keys come from the Authelia configuration reference.
authentication_backend: ldap: address: 'ldaps://ldap.example.com' implementation: 'activedirectory' base_dn: 'DC=example,DC=com' user: 'CN=svc-authelia,OU=service,DC=example,DC=com' password: 'change-me'
identity_providers: oidc: hmac_secret: '<generated secret>' jwks: - key: | -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- clients: - client_id: 'grafana' client_name: 'Grafana' # pbkdf2 digest, generated with: authelia crypto hash generate pbkdf2 client_secret: '$pbkdf2-sha512$310000$...' authorization_policy: 'two_factor' redirect_uris: - 'https://grafana.example.com/login/generic_oauth' scopes: ['openid', 'profile', 'email', 'groups']Which One Should You Run?
Five to fifteen apps, one household. Authelia. You will not miss realms, and the proxy integration is the shortest path.
A small business with a mix of SaaS and self-hosted apps. Authentik. The SAML provider covers the vendor apps, the flow editor covers the “HR wants a custom signup” request, and a person who is not you can click around the admin UI.
Active Directory already exists, or you serve several organizations. Keycloak. Realms, mature federation and Authorization Services are what it is built for. Accept the JVM and the learning curve.
Fewer than five apps. None of them. Read the lightweight post and move on.
Cost note: Keycloak and Authentik both want about 2 GB of RAM at the vendor’s own recommendation. Authelia does not publish a figure, and I did not benchmark it here. If you have 4 GB of RAM total on that mini PC, Authelia is the one that does not claim half of it on paper.
Migration Between Them
There is no export button that moves users and clients from one to another. Plan around the pieces.
- Users: if they come from LDAP, point the new system at the same directory and nothing moves.
- Users in a local database: export, then import with the target’s API or admin UI.
- Apps: register each OIDC client again on the new provider (you can keep the same client ID), issue a new secret, and point the app at the new issuer URL. The redirect URI stays the same.
- Cut over one app at a time and keep the old provider running until the last one is done.
Your 2 AM self will appreciate a rollback plan that is “switch the issuer back.”
Common Questions
Does Authelia support SAML?
No. Authelia does not support SAML as of version 4.39.28. Its roadmap lists a SAML 2.0 provider under planned work, not shipped work. Authelia provides forward-auth and an OpenID Connect provider, so a SAML-only application such as an older enterprise SaaS tool needs Keycloak or Authentik instead.
Does Authentik still need Redis?
No. Authentik removed Redis in release 2025.10. The current compose file runs Postgres, a server container and a worker container. Tutorials that list a Redis service were written for older releases, and an external Redis configuration can simply be deleted when upgrading.
How much RAM does Keycloak need?
The Keycloak container docs recommend a 2 GB memory limit for smaller production deployments. They say a limit of at least 750 MB approximates the old 512 MB heap. Always set a container memory limit, because Keycloak sizes its heap as a percentage of it.
Can Keycloak federate Active Directory?
Yes. Keycloak has a built-in LDAP and Active Directory user federation provider. You configure it per realm in the admin UI or with kcadm.sh. It supports read-only or writable modes and scheduled sync, so Active Directory stays the source of truth.
What replaced KEYCLOAK_ADMIN in Docker?
The KC_BOOTSTRAP_ADMIN_USERNAME and KC_BOOTSTRAP_ADMIN_PASSWORD variables replaced KEYCLOAK_ADMIN, which no longer appears in the current Keycloak container docs. The bootstrap variables create a temporary admin account on first start. Create a permanent admin user in the admin console afterward, delete the temporary account, and remove the variables from the Compose file.