How to Install Authentik for Self-Hosted SSO
Table of contents
- Key takeaways
- What has changed since 2025.10
- How to upgrade from 2025.10 to 2026.8 without skipping branches
- Why Authentik and not something else
- The architecture since 2025.10
- The basic deployment
- First start and migrations
- Forward auth with Traefik
- Typical friction points
- Forward auth that stops working after an upgrade: headers and trusted proxies
- Two more checks that prevent surprises
- Frequently asked questions
- Do I need to install Redis to use Authentik?
- Is Authentik better than Keycloak?
- Can I upgrade straight from 2025.10 to 2026.8?
- Conclusion
- Sources
Tested with authentik 2026.8.2 · PostgreSQL 16 · Traefik 3.7 · Docker Compose 2.40 · verified
Updated: 2026-09-16
An Authentik Docker Compose install now needs only three containers: PostgreSQL, the server and the worker, since Redis stopped being mandatory in version 2025.10. Once running, it acts as your identity provider for single sign-on over OAuth2, OIDC, SAML and LDAP, and a reverse proxy such as Traefik can delegate authentication to it through forward auth.
Installing Authentik with Docker Compose deploys an IdP (identity provider: verifies who you are and what you’re allowed to access) that centralizes single sign-on, SSO: one login for every connected application. It uses OAuth2, OIDC, SAML, and LDAP, the standard authentication and directory protocols most business applications already speak. The minimum stack is three containers (PostgreSQL, server, and worker), with no Redis since version 2025.10, and the current stable release is 2026.8.2. An afternoon is enough to get web login and forward auth (delegated authentication: a reverse proxy like Traefik checks with Authentik before letting each request through) working on a couple of services.
This guide is also available in Spanish: Cómo instalar Authentik para SSO auto-alojado.
Key takeaways
- Since version 2025.10, Authentik drops the Redis dependency entirely: the minimum stack is PostgreSQL + server + worker (three containers instead of four).
- Tested on 16 September 2026 with Authentik 2026.8.2, the tag the official
compose.ymlpins today. The initial setup now also asks for the instance’s base URL. - Forward auth with Traefik is the most common use case: protecting internal web services without modifying each application.
- Three classic first-install problems: domain/URL configuration, cookie handling across subdomains, and persistent volumes for PostgreSQL.
- Fits small and medium organizations well; for thousands of users or complex policies, Keycloak remains more powerful.
- Never skip major.minor versions in upgrades: since 2026.8, Authentik refuses to migrate if you try. The 2026.5.7 to 2026.8.2 step currently fails because of a known bug, so go through 2026.8.0 first.
What has changed since 2025.10
This guide was written on the 2025.10 branch, the first without Redis. The current branch is 2026.8: 2026.8.0 shipped on 18 August 2026 and 2026.8.1 on 1 September. 2026.8.2 shipped on 9 September 2026 and is the tag the official compose.yml pins today.
According to the 2026.8 release notes[1], 2026.8.2 includes five security patches. The project’s SECURITY.md[2] only gives security support to the 2026.5.x and 2026.8.x branches, so an install still on 2025.10 no longer gets patches. We checked this on 16 September 2026 against the repository releases[3], and on this machine (linux/arm64) we repeated the install from scratch and the full upgrade from 2025.10.4.
Each intermediate branch brings something you have to do yourself when upgrading:
| Branch | What changes for you |
|---|---|
| 2025.12 | Group names must be unique before upgrading, because the migration fails if there are duplicates. Files move from the /media mount to /data/media and are served under /files instead of /media. |
| 2026.2 | The Compose file is now called compose.yml. SCIM providers with a group filter are deactivated until you review their configuration. |
| 2026.5 | The default listen address changes from 0.0.0.0 to [::], which can break IPv4-only environments. AUTHENTIK_POSTGRESQL__CONN_OPTIONS is deprecated. |
| 2026.8 | The X-Forwarded-* headers are only accepted from trusted proxies, and hash_password no longer accepts the password as an argument. A new base URL setting appears, required from 2026.11. |
New in 2026.8 are switching between accounts signed in at the same time, typed object attributes and policy bindings with an expiry date. On OAuth 2.0 and OpenID Connect, the project is now OpenID Certified and adds token exchange and dynamic client registration. Privileged access management, agent accounts, scheduled user offboarding and self-hosted event maps are Enterprise features. Under the hood, the server and the proxy outpost move from Go to Rust, with no improvements yet according to the notes.
How to upgrade from 2025.10 to 2026.8 without skipping branches
Going from 2025.10 to 2026.8 crosses four branches, and the official upgrade guide[4] asks you to reach the latest patch of each one before moving to the next. Today that means 2025.10.4, 2025.12.6, 2026.2.7, 2026.5.7 and 2026.8.2. Before you start, back up PostgreSQL: Authentik does not support downgrading.
Since 2026.8, Authentik itself blocks skips. When we started 2026.8.2 on a 2025.10.4 database, the server stopped before migrating anything. The error was Major version skips are not allowed, with a link to the upgrade guide. The database was left untouched.
The move to 2025.12 needs the most manual work, because files change location. The release notes for that version handle it like this:
docker compose down
mkdir -p ./data
mv ./media ./data/media
wget -O docker-compose.yml \
https://goauthentik.io/version/2025.12/docker-compose.yml
docker compose up -d
From 2026.2 on, the download changes path and name. Copy your changes from the old docker-compose.yml into the new compose.yml first, then delete the old file: if both exist, Compose uses compose.yml and warns with Found multiple config files with supported names. For 2026.5, download the file the same way with 2026.5 in the URL and run docker compose up -d again.
wget -O compose.yml \
https://goauthentik.io/version/2026.2/lifecycle/container/compose.yml
rm docker-compose.yml
docker compose up -d
The last step has a trap today. Release 2026.5.7 (9 September 2026) applies an index migration, authentik_core.0064, which on the 2026.8 branch depends on another migration that 2026.5 does not have. When we upgraded from 2026.5.7 to 2026.8.2, the server went into a restart loop with this error (wrapped over four lines to fit):
django.db.migrations.exceptions.InconsistentMigrationHistory:
Migration authentik_core.0064_user_authentik_c_usernam_2f0e4b_idx
is applied before its dependency authentik_core.0063_actor
on database 'default'.
The bug is confirmed in issue 25996[5], opened on 10 September 2026 and still unfixed on 16 September. The check fails before migrating, so the database does not change. The developer who introduced the bug points, in that issue, to the simpler workaround: go through 2026.8.0, which does not include that migration yet, and then upgrade to 2026.8.2.
The official compose.yml reads the tag from AUTHENTIK_TAG, so set it in .env, start the stack, wait until server and worker are healthy, and remove it:
echo 'AUTHENTIK_TAG=2026.8.0' >> .env
docker compose up -d
sed -i '/^AUTHENTIK_TAG=/d' .env
docker compose up -d
In our test, the 2026.8.0 step applied the pending migrations and left the containers healthy in 48 s. The final upgrade to 2026.8.2 took 29 s, with no restarts, at a load average between 4 and 7 on a shared 18-core machine.
Why Authentik and not something else
The choice between Authentik, Keycloak, Zitadel, and commercial solutions like Auth0 depends on context, but for small-to-medium self-hosting the choice has clear nuances:
- Keycloak[6]: powerful but heavy. Admin interface has a learning curve that slows teams without dedicated time.
- Zitadel: elegant but younger and with a smaller ecosystem.
- Auth0: excellent but stopped being a realistic option for anyone wanting to self-host.
Authentik occupies the middle ground: complete enough for real cases, with good SAML and OAuth2 support, with useful forward auth to protect services behind Traefik or Nginx, and with manageable resource consumption. The Docker Compose install guide[7] asks for at least 2 CPU cores and 2 GB of RAM. In our test, at idle, the server used about 400 MiB, the worker about 265 MiB and PostgreSQL about 56 MiB.
The architecture since 2025.10
An important change to understand before installing: in version 2025.10, Authentik removed Redis entirely, not made it optional. For years the minimum architecture was PostgreSQL + Redis + server + worker.
The process started in version 2024.6, replacing Redis locks with PostgreSQL advisory locks. The task queue moved to PostgreSQL in 2025.8, and in 2025.10 caching, the embedded outpost’s sessions and WebSocket connections followed. The latter use NOTIFY/LISTEN instead of Redis pub/sub. The Authentik team documents the change on their engineering blog[8].
This simplification has practical effects, with nuance:
- The stack drops from four containers to three, reducing configuration and failure surface.
- Anyone installing for the first time no longer has to explain why a Redis is hanging off the identity service.
- In exchange, Authentik now uses roughly 50% more PostgreSQL connections than before, since it absorbs the work Redis used to do. On instances with tight connection limits, check
max_connectionsin PostgreSQL before upgrading. Our test instance had 12 open connections at idle, against the limit of 100 that the PostgreSQL image ships with. - If PostgreSQL requires TLS, Authentik since 2025.10 requires TLS 1.3 or the Extended Master Secret extension to connect.
The basic deployment
The deployment starts from the official compose.yml, which has had that name since 2026.2 (it used to be docker-compose.yml). Download it and generate the PostgreSQL password and the secret key into .env with the commands from the official guide:
mkdir authentik && cd authentik
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
The file defines three services:
- A
postgresqlservice with thepostgres:16-alpineimage (PostgreSQL 16.15 in our test) and apg_isreadyhealthcheck. - A
serverservice with theghcr.io/goauthentik/serverimage andservercommand. - A
workerservice with the same image, theworkercommand and the Docker socket mounted.
The server listens on ports 9000 (HTTP) and 9443 (HTTPS), which Traefik or Nginx use for forward auth. If they are already taken on the host, change the published ports with COMPOSE_PORT_HTTP and COMPOSE_PORT_HTTPS in .env. In our test we used 22900 and 22943.
The worker mounts /var/run/docker.sock to manage Docker outposts, which gives it control over the host’s Docker; the documentation suggests a socket proxy or removing the mount. We removed it in our test, on a shared machine, and the embedded outpost worked without it.
Minimum environment variables:
AUTHENTIK_SECRET_KEY: a long random string used to sign tokens.AUTHENTIK_POSTGRESQL__HOST: pointing to the database service.AUTHENTIK_POSTGRESQL__USER,AUTHENTIK_POSTGRESQL__NAME,AUTHENTIK_POSTGRESQL__PASSWORD.
In serious deployments, move the password and the secret out of direct definition into a secrets manager or a git-ignored .env file (see our guide on environment variables and secrets in Docker Compose).
The server service from the official file looks like this, with the tag pinned and resource limits added. The env_file entry is what passes any variable you add to .env into the container:
services:
server:
image: ghcr.io/goauthentik/server:2026.8.2
command: server
env_file:
- .env
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
ports:
- "${COMPOSE_PORT_HTTP:-9000}:9000"
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
shm_size: 512mb
volumes:
- ./data:/data
- ./custom-templates:/templates
depends_on:
postgresql:
condition: service_healthy
deploy:
resources:
limits: {memory: 768M, cpus: "1.5"}
The tag pinned above is 2026.8.2 (9 September 2026), the same one the official compose.yml for the 2026.8 branch uses; when this guide was written it was 2025.10. Since 2025.12, uploaded files live in ./data, mounted at /data. If Authentik sits behind Traefik or Nginx, also check AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS (details in the section on forward auth after an upgrade).
A useful detail: set memory and CPU limits on the containers from the start. The server can spike during mass logins or LDAP syncs, and without a declared limit can affect other services on the same host. With 768 MiB and 1.5 cores, a fresh install completed its migrations without the kernel killing the process, and the server used 460 MiB after a forward auth login.
With .env ready, pull the images and start the stack:
docker compose pull
docker compose up -d
First start and migrations
When the server boots for the first time, it runs the database migrations. In our test, 69 s passed from docker compose up -d to both server and worker being healthy, at a load average between 35 and 43 on a shared 18-core machine. You no longer need to start the services one by one: Compose waits for the PostgreSQL healthcheck before starting server and worker.
Server and worker both try to migrate, but the startup script first takes a PostgreSQL advisory lock (pg_advisory_lock), so only one of them migrates. This excerpt of docker compose logs --no-log-prefix server shows the start and the end of the migrations:
2026-09-16 21:13:12 [info ] waiting to acquire database lock
2026-09-16 21:13:12 [info ] applying django migrations
System check identified no issues (4 silenced).
Meanwhile, the worker logs waiting to acquire database lock. When the server releases the lock, the worker takes it, finds nothing pending and logs No migrations to apply. Upgrades follow the same process. Even so, you cannot skip major.minor branches: go one by one (from 2026.5 to 2026.8, not straight from 2025.10 to 2026.8).
After the migrations, open http://your_server:9000/, with whatever port you published. In 2026.8 the root redirects to /setup and from there to /if/flow/initial-setup/. If you open that path without going through the root, the flow denies access with the message Access the authentik setup by navigating to followed by the root URL.
The setup asks for the email and password of the akadmin user and, new in 2026.8, the instance’s base URL (for example https://authentik.example.com). Use a real email and save the password in a manager.
Right when the containers turned healthy, /if/flow/initial-setup/ returned 404: the worker needed about 12 s more to create the default flows. If the flow does not appear after restarting the containers, the login troubleshooting guide[9] suggests docker compose exec server ak changepassword akadmin.
Forward auth with Traefik
The most common use case when adopting Authentik is protecting internal web services behind a reverse proxy (the component that receives every HTTP request and routes it to the right container). The pattern is called forward auth: Traefik receives the request, forwards it to Authentik to verify a valid session, and if none exists sends the user to login. If you already run Traefik, our guide on installing Traefik with Docker Compose covers the reverse proxy from scratch.
The configuration on the Authentik side follows these steps:
- Create a proxy provider with forward auth single application or forward auth domain.
- Create an application linked to it.
- Assign the application to a proxy outpost. The embedded outpost, which runs inside the server, is enough for small deployments and needs no extra container.
On the Traefik side, a middleware of type forwardAuth[10] points to the outpost, and that middleware applies to any router that should be protected. This is an excerpt of the dynamic file we tested with Traefik 3.7.13 and its file provider, with Traefik on the same Compose network as Authentik:
http:
middlewares:
authentik:
forwardAuth:
address: http://server:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeadersRegex: "(?i)^x-authentik-"
routers:
app:
rule: Host(`app.localhost`)
middlewares: [authentik]
service: whoami
app-outpost:
rule: Host(`app.localhost`) && PathPrefix(`/outpost.goauthentik.io/`)
priority: 1000
service: authentik
The app-outpost router sends the /outpost.goauthentik.io/ paths on the application’s domain to the authentik service, which points to http://server:9000/outpost.goauthentik.io. The whoami service is the protected application. A request without a session got a 302 to Authentik’s /application/o/authorize/; after login, the application received X-Authentik-Username, X-Authentik-Groups and X-Authentik-Email.
A pattern I use is having two predefined middleware chains:
chain-base: for public services without authentication.chain-oauth: for protected services passing through Authentik.
This clean separation makes adding or removing protection from a service a matter of changing a label, not rebuilding configuration.
Typical friction points
Three problems almost everyone runs into the first time:
- Domain and URL configuration. Authentik needs to know from which domain it is accessed to correctly generate redirects. Since 2026.8 that starts with the base URL (in the initial setup, under System > Settings or with
AUTHENTIK_WEB__BASE_URL), which becomes required from 2026.11. The outpost also has its own Authentik host:AUTHENTIK_HOSTif you deploy it separately, orauthentik_hostin the embedded outpost’s configuration. In our test, with that field empty, the forward auth redirect pointed tohttp://localhostwithout the published port; once we set it tohttp://localhost:22900, the login completed. - Cookie handling across subdomains. For an Authentik session to protect services on different subdomains, cookies need to be configured with the parent domain. The solution is the forward auth domain mode with the cookie domain pointing to the root domain (for example
example.comforapp1.example.comandapp2.example.com, as in the documentation). - Data storage. The PostgreSQL database is critical and must live on a persistent volume (see our guide on Docker volumes and bind mounts) with backups configured, for example with Restic. More than one team has lost entire Authentik configurations by having the volume on tmpfs or by deleting the volume when running
compose down -vby mistake. Include the./datadirectory too, where uploaded files live.
Forward auth that stops working after an upgrade: headers and trusted proxies
If forward auth worked and stops right after you upgrade Authentik, you most likely did not break your configuration: the headers Authentik accepts have changed. There are two changes, and they affect Traefik and Nginx differently.
The first arrived with 2026.8: the server only uses X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-For when the connection comes from a trusted network. According to the reverse proxy documentation[11], the default list covers 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fe80::/10 and ::1/128, and AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS replaces it. Our Traefik connected from the Compose network 172.31.0.0/16, inside that list, and worked with no changes.
With AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS=127.0.0.0/8, which leaves that network out, Authentik logged the checks as addressed to server:9000 and every protected request returned 404 Not Found. Adding 172.31.0.0/16 to the list made forward auth redirect to the login again.
The second change only affects Nginx forward auth mode and came with Authentik 2025.12.5 and 2026.2.3, which fixed advisory GHSA-5wcc-hf24-rf5h[12] (12 May 2026). The outpost read the X-Original-URI header, which Nginx does not set and which a client could inject to bypass authentication. Since those versions it reads X-Original-URL, which the Nginx configuration in the documentation sends. Traefik, Caddy and proxy mode are outside the advisory; with Traefik, the outpost builds the URL from X-Forwarded-Uri.
| If your Authentik is… | What you need |
|---|---|
| 2025.12.4, 2026.2.2 or older | Upgrade: with Nginx in forward auth mode, a client can bypass authentication (GHSA-5wcc-hf24-rf5h) |
| 2025.12.5, 2026.2.3 or newer, behind Nginx | Nginx must send X-Original-URL to the outpost, as in the documentation’s configuration |
| 2026.5 | Check the default listen address: it changed from 0.0.0.0 to [::], which can break IPv4-only environments |
| 2026.8 (current branch; 2026.8.2 from 9 September 2026) | The proxy must connect from a network listed in AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS; otherwise Authentik ignores the X-Forwarded-* headers |
Two more checks that prevent surprises
-
Strip incoming
X-Authentik-*headers. If a client can send them and your proxy lets them through to the application, the client can impersonate a user. In Traefik,authResponseHeadersonly replaces the headers on its list: in our test, a made-upX-Authentik-Role-Overrideheader reached the application unchanged. WithauthResponseHeadersRegex: "(?i)^x-authentik-", Traefik first removes every matching header, the forged header disappeared and Authentik’s own headers still arrived; without the(?i), the application received none of them. -
AUTHENTIK_POSTGRESQL__CONN_OPTIONSis deprecated since the 2026.5 branch, and in 2026.8 it is still marked to be removed “in an upcoming version”. If it is in your.env, drop it now rather than in the middle of an urgent upgrade.
A note on Redis, because old guides cause confusion: since 2025.10 it is not needed. Caching, the embedded outpost and WebSockets moved to PostgreSQL, and the Redis settings can be deleted from your configuration.
Frequently asked questions
Do I need to install Redis to use Authentik?
No, not since version 2025.10. Authentik moved the task queue to PostgreSQL in 2025.8, and caching, the embedded outpost’s sessions and WebSocket notifications in 2025.10. The minimum stack is three containers: PostgreSQL, server, and worker. In versions before 2025.10, Redis was mandatory.
Is Authentik better than Keycloak?
It depends on scale. For small or medium teams, Authentik installs faster and stays maintainable thanks to a simpler interface. For large organizations with thousands of users, complex federation, or strict compliance requirements, Keycloak offers more policy depth and a more mature community.
Can I upgrade straight from 2025.10 to 2026.8?
No. Release 2026.8 only migrates databases that come from 2026.5 or from 2026.8 itself, and in any other case it stops with the error Major version skips are not allowed before touching anything. Go through 2025.12.6, 2026.2.7 and 2026.5.7, then to 2026.8.0 before 2026.8.2. The direct step from 2026.5.7 fails while issue 25996 remains open.
Conclusion
Authentik offers the best ratio of install effort to capabilities obtained in the self-hosted identity space. An afternoon is enough to have a working instance with web login and forward auth on a couple of services. If you are evaluating self-hosted identity and do not have extreme scale or specific compliance requirements, Authentik is the first candidate to try.
Where to pause and think: whether your organization has external users, partners, or customers who will also authenticate. Availability and federated identity requirements change a lot when SSO stops being internal. The good news is that this review can be done with Authentik already running on internal tasks, without needing to pick the final tool from day one.
Sources: official Authentik documentation[13], Docker Compose installation[7], Authentik upgrade guide[4], 2026.8 release notes[1], Authentik reverse proxy documentation[11], Authentik repository on GitHub[14], issue 25996 on the 2026.5.7 to 2026.8.2 upgrade[5], security advisory GHSA-5wcc-hf24-rf5h[12], Authentik’s engineering blog on removing Redis[8], Traefik documentation on the ForwardAuth middleware[10].
Sources
- 2026.8 release notes
- SECURITY.md
- repository releases
- official upgrade guide
- issue 25996
- Keycloak
- Docker Compose install guide
- their engineering blog
- login troubleshooting guide
- middleware of type forwardAuth
- reverse proxy documentation
- GHSA-5wcc-hf24-rf5h
- official Authentik documentation
- Authentik repository on GitHub