SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor

How to Run Authentik SSO with Docker Compose

Run Authentik with Docker Compose for one login across your apps. See the key env values, akadmin bootstrap, and Traefik forward auth setup.

One login for every app wey you host

Authentik na self-hosted SSO (single sign-on) server: your users go sign in once, and every app behind am go accept that session instead of asking for im own password. The install na official Docker Compose file plus two generated secrets. The part wey need real thinking come after that: point reverse proxy to am, then put one existing app behind forward auth.

Authentik dey ship as three services for that Compose file: one PostgreSQL database, one server process, and one worker process. The server container still dey run the embedded outpost, wey be the component wey dey answer “is this request signed in?” for every protected app. Version 2026.8.5 na the current release as of 25 October 2026. Dem publish am on 21 October 2026 as the latest patch for the 2026.8 series, wey start with 2026.8.0 on 18 August, and e replace 2026.8.4 from 14 October as the default image tag for the official Compose file. The project ask for host wey get at least 2 CPU cores and 2 GB of RAM. Treat that one as the minimum. PostgreSQL and the worker both dey hold memory once the box don dey up for one day.

Wetin you need before you start

You need Docker Engine with the Compose v2 plugin. You fit confirm say e dey available with docker compose version. If e print error instead of version, install the plugin before you continue. The basic steps dey for running apps with Docker Compose on a VPS. You also need DNS A record wey point to the server. For the examples below, na auth.example.com. Authentik dey build redirect URLs from the hostname wey browser use.

Run the stack as ordinary user wey dey inside docker group, instead of root. Membership of that group get the same power as root for the host. So give am to one deploy account only, and nobody else, like the approach for least-privilege user accounts on a VPS.

Install with the official Compose file

sudo install -d -o "$USER" -g "$USER" /opt/authentik
cd /opt/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
docker compose pull
docker compose up -d

docker compose ps suppose list three containers, all of dem up, with postgresql reporting healthy after e pass the check. The first start dey run database migrations, so give am one minute before the web interface go answer.

Both generated values matter, but for different reasons. PG_PASS na the PostgreSQL password. Authentik install docs talk say dem no support passwords wey pass 99 characters, so keep any password wey you choose yourself under that length; the generated one short well well. AUTHENTIK_SECRET_KEY na the key Authentik uses to sign cookies, and the configuration docs talk say changing am invalidates active sessions. So if you create new key later, e go log every signed-in user out. Keep .env with mode 600, and keep one copy for safe place with your backups, so restore go come back with the same key wey the server dey use.

The Compose file marks both values as required with the ${VAR:?message} form: ${PG_PASS:?database password required} for the PostgreSQL service and ${AUTHENTIK_SECRET_KEY:?secret key required} for the server and worker. That means Compose no go start if the file no dey there. If you run docker compose up -d from wrong directory, e go print line like required variable AUTHENTIK_SECRET_KEY is missing a value: secret key required and stop. That message na path problem, no be config problem.

Environment values wey matter

Everything else go enter the same .env file. Authentik dey map double underscore to nested configuration key, so AUTHENTIK_EMAIL__HOST dey set email.host. Single underscore no dey map to the nested key. Na this be the commonest reason why setting go look like say e no do anything.

  • AUTHENTIK_BOOTSTRAP_PASSWORD dey set password for the built-in akadmin user when e start for the first time, so you no go ever type am for public web form. AUTHENTIK_BOOTSTRAP_EMAIL and AUTHENTIK_BOOTSTRAP_TOKEN dey set that user address and API token the same way.
  • COMPOSE_PORT_HTTP and COMPOSE_PORT_HTTPS dey move the published ports comot from the default ports 9000 and 9443.
  • AUTHENTIK_EMAIL__HOST, AUTHENTIK_EMAIL__PORT, AUTHENTIK_EMAIL__USERNAME, AUTHENTIK_EMAIL__PASSWORD, AUTHENTIK_EMAIL__USE_TLS and AUTHENTIK_EMAIL__FROM dey configure outbound mail. Without dem, Authentik go try localhost for port 25, so password-reset mails go end as connection error for worker log.
  • AUTHENTIK_LOG_LEVEL=debug dey turn on the details wey you need when login flow no dey behave well. Set am back to info afterwards.
  • AUTHENTIK_ERROR_REPORTING__ENABLED na false by default. Set am to true only if you dey okay with sending crash reports upstream.

These ones na secrets inside plain file, so treat the directory the same way you dey treat any other credential store. Password manager like self-hosted Vaultwarden instance better pass note for your laptop as the place to keep recovery copy.

