The stack at a glance
Authentik is a Django app plus a PostgreSQL database plus Redis plus one or more "outposts" — small Go binaries that handle the actual proxy/LDAP/RADIUS endpoints. The recommended deployment is a docker compose stack of: server (web UI + API), worker (background tasks), postgresql, redis. Outposts can be embedded (in the main server container) or deployed separately.
docker compose setup
Create a directory for the stack, drop in a docker-compose.yml, an .env with secrets, and the official media and certs volume mounts:
# docker-compose.yml
version: "3.9"
services:
postgresql:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "pg_isready", "-U", "${PG_USER:-authentik}"]
interval: 30s
timeout: 5s
retries: 5
volumes:
- database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS}
POSTGRES_USER: ${PG_USER:-authentik}
POSTGRES_DB: ${PG_DB:-authentik}
redis:
image: docker.io/library/redis:alpine
command: --save 60 1 --loglevel warning
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 30s
timeout: 3s
retries: 5
volumes:
- redis:/data
server:
image: ghcr.io/goauthentik/server:2026.4
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
volumes:
- ./media:/media
- ./custom-templates:/templates
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgresql
- redis
worker:
image: ghcr.io/goauthentik/server:2026.4
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./media:/media
- ./certs:/certs
- ./custom-templates:/templates
depends_on:
- postgresql
- redis
volumes:
database:
redis:
# .env
PG_PASS=$(openssl rand -base64 36 | tr -d '\n')
AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')
Generate those two secrets ahead of time (they'd be templated into the file by hand). Then:
docker compose up -d
docker compose logs -f server
The server is now listening on http://localhost:9000 (HTTP) and https://localhost:9443 (HTTPS with a self-signed cert). The first run also boots a bootstrap flow at /if/flow/initial-setup/ — visit that path to create the admin user.
Put Authentik behind your reverse proxy (Caddy, nginx, Traefik) for TLS termination with a real cert. The Authentik server expects the proxy to set X-Forwarded-For and X-Forwarded-Proto; both nginx recommendedProxySettings and Caddy's reverse_proxy do this by default.
Initial admin setup
Browse to /if/flow/initial-setup/. Create the akadmin user, set its password, and you're logged into the admin UI at /if/admin/. Two things to do first:
- Set the public URL. System → Configuration. The default is
http://localhost:9000; change it to the URL the proxy serves Authentik on (e.g.https://auth.example.com). This is what OIDC discovery URLs and email links will reference. - Email. System → Configuration → SMTP server. Without email, password reset and email-based MFA enrollment don't work.
Concept: providers, applications, outposts
Authentik separates three things that other IdPs conflate:
- Provider — the protocol-level configuration. An OIDC provider has client-id / client-secret / redirect URIs / scopes. A proxy provider has external host, internal host, mode. A SAML provider has metadata, signing certs.
- Application — what users see in the Authentik app launcher; ties a slug and an icon to one provider, and applies policy bindings for who can access it.
- Outpost — for proxy/LDAP/RADIUS providers, the actual binary that does the protocol. By default, an "embedded" outpost runs inside the server container; for production, separate outposts can be deployed near the apps they protect.
Adding an OIDC app (example: Grafana)
In the admin UI:
- Applications → Providers → Create → OAuth2/OpenID Provider.
- Name:
grafana - Client type: Confidential
- Client ID, Client Secret: auto-generated — copy them, you'll need them in grafana.ini
- Redirect URIs:
https://grafana.example.com/login/generic_oauth - Signing Key: pick the default certificate
- Name:
- Applications → Applications → Create.
- Name: Grafana
- Slug:
grafana - Provider: the grafana provider just created
- Launch URL:
https://grafana.example.com
In Grafana's grafana.ini (or its env-var equivalents):
[auth.generic_oauth]
enabled = true
name = Authentik
client_id = <client-id>
client_secret = <client-secret>
scopes = openid email profile
auth_url = https://auth.example.com/application/o/authorize/
token_url = https://auth.example.com/application/o/token/
api_url = https://auth.example.com/application/o/userinfo/
role_attribute_path = contains(groups[*], 'grafana-admins') && 'Admin' || contains(groups[*], 'grafana-editors') && 'Editor' || 'Viewer'
Restart Grafana. The login page now shows a "Sign in with Authentik" button.
Forward-auth for apps without OIDC
Some self-hosted apps don't speak OIDC. The forward-auth pattern lets the reverse proxy ask Authentik "is this user allowed through?" before serving the request:
- Create a provider: Applications → Providers → Create → Proxy Provider.
- Mode: Forward auth (single application) — for one external host — or Forward auth (domain level) for a wildcard.
- External host:
https://app.example.com
- Create an application bound to it.
- Outposts → Edit the embedded outpost → assign the new provider to it. Within a few seconds, the outpost's
/outpost.goauthentik.io/endpoints are live on the Authentik server.
On the Caddy side:
app.example.com {
forward_auth auth.example.com {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name
trusted_proxies private_ranges
}
reverse_proxy 127.0.0.1:8080
}
auth.example.com {
reverse_proxy 127.0.0.1:9000
}
Now any request to app.example.com is bounced through Authentik first; on success, the proxy adds X-Authentik-* headers the upstream can trust (because the proxy is the one setting them).
Backups
Two things matter:
- PostgreSQL — all users, providers, applications, policies, flows.
docker exec postgresql pg_dump -U authentik authentik | gzip > backup-$(date +%F).sql.gzon a nightly timer. - Media volume — uploaded icons, custom templates, branding. Snapshot it alongside the database dump.
The AUTHENTIK_SECRET_KEY from .env is required to decrypt fields encrypted at rest, so back it up out of band with the rest of the secrets.
What to read after this
The official docs are thorough; the most useful pages once the basics work are:
- Flows & Stages — how login/enrollment/password-reset are composed from reusable stages, and how to add MFA enrollment or geo-restrictions.
- Policies — group-based access control with Python expressions as the escape hatch.
- Property mappings — transform attributes between Authentik and the downstream app (e.g. derive Grafana roles from group membership).