First login and the admin account

Open http://SERVER_IP:9000 for browser. Authentik go show the initial setup flow and ask you to set password for the default akadmin user. If you don already set AUTHENTIK_BOOTSTRAP_PASSWORD, that step don complete and you go go straight to the login page.

Create normal admin user for yourself under Directory and then Users. Add am to the authentik Admins group, then sign in with that account. Leave akadmin as break-glass account with long password wey you store offline. If broken flow or stage ever lock you out, docker compose run --rm server create_recovery_key 10 akadmin, when you run am from /opt/authentik, go print link wey signs you in directly as akadmin. Anybody wey hold that link get the same access, so treat am like password. Everyday work with shared built-in account go spoil the audit log, because every event go say akadmin and nothing go show who perform am. This matter still continue beyond Authentik: something like self-hosted OneCLI harness wey give each person their own agent only leaves readable trail when the identity wey reaches am belong to one human, instead of login wey the whole team dey share.

Put Authentik behind your reverse proxy

Publishing port 9000 to internet dey work, but you want TLS (transport layer security) and a real hostname. If you already dey use the setup from Traefik as reverse proxy for multiple Compose apps, join Authentik to the same external proxy network with an override file. Create docker-compose.override.yml next to compose.yml:

services:
  server:
    networks:
      - default
      - proxy
    labels:
      traefik.enable: "true"
      traefik.docker.network: proxy
      traefik.http.routers.authentik.rule: Host(`auth.example.com`)
      traefik.http.routers.authentik.entrypoints: websecure
      traefik.http.routers.authentik.tls.certresolver: le
      traefik.http.services.authentik.loadbalancer.server.port: "9000"

networks:
  proxy:
    external: true

Apply am with docker compose up -d. Compose go merge the override automatically, so the server service go keep everything from the official file and gain the labels. Check am with curl -I https://auth.example.com/if/user/, wey suppose answer HTTP/2 200. A 404 page not found from Traefik mean say no router match the request. Check say the labels reach the container, say the Host() rule match the hostname exactly, and say the request enter through the HTTPS entry point wey the router dey listen on.

Once the hostname work, bind the published ports to 127.0.0.1 for the override, so na only through the proxy person fit enter.

Protect one app with forward auth

Authentik proxy provider get three modes, and if you choose wrong one, e fit waste one hour. Proxy mean say the outpost itself dey forward traffic go the upstream app. Forward auth (single application) mean say your own reverse proxy still dey move the traffic, and e only ask Authentik whether the request don sign in. Forward auth (domain level) dey protect every app under one parent domain with one provider, but you lose per-application authorization rules. If Traefik dey front, you need forward auth (single application). If you need one real app to practise with, something like self-hosted AFFiNE workspace na good first candidate, because na the kind internal tool wey you want make your own devices reach, and nobody else. Team tool make the reason stronger: put self-hosted Chatwoot support desk behind the same provider, so everybody wey dey answer the inbox sign in once for the day instead of sharing another password.

For the web interface, open Applications and then Providers, create a Proxy Provider, choose the forward auth single application mode, and set the external host to https://app.example.com. Create an Application wey point to that provider. Then open Outposts, edit the authentik Embedded Outpost, and move the new application into its selected applications. The outpost only answers for applications wey you give am, so if you skip that last step, the correctly configured provider still no go return anything.

Define the middleware once, for the Authentik container, and reference am from every protected app:

      traefik.http.middlewares.authentik.forwardauth.address: http://server:9000/outpost.goauthentik.io/auth/traefik
      traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: "true"
      traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version

authResponseHeaders na the list of headers wey Traefik copies from Authentik answer enter the request wey e sends upstream. If you omit am, the app still dey protected, but e no go know who the user be. So anything wey reads X-authentik-username for automatic login go remain logged out. This gap dey clear pass for front of app wey get its own sign-in, like self-hosted openGym workout tracker and its passkey login. The headers na the difference between one prompt and two for the same page.

The protected app itself need two routers, no be one:

    labels:
      traefik.enable: "true"
      traefik.http.routers.myapp.rule: Host(`app.example.com`)
      traefik.http.routers.myapp.entrypoints: websecure
      traefik.http.routers.myapp.tls.certresolver: le
      traefik.http.routers.myapp.middlewares: authentik@docker
      traefik.http.routers.myapp-auth.rule: Host(`app.example.com`) && PathPrefix(`/outpost.goauthentik.io/`)
      traefik.http.routers.myapp-auth.entrypoints: websecure
      traefik.http.routers.myapp-auth.tls.certresolver: le
      traefik.http.routers.myapp-auth.priority: "15"
      traefik.http.routers.myapp-auth.service: authentik

The second router na the part wey everybody dey leave out. After sign-in, Authentik sends the browser back to a path under /outpost.goauthentik.io/ for the app's hostname, no be for auth.example.com. If router no send that path prefix go the Authentik service, the request go reach your app, the app go answer 404, and the login no go finish. The higher priority na wetin make the specific path rule win over the plain Host() rule for the same domain.

Test am for a private browser window. You suppose dey sent to auth.example.com, sign in, then come back to the app. docker compose logs -f server for the Authentik side prints one authorization event for each attempt. This one go tell you whether the request reach Authentik at all.

Wetin you go actually encounter

Redirect loop wey no dey end between the app and the login page. The external host for the provider no match wetin browser dey use, usually http:// for the provider against https:// for the address bar. The session cookie then set for another origin, so every return go look like fresh anonymous request. Fix the external host and clear cookies for both domains before you test again.

404 for /outpost.goauthentik.io/start. The outpost router no dey, or its priority lower than the catch-all router for that host.

The app loads without ever asking for login. The middlewares label names a middleware wey no dey exist. Traefik no dey warn about this, so typo for authentik@docker simply mean say no middleware go run. Open the Traefik dashboard and confirm say the router list the middleware.

403 from Authentik after successful login. The user don authenticate but e no get authorization: the application get policy binding, or group requirement, wey this user no satisfy. The Events log for the admin interface go show the policy wey deny am.

When Keycloak better fit

Keycloak na the older project, and Red Hat dey back am. E fit pass for classic enterprise identity work: plenty SAML federation, brokering login from different external identity providers at the same time, and realm export and import as documented migration path. Commercial support behind am fit matter to some organisations for paper. The trade-off be say Keycloak no get proxy of im own. So, to protect app wey no speak OIDC (OpenID Connect) or SAML, you go need run something like oauth2-proxy beside am. Authentik built-in proxy provider already do this work and e dey integrated. Na why most self-hosters wey get different kinds of apps dey choose am.

Backups and upgrades

Three things make restore possible: the PostgreSQL database, the ./data directory, and .env.

cd /opt/authentik
docker compose exec -T postgresql pg_dump -U authentik authentik | gzip > authentik-$(date +%F).sql.gz

Store that dump and .env together. The dump no get copy of AUTHENTIK_SECRET_KEY, and restoring with different key invalidates every active session. So keeping the original key means the restored server go continue as e was before.

Upgrades na tag change. Set AUTHENTIK_TAG inside .env to the release wey you want, then run docker compose pull followed by docker compose up -d. If you never set AUTHENTIK_TAG, the version go come from the default wey dey written inside compose.yml itself. That means downloading newer copy of that file na the upgrade, and na the method the official upgrade docs describe. Read the release notes first. Authentik dey use date-based versions, and as of October 2026 its docs talk say upgrades must follow the sequence of major releases: move to the latest patch for the series wey you dey run, then move through every later series in order without skipping any. Authentik no support downgrading, so take database dump before the pull, no be after.

FAQ

Authentik free to self-host?

The open source edition free, and e cover everything we talk about above: the proxy provider, forward auth, OIDC (OpenID Connect), SAML, and the flows engine. Paid enterprise tier add support and some enterprise features, but you no need licence for anything here.

I need Traefik before I fit use Authentik?

No. Forward auth dey work with nginx through auth_request and with Caddy through forward_auth. The pattern na the same for every case: reverse proxy go ask Authentik about each request, and path prefix /outpost.goauthentik.io/ for the protected hostname must route go Authentik instead of the app.

Why my protected app dey move between login and error forever?

The external host wey you configure for the proxy provider no match the URL wey browser dey use, most often http against https. Session cookie dey issue for one origin and browser dey read am for another one, so Authentik dey see anonymous request every time. Correct the external host, then clear cookies for both hostnames before you test again.

How much RAM Authentik need?

The documented minimum na 2 CPU cores and 2 GB of RAM as of October 2026. This cover PostgreSQL, the server, and the worker together. For 2 GB machine, memory pressure fit wake kernel's OOM killer. E go end the process wey get the highest badness score: roughly the share of allowed memory wey e dey use, plus im oom_score_adj. If na the worker dem end, the symptom na background tasks and outbound email go stop, while login page still dey work. Give am 4 GB if the same server dey also run the apps wey you dey protect.

Dis article help you?