authentik is an open-source Identity Provider (IdP) and Single Sign-On (SSO) platform built for modern, protocol-diverse environments. It speaks SAML, OAuth2 / OIDC, LDAP, RADIUS, SCIM, WS-Federation, Microsoft Entra, Google Workspace, and SSF (Shared Signals Framework) — out of one server. You self-host it, configure it once, then point every app you want to secure at it.
This guide covers everything you need to install, configure, and operate authentik day-to-day. It is written against version 2026.8.0-rc1 (commit 9e75f62). Where the upstream documentation at docs.goauthentik.io goes deeper on a particular integration, this guide links there.
Important
authentik has a dual license model. The core (MIT, in
authentik/) is forever-free. The Enterprise tier (authentik/enterprise/under a custom EE License) adds features like SSF, MS Entra/Google Workspace sync, audit logging, MTLS stage, RAC, account lockdown, and unique-password policy. The two are in the same container — your license key activates Enterprise modules at runtime.
Table of Contents
- What authentik Does
- Concepts You Must Know
- Quick Start (Docker Compose)
- Installation
- Initial Setup
- Adding Your First Application
- Flows and Stages
- Providers — Securing Apps
- Sources — Authenticating Users
- Outposts
- Users, Groups, and RBAC
- Policies and Property Mappings
- Blueprints — GitOps for authentik
- Customization and Branding
- Events and Notifications
- Backups, Upgrades, and Air-Gapped Installs
- Common Tasks (Recipes)
- CLI Reference (
ak) - Troubleshooting
- FAQ
- Reference Tables
1. What authentik Does
authentik sits between your users and the apps you want to secure. A user opens an app. The app hands them off to authentik. authentik proves who they are using whatever combination of password / MFA / federated login / certificate / device check you've configured. authentik hands them back to the app with a signed token (SAML assertion, OIDC ID token, etc.) that the app trusts.
Capabilities
- Identity Provider — authentik is the source of truth for who your users are. Local users, federated users, or both.
- Single Sign-On — sign in once at authentik, access dozens of apps without re-entering credentials.
- Multi-protocol — SAML, OAuth2/OIDC, LDAP, RADIUS, SCIM, WS-Federation, Forward Auth (Proxy provider), Remote Access Connection (RAC, an in-browser RDP/SSH/VNC gateway).
- Inbound federation — accept logins from external LDAP, OAuth, SAML, Kerberos, SCIM, Plex, Telegram.
- Outbound sync — push users and groups to MS Entra ID, Google Workspace, or any SCIM endpoint.
- Flexible MFA — TOTP, WebAuthn (passkeys, security keys), Duo, SMS, email, static codes, endpoint device attestation (GDTC).
- Self-service — users can recover passwords, manage MFA devices, view assigned apps, edit profile fields — without admin involvement.
- Audit trail — every login, every config change, every consent grant is logged. Forwardable to Sentry, syslog, or your SIEM.
- GitOps-native — every object (Flow, Stage, Policy, Provider, Application, User, Group) can be expressed as a YAML blueprint and applied declaratively.
What authentik is NOT
- Not a password manager. Users still need to remember their authentik credentials (or federate from one they already do).
- Not a CASB or proxy gateway by itself — but the Proxy provider + an outpost gives you Forward Auth (à la Authelia / oauth2-proxy) for any app that supports header-based auth or Traefik/Nginx forward-auth.
- Not an MDM. authentik integrates with endpoint-attestation systems but doesn't manage devices.
2. Concepts You Must Know
These six concepts run through every page of the admin UI. Learn them first.
| Concept | What it is | Lives at |
|---|---|---|
| Flow | An ordered sequence of stages that completes one user-facing journey (login, recovery, enrollment, invalidation). | Admin → Flows and Stages → Flows |
| Stage | A single step inside a flow (identification, password, MFA, consent, user-write, etc.). 24 stage types ship in core + EE. | Admin → Flows and Stages → Stages |
| Policy | A boolean expression (Python or rule-based) that gates whether a stage executes, an app is visible, or a flow advances. | Admin → Customization → Policies |
| Provider | Outbound protocol implementation — SAML, OAuth2, LDAP, RADIUS, Proxy, RAC, SCIM, etc. The thing apps talk to. | Admin → Applications → Providers |
| Source | Inbound protocol implementation — federated login from external IdPs. | Admin → Directory → Federation & Social login |
| Application | A user-visible "tile" in the User interface. Wraps one Provider and (optionally) some policies. | Admin → Applications → Applications |
A simple login flow looks like this:
User → Application "Slack" → Provider (SAML) → authentik
↓
Flow "default-authentication-flow":
Stage 1: Identification (enter username)
Stage 2: Password
Stage 3: Authenticator Validation (MFA)
Stage 4: User Login (issue session)
↓
SAML assertion → Slack → user is signed in
Tip
If you only remember one thing, remember this: Flows are recipes. Stages are ingredients. Policies decide which ingredients to use today.
3. Quick Start (Docker Compose)
The fastest path to a working authentik. Requires 2 CPU cores, 2 GB RAM, and Docker Compose v2.
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
docker compose pull
docker compose up -d
Then open http://<your-server>:9000/if/flow/initial-setup/ and set the password for the akadmin user.
Expected output of docker compose up -d:
[+] Running 5/5
✔ Network authentik_default Created
✔ Container authentik-redis-1 Started (NOTE: only present in legacy compose files; current version uses PostgreSQL listen/notify, no Redis)
✔ Container authentik-postgresql Started
✔ Container authentik-server Started
✔ Container authentik-worker Started
Important
PostgreSQL passwords must be ≤ 99 characters due to a PostgreSQL libpq limitation. The
openssl rand -base64 36above produces a 48-char password, which is well within the limit. If you choose a custom password, stay under 100 chars.
Caution
All authentik containers must run in UTC. Do not mount
/etc/timezoneor/etc/localtime— it breaks OAuth and SAML clock-skew validation. Times in the UI are localized client-side. (See GitHub #3005.)
4. Installation
Choose your deployment
Docker Compose
Best for: lab, homelab, small production (< 500 users, single host).
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
The compose file pins a specific authentik version. To upgrade, re-download the file (it tracks the latest stable release) and run docker compose pull && docker compose up -d.
Kubernetes (Helm)
Best for: production at scale, HA, multi-replica.
helm repo add authentik https://charts.goauthentik.io
helm repo update
helm upgrade --install authentik authentik/authentik \
-n authentik --create-namespace \
--set authentik.secret_key=$(openssl rand -base64 60 | tr -d '\n') \
--set postgresql.auth.password=$(openssl rand -base64 36 | tr -d '\n') \
--set postgresql.enabled=true
The chart's full schema lives at github.com/goauthentik/helm. Override values.yaml for production: external PostgreSQL, replicaCount > 1 for server and worker, ingress with cert-manager, and geoip.enabled=true if you want GeoIP-based policies.
AWS CloudFormation
Best for: AWS shops that want a one-click deploy.
Templates are hosted at docs.goauthentik.io/docs/install-config/install/aws. The bundled CDK app lives at lifecycle/aws/app.py in the repo.
DigitalOcean Marketplace
Best for: trying authentik on a fresh droplet.
DigitalOcean Marketplace listing provisions a droplet with authentik pre-installed via Docker Compose.
System requirements
| Profile | CPU | RAM | Disk | Postgres |
|---|---|---|---|---|
| Lab / homelab | 2 cores | 2 GB | 10 GB | bundled |
| Small prod (≤ 500 users) | 2 cores | 4 GB | 20 GB | bundled or external |
| Medium prod (≤ 10k users) | 4 cores | 8 GB | 50 GB | external recommended |
| Large prod / HA | 8+ cores per replica × N | 16+ GB per replica | 100+ GB | external + replica |
Note
authentik does not require Redis in current versions. Pub/sub uses PostgreSQL
LISTEN/NOTIFYvia the bundleddjango-channels-postgresanddjango-postgres-cachepackages. This simplifies deployment but means PostgreSQL is your only stateful dependency.
Email (recommended)
Configure SMTP early — authentik uses email for password resets, MFA, invitations, and admin alerts.
# in your compose .env or values.yaml
AUTHENTIK_EMAIL__HOST: smtp.example.com
AUTHENTIK_EMAIL__PORT: 587
AUTHENTIK_EMAIL__USERNAME: [email removed]
AUTHENTIK_EMAIL__PASSWORD: <smtp-app-password>
AUTHENTIK_EMAIL__USE_TLS: "true"
AUTHENTIK_EMAIL__FROM: [email removed]
Test it after restart:
docker compose exec worker ak test_email [email removed] -S default-email-stage
Expected output:
Sending test email via stage 'default-email-stage' to [email removed]... sent.
Reverse proxy
You almost certainly want one. authentik listens on :9000 (HTTP) and :9443 (HTTPS) by default. Place it behind Traefik / Nginx / Caddy / HAProxy with a real TLS cert.
Important
Add your reverse proxy's IP to
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRSso authentik trustsX-Forwarded-For. Defaults cover RFC1918 ranges (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) and IPv6 link-local — if you're behind a non-RFC1918 reverse proxy (Cloudflare Tunnel, public load balancer), add its CIDRs explicitly.
For full reverse-proxy snippets see docs.goauthentik.io/docs/install-config/reverse-proxy.
5. Initial Setup
- Open
http://<your-server>:9000/if/flow/initial-setup/. - Set a password for
akadmin(the bootstrap superuser). - You're redirected to the Admin interface at
/if/admin/.
Tip
The User interface (
/if/user/) is what your end users see. The Admin interface (/if/admin/) is the config UI. The Flow interface (/if/flow/) is what runs during a login/enroll/recovery. Bookmark whichever you use most.
Verify the install
In the Admin interface:
- Dashboard — should show "System status: All good", PostgreSQL connected, embedded outpost healthy.
- System → Workers — at least one worker is connected (Last seen < 60s ago). If "No workers" persists, see Troubleshooting.
- Flows and Stages → Flows — you should see 6+ default flows:
default-authentication-flow,default-invalidation-flow,default-password-change-flow,default-source-enrollment-flow,default-source-authentication-flow,default-user-settings-flow.
If any of the above is missing, the bundled blueprints didn't apply. Run docker compose exec worker ak apply_blueprint default/0-bootstrap.yaml and check the System Tasks page for the result.
First admin actions
- Replace
akadminwith a real account: Directory → Users → Create (your name) → assign to theauthentik Adminsgroup → log out, log back in as you. - Disable
akadmin: Directory → Users → akadmin → Deactivate. (Don't delete — keep it for break-glass recovery.) - Generate a recovery key in case you lock yourself out:
docker compose exec worker ak create_recovery_key 525600 akadmin
The output is a URL like https://<your-server>/if/flow/...?token=.... Print it, store it offline. It's valid for 525,600 minutes (one year).
6. Adding Your First Application
Let's secure a real app — Grafana via OAuth2/OIDC.
Step 1 — Create the Provider
- Admin → Applications → Providers → Create → OAuth2/OpenID Provider.
- Name:
Grafana OAuth. - Authorization flow:
default-provider-authorization-implicit-consent(orexplicit-consentif you want users to see the scopes screen). - Client type:
Confidential. - Note the auto-generated Client ID and Client Secret.
- Redirect URIs:
https://grafana.example.com/login/generic_oauth(exact match — wildcards via regex are supported but discouraged). - Signing Key: pick
authentik Self-signed Certificate(or your own). - Save.
Step 2 — Create the Application
- Admin → Applications → Applications → Create.
- Name:
Grafana. - Slug:
grafana(URL-safe, becomes/application/o/grafana/). - Provider: select
Grafana OAuth. - Launch URL:
https://grafana.example.com. - Icon: upload or paste a URL.
- Save.
Step 3 — Configure Grafana
In Grafana's grafana.ini:
[auth.generic_oauth]
enabled = true
name = authentik
client_id = <client-id-from-step-1>
client_secret = <client-secret-from-step-1>
scopes = openid profile email
auth_url = https://<authentik>/application/o/authorize/
token_url = https://<authentik>/application/o/token/
api_url = https://<authentik>/application/o/userinfo/
login_attribute_path = preferred_username
role_attribute_path = contains(groups[*], 'Grafana Admins') && 'Admin' || 'Viewer'
Restart Grafana, click "Sign in with authentik", and you're in.
Tip
The OIDC discovery endpoint at
https://<authentik>/application/o/grafana/.well-known/openid-configurationreturns all URLs above as JSON — paste that into any modern OIDC client and it auto-configures.
Step 4 — Restrict who can log in
-
Customization → Policies → Create → Expression Policy.
-
Expression:
return ak_is_group_member(request.user, name="Grafana Users") -
Bind it: Applications → Applications → Grafana → Policy / Group / User Bindings → Create binding → Policy = the one you just made, Negate = no.
Now only members of the Grafana Users group will see Grafana in their User interface and be allowed to authenticate.
7. Flows and Stages
A flow is a recipe. Each step in the recipe is a stage. Flows are addressed in the URL: /if/flow/<slug>/.
Default flows
| Slug | Purpose |
|---|---|
default-authentication-flow |
The main login flow. |
default-invalidation-flow |
Logout / session destroy. |
default-password-change-flow |
User-initiated password change. |
default-recovery-flow |
Forgot password. |
default-source-enrollment-flow |
First-time login from a federated source. |
default-source-authentication-flow |
Subsequent login from a federated source. |
default-provider-authorization-implicit-consent |
OAuth2 consent screen (auto-allow). |
default-provider-authorization-explicit-consent |
OAuth2 consent screen (show scopes). |
default-user-settings-flow |
The /if/user/#/settings self-service page. |
default-user-settings-flow (MFA tab) |
MFA enrollment/management. |
All shipped stage types
Confirmed in extracted/class_hierarchy.tsv and the authentik/stages/ directory tree:
| Stage | Purpose | License |
|---|---|---|
identification |
Collect username/email (and optionally show federated source buttons) | core |
password |
Validate the user's password | core |
prompt |
Generic form (collect arbitrary attributes) | core |
consent |
"Do you allow Grafana to read your email?" | core |
user_login |
Create the session | core |
user_logout |
Destroy the session | core |
user_write |
Persist collected attributes to a User object (enrollment, profile update) | core |
user_delete |
Delete a user account | core |
invitation |
Validate an invitation token | core |
deny |
Hard stop the flow (with custom message) | core |
redirect |
Send the user to an external URL | core |
email |
Send an email (verification, recovery) | core |
captcha |
Google reCAPTCHA / hCaptcha / Cloudflare Turnstile | core |
source |
Hand off to a federated source mid-flow | core |
authenticator_static |
Static one-time backup codes | core |
authenticator_totp |
TOTP enrollment (Google Authenticator, 1Password, etc.) | core |
authenticator_webauthn |
Passkey / FIDO2 / security-key enrollment | core |
authenticator_duo |
Duo Push enrollment | core |
authenticator_sms |
SMS code (Twilio or generic webhook) | core |
authenticator_email |
Email OTP | core |
authenticator_endpoint_gdtc |
Google Device Trust Connector | Enterprise |
authenticator_validate |
Validate any of the above MFA factors | core |
account_lockdown |
Lock account on policy match | Enterprise |
mtls |
Validate client cert (mutual TLS) | Enterprise |
Editing a flow
- Flows and Stages → Flows → click the flow name.
- Stage Bindings tab — drag stages to reorder, click Create binding to add a new stage, edit the binding's Order number to reposition.
- To gate a stage on a policy: edit the binding → set Policy → Save. Now the stage runs only if the policy returns
True.
Tip
Never edit a
default-*flow directly. Clone it (Duplicate action), edit the clone, then point your application at the new flow. Defaults get re-applied on upgrade and your changes will vanish.
Building a custom flow — example
A "passwordless WebAuthn" login flow:
- Create flow: name
Passwordless, slugpasswordless, designationAuthentication, layoutStacked. - Bind stages in order:
- 10 —
default-authentication-identification(Identification, but configure it to not show password) - 20 —
default-authentication-mfa-validation(Authenticator Validation, accept only WebAuthn) - 30 —
default-authentication-login(User Login)
- 10 —
- Done. Send users to
/if/flow/passwordless/to test.
8. Providers — Securing Apps
Providers are how external apps talk to authentik. Each app gets one provider. Pick the protocol the app supports.
Available provider types
| Provider | Use when… | Notes |
|---|---|---|
| OAuth2 / OpenID Connect | The app is a modern web app or SPA, supports OIDC | Most common. Confidential or public client. Refresh tokens, device code grant, dynamic client registration via /application/o/register/. |
| SAML 2.0 | The app is enterprise software (Atlassian, Salesforce, AWS IAM, Microsoft 365) | IdP-initiated and SP-initiated. Encryption + signing. Per-app NameID format. Redirect or POST binding. |
| Proxy | The app has no auth, but you can put a reverse proxy in front of it | Requires an outpost. Three modes: forward auth (Nginx/Traefik/Caddy), forward auth (single application), proxy. |
| LDAP | The app only speaks LDAP (e.g. Gitea, Nextcloud, Synology) | authentik becomes an LDAP server. Requires an LDAP outpost. Supports bind-DN and search-bind flows. |
| RADIUS | The app or appliance only speaks RADIUS (firewalls, VPNs, switches) | Requires a RADIUS outpost. PAP, CHAP, MS-CHAPv2. |
| SCIM 2.0 | You want to push users/groups to an external SaaS that supports SCIM | Outbound provisioning. Schedules and reconciliation. |
| Microsoft Entra ID | You want to sync users/groups to Entra ID (formerly Azure AD) | Enterprise. Uses MS Graph. |
| Google Workspace | You want to sync users/groups to Google Workspace | Enterprise. Uses Admin SDK. |
| WS-Federation | The app speaks WS-Federation (legacy SharePoint, some .NET apps) | Enterprise. |
| RAC (Remote Access Connection) | You want browser-based RDP/SSH/VNC via Apache Guacamole-like UI | Enterprise. Requires a RAC outpost. |
| SSF (Shared Signals Framework) | You want to push session events to a SaaS that consumes SSF | Enterprise. Push-only. |
OAuth2 / OIDC — endpoints
Auto-mounted by authentik when a provider exists:
| Endpoint | Path | Used by |
|---|---|---|
| Discovery | /application/o/<slug>/.well-known/openid-configuration |
Most modern OIDC clients (just paste the URL) |
| Authorization | /application/o/authorize/ |
Browser-driven login |
| Token | /application/o/token/ |
Code → token exchange |
| UserInfo | /application/o/userinfo/ |
Token → user attributes |
| JWKS | /application/o/<slug>/jwks/ |
Token signature verification |
| End-session | /application/o/<slug>/end-session/ |
Single Logout |
| Introspection | /application/o/introspect/ |
Resource server token validation |
| Revoke | /application/o/revoke/ |
Refresh-token revocation |
| Device | /application/o/device/ |
Device code grant (TVs, CLIs) |
SAML — metadata
IdP metadata URL: https://<authentik>/api/v3/providers/saml/<id>/metadata/?download
SSO URL (redirect): https://<authentik>/application/saml/<slug>/sso/binding/redirect/
SSO URL (POST): https://<authentik>/application/saml/<slug>/sso/binding/post/
SLO URL (redirect): https://<authentik>/application/saml/<slug>/slo/binding/redirect/
SLO URL (POST): https://<authentik>/application/saml/<slug>/slo/binding/post/
Most SAML SPs will accept the metadata URL and configure themselves. If yours doesn't, download the XML and import it.
Proxy — Forward Auth recipe (Traefik)
The Proxy provider lets you put authentik in front of any app, even ones with zero auth support, by terminating in an outpost and verifying every request.
# traefik.yml — middleware
http:
middlewares:
authentik:
forwardAuth:
address: http://outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-email
- X-authentik-uid
- X-authentik-groups
- X-authentik-name
Then on any service:
labels:
- "traefik.http.routers.myapp.middlewares=authentik@file"
Now myapp requires an authentik login. The user's email, username, and groups are forwarded as headers — your app can use them or ignore them.
9. Sources — Authenticating Users
A source lets users sign into authentik with credentials from somewhere else. authentik supports both push-style (we ask them) and pull-style (they're already logged in elsewhere) federation.
Available source types
| Source | Use when… | Notes |
|---|---|---|
| LDAP | You have Active Directory or OpenLDAP and want users to log in with their AD credentials | Periodic sync of users + groups. Detects deletions. Password validation at login time. |
| OAuth2 | You want "Sign in with Google / GitHub / GitLab / Discord / Microsoft / Twitter / Twitch / Mailcow / Apple / Okta / Keycloak / Shibboleth / WeChat" | Each is a preconfigured type with the right OAuth client/scope defaults. Generic OAuth2 source for anything else. |
| SAML | You want users to sign in via an external SAML IdP (your customer's Okta, etc.) | Outbound metadata + inbound assertion validation. |
| Kerberos | Domain-joined Windows clients should SSO via GSSAPI/SPNEGO | Requires a keytab. Supports principal-name mapping. |
| SCIM | An external IdP pushes users/groups to you via SCIM 2.0 | Receive-side. Schemas and PATCH support. |
| Plex | You're building a Plex-centric service | Uses Plex's auth API. |
| Telegram | Telegram login widget | Uses Telegram's bot API. |
Adding an LDAP source (Active Directory)
- Directory → Federation & Social login → Create → LDAP Source.
- Server URI:
ldap://dc1.example.com:389orldaps://dc1.example.com:636. - Bind CN:
cn=authentik,ou=Service Accounts,dc=example,dc=com(a read-only service account). - Bind Password: the password for that account.
- Base DN:
dc=example,dc=com. - User object filter:
(objectClass=user)(or(&(objectClass=user)(sAMAccountName=*))to skip computer accounts). - Group object filter:
(objectClass=group). - Object uniqueness field:
objectSidfor AD,uidfor OpenLDAP,entryUUIDfor 389DS. - Save → click Run sync now.
- Verify: Directory → Users now shows synced users with the source set to your LDAP source.
Tip
Test the connection first without saving the source:
docker compose exec worker ak ldap_check_connection <source-slug>The command is in
authentik/sources/ldap/management/commands/ldap_check_connection.py. It printsOKor a Python-formatted error.
Adding an OAuth2 source (GitHub)
- Federation & Social login → Create → OAuth Source → Provider type: GitHub.
- Get a Client ID/Secret from GitHub: GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
- Authorization callback URL on GitHub:
https://<authentik>/source/oauth/callback/<source-slug>/. - Paste Client ID + Secret into authentik. Save.
- Edit your
default-source-enrollment-flowanddefault-source-authentication-flowif you want custom enrollment behavior. Defaults are usually fine. - The "Sign in with GitHub" button now appears on your identification stage if you bound the source to it.
Kerberos SSO (Active Directory desktops)
# 1. Create a keytab on a DC
ktpass -princ HTTP/[email removed] \
-mapuser authentik$ -pass <password> \
-crypto AES256-SHA1 -ptype KRB5_NT_PRINCIPAL \
-out authentik.keytab
# 2. Copy keytab into the authentik worker:
docker cp authentik.keytab authentik-worker-1:/var/lib/authentik/
# 3. Federation → Create → Kerberos Source
# keytab path: /var/lib/authentik/authentik.keytab
# realm: EXAMPLE.COM
# 4. Test:
docker compose exec worker ak kerberos_check_connection <source-slug>
Then enable it in your identification stage's Source settings so the Kerberos button (or auto-SSO if you bind to MTLS) appears.
10. Outposts
An outpost is a separate process (Go binary, runs in its own container) that handles the wire protocol for non-HTTP providers. authentik core runs in Python; outposts run in Go because LDAP, RADIUS, RDP, and forward-auth need low-latency raw-protocol handling.
Outpost types
Confirmed in the briefing's module_index.cbf (cmd/proxy, cmd/ldap, cmd/radius, cmd/rac, cmd/server):
| Outpost | Binary | Port | Used by |
|---|---|---|---|
| Embedded | inside the server container |
shares 9000 | Proxy provider for single-host installs |
| Proxy | cmd/proxy |
9000/9443 | Proxy provider for separate deployment |
| LDAP | cmd/ldap |
3389/6636 (TLS) | LDAP provider |
| RADIUS | cmd/radius |
1812 (UDP) | RADIUS provider |
| RAC | cmd/rac |
varies | RAC provider (Guacamole-style) |
Embedded vs separate
- Embedded — easiest. Lives inside the main
servercontainer. Set the application's outpost =authentik Embedded Outpost. No extra deploy. Use for proxy providers only. - Separate — required for LDAP, RADIUS, RAC, and recommended for production Proxy at scale. Deploy as a separate Docker container or Kubernetes pod. authentik can auto-deploy outposts to Docker or Kubernetes if you give the worker the necessary socket / kubeconfig access.
Auto-deploying outposts via Docker
The default compose.yml mounts /var/run/docker.sock into the worker container. This lets authentik spin up outpost containers when you create a Proxy/LDAP/RADIUS/RAC provider and bind it to an outpost.
Warning
Mounting
/var/run/docker.sockgives the worker container effective root on the host. If you can't accept that, use a Docker Socket Proxy to restrict the operations, or deploy outposts manually.
Manual outpost deployment
# outpost-proxy.compose.yml
services:
proxy:
image: ghcr.io/goauthentik/proxy:2026.8.0-rc1
ports:
- 9000:9000
- 9443:9443
environment:
AUTHENTIK_HOST: https://authentik.example.com
AUTHENTIK_TOKEN: <outpost-token-from-admin-ui>
AUTHENTIK_INSECURE: "false"
The outpost token is generated when you create the outpost in the Admin UI. Each outpost gets its own token.
11. Users, Groups, and RBAC
Users
Three user types, set per-user:
| Type | Counts toward license? | Notes |
|---|---|---|
internal |
Yes | Regular user. Can log into the User interface. |
external |
Yes | Customer-facing user (B2C). Same capabilities as internal but billed differently in Enterprise. |
service_account |
No | API consumer. Cannot interactively log in. Holds tokens. |
internal_service_account |
No | Used by outposts. Created automatically. |
Switch a user's type:
docker compose exec worker ak change_user_type --type service_account alice
The command is in authentik/core/management/commands/change_user_type.py:11. Use --all to change every user (be careful).
Groups
Groups can be local (created in authentik), synced (from an LDAP/SCIM/OAuth source), or hybrid. Group membership drives:
- Application access — bind a group to an application via a policy or directly via "Group Bindings".
- RBAC roles — assign a Role to a Group; everyone in the group gets the role.
- SAML/OIDC claims — the
groupsclaim in OAuth2 access tokens, the SAMLMemberOfattribute, and the LDAP-providermemberOfare all auto-populated from group membership.
RBAC
Roles assign Django-Guardian object-level permissions. The bundled authentik Admins role grants full admin. You can create finer roles like "View users in engineering group only".
| Role permission scope | Notes |
|---|---|
| Global | Affects everything (e.g. can_view_event) |
| Per-model | Affects a model class (e.g. can_change_user on User) |
| Per-object | Affects a single instance (e.g. can_change_user on user-id=42) |
See Directory → RBAC in the Admin UI.
12. Policies and Property Mappings
Policies
A policy is a function that returns True or False. You bind policies to flows, stages, application visibility, or source enrollment to gate access.
| Policy type | Returns True when… |
|---|---|
| Expression | A Python expression evaluates truthy. Has access to request, ak_user_has_authenticator(...), ak_is_group_member(...), ak_logger, and more. |
| Event Matcher | A specific event has occurred recently for this user (e.g. "5 failed logins in 1 hour"). |
| Password | Password meets complexity / age requirements. |
| Reputation | The user's IP/user reputation score is above a threshold (negative scores accumulate on failed logins). |
| Expiry | The user/password is older than N days. |
| GeoIP | Login is from a specific country/ASN. Requires GeoLite2 mounted at /geoip/. |
| Dummy | Always returns the value you configure. For testing. |
| Unique Password | Enterprise. Password has never been used by this user before. |
Expression policy — example
# Block logins from non-corporate IPs unless user is in 'Remote Workers' group
from authentik.policies.utils import ak_is_group_member
if request.context.get("geoip") and request.context["geoip"]["country"] != "US":
return ak_is_group_member(request.user, name="Remote Workers")
return True
Available helpers (documented in website/docs/customize/policies/expression/):
| Function | Use |
|---|---|
ak_is_group_member(user, name="...") |
Check membership by group name |
ak_user_has_authenticator(user, device_type="totp") |
Does the user have an MFA device? |
ak_logger.info("msg") |
Write to authentik's log |
ak_call_policy("slug", request=request) |
Compose policies — call one from another |
request.context["geoip"] |
IP geolocation, if enabled |
request.context["asn"] |
ASN data, if enabled |
Property mappings
Property mappings transform user/group attributes into the shape your provider/source needs. They're tiny Python expressions.
OAuth2 scope mapping — example
# Adds the user's department to the 'department' claim
return {
"department": request.user.attributes.get("department", "unknown"),
"manager": request.user.attributes.get("manager_email"),
}
SAML attribute mapping — example
# Send memberships as a multi-valued SAML attribute
return [group.name for group in request.user.ak_groups.all()]
Mappings are listed under Customization → Property Mappings.
13. Blueprints — GitOps for authentik
Blueprints let you define every authentik object (Flow, Stage, Policy, Provider, Application, User, Group, Outpost) as YAML, apply them automatically on startup, and version-control them in git.
A minimal blueprint
version: 1
metadata:
name: "my-grafana-app"
entries:
- model: authentik_providers_oauth2.oauth2provider
identifiers:
name: "Grafana OAuth"
attrs:
client_type: "confidential"
client_id: !Env GRAFANA_CLIENT_ID
client_secret: !Env GRAFANA_CLIENT_SECRET
redirect_uris:
- matching_mode: strict
url: "https://grafana.example.com/login/generic_oauth"
signing_key: !Find [authentik_crypto.certificatekeypair, [name, "authentik Self-signed Certificate"]]
- model: authentik_core.application
identifiers:
slug: "grafana"
attrs:
name: "Grafana"
provider: !KeyOf provider-id
meta_launch_url: "https://grafana.example.com"
YAML tags
| Tag | What it does |
|---|---|
!Env VAR |
Read environment variable VAR |
!Find [model, [field, value]] |
Look up an existing object by field/value |
!KeyOf <id> |
Reference another object in the same blueprint by identifiers.id |
!Format ["template {0}", arg] |
Python .format() |
!If [condition, then, else] |
Conditional |
!Context key |
Read from the apply-time context |
Applying blueprints
| How | What |
|---|---|
Mounted at /blueprints/ |
Automatically discovered and applied on startup |
docker compose exec worker ak apply_blueprint <file> |
Manual apply, useful for testing |
Via API: POST /api/v3/managed/blueprints/ |
Upload and apply at runtime |
The apply_blueprint command is at authentik/blueprints/management/commands/apply_blueprint.py:36.
Tip
Bundled blueprints live at
blueprints/default/,blueprints/example/, andblueprints/system/. Don't edit those — copy what you need into a new file under your own/blueprints/custom/mount.
Example library
The blueprints/example/ directory ships ~20 ready-to-adapt blueprints covering common SaaS apps (Slack, Nextcloud, AWS, GitLab, etc.) plus all the protocol patterns. Browse the GitHub directory for the full list.
14. Customization and Branding
Brands (multi-tenancy)
A Brand is a subdomain-scoped configuration: logo, colors, default flows, footer links. Use this to run multiple "instances" of authentik from one deployment.
- System → Brands → Create.
- Domain:
customer-a.auth.example.com. - Default flows: set the four flows (auth, invalidation, recovery, unenrollment) you want this brand to use.
- Branding: set logo, favicon, title, additional metadata.
- Save.
When a request hits customer-a.auth.example.com, authentik will use this brand's flows and visual settings.
Custom CSS
System → Brands → <your brand> → Custom CSS:
:root {
--pf-global--primary-color--100: #1f2937;
--pf-global--BackgroundColor--dark-100: #111827;
}
.pf-c-login__main-header img { max-height: 80px; }
Applied immediately to all flow and admin interfaces under this brand.
Email templates
Mount custom templates at /templates/ in the worker container, then reference them in an Email stage's "Template" dropdown. See authentik/stages/email/templates/ for the structure to clone.
15. Events and Notifications
Every authentik action emits an event. Events are stored in PostgreSQL and queryable from the Admin UI.
Event types (sampled from extracted/class_hierarchy.tsv)
| Action | Trigger |
|---|---|
login / login_failed |
User authentication attempt |
logout |
Session destruction |
user_write |
User attribute change |
password_set |
Password change |
secret_view |
Sensitive value shown in UI |
configuration_error |
Misconfiguration detected |
policy_execution |
Policy evaluated |
model_created / model_updated / model_deleted |
DB change |
update_available |
New authentik version released |
impersonation_started / impersonation_ended |
Admin impersonated a user |
flow_execution |
Flow ran |
source_linked |
User completed federated enrollment |
Notification rules
- System → Events → Notification Rules → Create.
- Event matcher policy: e.g.
EventMatcherpolicy forlogin_failed. - Group to notify: e.g.
Security Admins. - Severity, transport (email, webhook, Sentry).
- Save.
Webhook transports
# Notification Transport: Webhook (Generic)
URL: https://chat.example.com/hooks/incoming/xyz
Webhook Body:
{
"text": "{{ notification.body }}",
"user": "{{ notification.user.username }}",
"severity": "{{ notification.severity }}"
}
Slack-formatted and Discord-formatted variants ship as separate transport types.
16. Backups, Upgrades, and Air-Gapped Installs
Backups
authentik state lives in two places:
-
PostgreSQL — every object, every user, every flow, every event. Back it up with
pg_dump:docker compose exec postgresql pg_dump -U authentik authentik > authentik-$(date +%F).sql -
/datavolume — uploaded files (icons, flow backgrounds, CSV reports).tarit:docker compose exec server tar -C /data -czf - . > authentik-data-$(date +%F).tar.gz
Restore: stop authentik, psql < dump.sql and untar the data volume back.
Upgrades
wget -O compose.yml https://docs.goauthentik.io/compose.yml
docker compose pull
docker compose up -d
Migrations run automatically in the worker on container start. Watch the worker logs:
docker compose logs -f worker | grep -E "(migrat|error)"
Important
Always read the release notes at docs.goauthentik.io/docs/releases before upgrading. Some releases introduce breaking changes (e.g.
2026.5.0changed the email password-reset template — see #22757). Migrations are forward-only.
Air-gapped installs
Set in .env:
AUTHENTIK_DISABLE_UPDATE_CHECK=true
AUTHENTIK_DISABLE_STARTUP_ANALYTICS=true
AUTHENTIK_ERROR_REPORTING__ENABLED=false
Mirror the container images to an internal registry. authentik does not call home for license validation in core; the Enterprise license server is checked periodically — see docs.goauthentik.io/docs/install-config/air-gapped for the full offline procedure.
17. Common Tasks (Recipes)
Enable MFA for a specific group
-
Create an Expression Policy:
return ak_is_group_member(request.user, name="MFA Required") -
Edit
default-authentication-flow→ Stage Binding fordefault-authentication-mfa-validation→ set this policy. Now MFA validation runs only for users in theMFA Requiredgroup.
Force password rotation every 90 days
- Customization → Policies → Create → Password Expiry Policy → Days: 90.
- Edit
default-authentication-flow→ after Password stage, add a stage binding fordefault-password-change-prompt(or a custom prompt) gated by this policy.
Auto-enrol new users from an OAuth source into a group
-
Federation & Social login → <your source> → Property Mapping:
# Enrollment Mapping from authentik.core.models import Group default_group, _ = Group.objects.get_or_create(name="Auto-enrolled") return {"ak_groups": [default_group]} -
Bind this mapping to the source's enrollment flow.
Use an external PostgreSQL
In .env:
AUTHENTIK_POSTGRESQL__HOST=db.example.com
AUTHENTIK_POSTGRESQL__PORT=5432
AUTHENTIK_POSTGRESQL__NAME=authentik
AUTHENTIK_POSTGRESQL__USER=authentik
AUTHENTIK_POSTGRESQL__PASSWORD=<password>
AUTHENTIK_POSTGRESQL__SSLMODE=require
Then remove the postgresql service from compose.yml. The same env keys exist for read replicas via AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__HOST etc. (see authentik/lib/default.yml lines 18-31).
Run with FIPS-compliant crypto
authentik/lib/default.yml exposes:
compliance:
fips:
enabled: false # set true via AUTHENTIK_COMPLIANCE__FIPS__ENABLED=true
The Rust outposts already link aws-lc-rs with the fips feature (see Cargo.toml). When compliance.fips.enabled=true, authentik also restricts TLS cipher suites and refuses non-FIPS modules at startup.
Export your config as blueprints
docker compose exec worker ak apply_blueprint --export > my-config.yaml
This roundtrips every flow/stage/provider/policy currently in the DB into a blueprint you can commit to git.
Generate or rotate an admin recovery key
docker compose exec worker ak create_recovery_key 60 akadmin
60 is duration in minutes (so 1 hour). The output URL bypasses MFA and password — store it offline.
Test an SMTP setup without sending real reset emails
docker compose exec worker ak test_email [email removed] -S default-email-stage
(From authentik/stages/email/management/commands/test_email.py.)
Apply schema changes to your IDE — generate Python types
docker compose exec worker ak build_schema --api-file schema.yml
Then download schema.yml, run openapi-generator against it for your language of choice. (The TypeScript client ships pre-generated at packages/client-ts/; Go client at packages/client-go/; Rust client at packages/client-rust/.)
18. CLI Reference (ak)
All commands run inside the worker container: docker compose exec worker ak <command>. The full list is auto-discovered from Django management commands under authentik/*/management/commands/. The most useful ones, from extracted/cli_commands.tsv:
| Command | Purpose | Source |
|---|---|---|
ak apply_blueprint <file> |
Apply a blueprint file (or --export to dump current state) |
authentik/blueprints/management/commands/apply_blueprint.py:36 |
ak benchmark -p 4 --csv |
Stress-test flow execution. -p = parallel processes |
authentik/flows/management/commands/benchmark.py:56-63 |
ak build_schema --api-file schema.yml |
Regenerate the OpenAPI spec | authentik/api/management/commands/build_schema.py:20-21 |
ak change_user_type --type <type> --all <usernames> |
Convert user(s) between internal / external / service_account | authentik/core/management/commands/change_user_type.py:11-13 |
ak create_admin_group <user> |
Add a user to the bootstrap admins group | authentik/recovery/management/commands/create_admin_group.py:18 |
ak create_recovery_key <minutes> <user> |
Generate a one-shot recovery URL | authentik/recovery/management/commands/create_recovery_key.py:29-36 |
ak hash_password <password> |
Hash a password as authentik stores it (for blueprint seeding) | authentik/core/management/commands/hash_password.py:13 |
ak import_certificate --name <n> --certificate <pem> --private-key <pem> |
Import a TLS cert/key pair | authentik/crypto/management/commands/import_certificate.py:51-53 |
ak kerberos_check_connection <source-slug> |
Smoke-test a Kerberos source | authentik/sources/kerberos/management/commands/kerberos_check_connection.py:17 |
ak kerberos_sync <source-slug> |
Run an on-demand Kerberos sync | authentik/sources/kerberos/management/commands/kerberos_sync.py:16 |
ak ldap_check_connection <source-slug> |
Smoke-test an LDAP source | authentik/sources/ldap/management/commands/ldap_check_connection.py:17 |
ak ldap_sync <source-slug> |
Run an on-demand LDAP sync | authentik/sources/ldap/management/commands/ldap_sync.py:15 |
ak scim_sync <provider-slug> |
Run an on-demand SCIM provisioning sync (outbound) | authentik/providers/scim/management/commands/scim_sync.py:15 |
ak set_flag <flag_key> <flag_value> |
Toggle a tenant feature flag | authentik/tenants/management/commands/set_flag.py:11-12 |
ak test_email <to> -S <stage-slug> |
Send a test email through a configured email stage | authentik/stages/email/management/commands/test_email.py:49-50 |
ak clean_orphan_obj_perms --batch-size N |
Clean up Guardian object permissions for deleted users | packages/ak-guardian/guardian/management/commands/clean_orphan_obj_perms.py:25-40 |
Also bundled but rarely user-facing: dumpdata, loaddata, makemigrations, migrate, runserver, shell — all standard Django management commands.
Outpost binaries
| Binary | Cobra-style commands | Source |
|---|---|---|
authentik-proxy |
serve, healthcheck |
cmd/proxy/main.go |
authentik-ldap |
serve, healthcheck |
cmd/ldap/main.go |
authentik-radius |
serve, healthcheck |
cmd/radius/main.go |
authentik-rac |
serve, healthcheck |
cmd/rac/main.go |
authentik-server |
serve, healthcheck, worker |
cmd/server/main.go, cmd/server/server.go:26, cmd/server/healthcheck.go:20 |
All Go binaries respect AUTHENTIK_* env vars the same way the Python server does (parsed by internal/config/config.go).
19. Troubleshooting
Error: Migration authentik_core.0058_setup fails on upgrade
Cause. Upgrading directly across more than one minor release without intermediate migrations applied. Confirmed in #22688 (upgrade from 2025.10.2 → 2026.5.0).
Solution. Roll back to your previous tag, then upgrade one minor release at a time:
# Restore the previous compose.yml
git checkout HEAD~1 compose.yml
docker compose up -d
# Wait for healthy, then upgrade to the next minor
sed -i 's|:2025.10.2|:2025.12.5|' compose.yml
docker compose pull && docker compose up -d
# Repeat for each minor up to 2026.5.0
Prevention. Read the release notes before every upgrade; skip-version upgrades are explicitly unsupported.
Error: No identification data provided after upgrading 2FA
Cause. Bug introduced in 2025.12.1 affecting Authenticator Validation Stage configurations. Tracked in #19552 (43 community comments — fix landed in 2026.1.0).
Solution. Upgrade to 2026.1.0 or later. Workaround on the affected version: edit your authentication flow's Authenticator Validation stage and re-save it (resets the internal device-class set).
Prevention. Stick to .0 / .x patch versions on production; let .0-rc and .0 settle before adopting.
Error: Starting the worker makes the server not load
Cause. Resource contention or a worker that holds the DB exclusive lock during a long-running migration. Closed in #22606; fix backported.
Solution. Restart in order: stop both, start server and postgresql, wait for server to be healthy, then start worker. If migrations still hang, docker compose logs worker to see which migration is stuck.
Prevention. Pin pre-release tags only in staging.
Error: SAML provider not JSON serializable (2026.5.0)
Cause. Regression in Event.log_deprecation when the SAML provider uses redirect binding. Closed in #22694 — patched in 2026.5.1.
Solution. Upgrade to 2026.5.1 or later.
Prevention. Subscribe to release announcements; this was hotfixed within 48 hours.
Error: OAuth single logout broken (2026.5.0)
Cause. Front-channel logout regression. Tracked in #22572 (14 comments, still open at time of writing).
Solution. Workaround: switch the OAuth provider to back-channel logout in the Provider's "Logout settings" tab. Long-term fix is pending.
Prevention. Test logout flows in staging before upgrading.
Error: LDAP Provider not returning expected results
Cause. Various root causes — most commonly missing objectClass mappings or incorrect search base. See #22743 (closed).
Solution.
- Check the LDAP outpost is connected: Admin → Applications → Outposts → the outpost should show "Last seen" within 60 seconds.
- Try the search manually:
ldapsearch -H ldap://outpost.example.com:3389 \ -D "cn=service-account,ou=users,dc=ldap,dc=goauthentik,dc=io" \ -w '<token>' -b "dc=ldap,dc=goauthentik,dc=io" \ "(objectClass=user)" - Check the outpost logs:
docker compose logs ldap-outpost.
Prevention. Keep your LDAP provider's "Search group" pointing at a small test group during initial setup; expand only after the smoke test passes.
Error: Admin interface crashes in Safari
Cause. Web component bug in older Safari versions. Tracked in #19166 (8 comments) → references #14322.
Solution. Use Firefox or Chrome for the Admin interface. The User interface is unaffected. Upgrade Safari to 17+.
Prevention. Test admin workflows in the browsers your team actually uses.
Error: "Refresh" on Outposts page doesn't refresh state
Cause. UI bug — the button triggers a fetch but doesn't re-render the list. Tracked in #19951.
Solution. Reload the whole page (F5). The data is updated; only the visual update is broken.
Prevention. N/A — cosmetic.
Error: authentik redirects to interface instead of app
Cause. Application launch URL not set or set incorrectly. Tracked in #18450.
Solution. Edit the Application → Launch URL must be the public URL of the app, not authentik's URL. For OAuth apps, this is what the user lands on after consent.
Prevention. Always test the Launch URL by clicking the app tile in the User interface after creation.
Error: Custom user attributes prevent invitation creation
Cause. Wizard validation bug when the User model has required custom attributes. Tracked in #22637.
Solution. Use the API directly to create the invitation:
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
https://<authentik>/api/v3/stages/invitation/invitations/ \
-d '{"name":"new-user","flow":"<uuid>","fixed_data":{"email":"[email removed]"}}'
Prevention. Mark custom user attributes as nullable in your prompt stage until the wizard fix lands.
Error: Cannot import the device-code-flow blueprint
Cause. The blueprint file location moved between releases. Tracked in #19525.
Solution. Use the bundled blueprint via apply_blueprint:
docker compose exec worker ak apply_blueprint default/flow-device-code-authorization.yaml
Prevention. Reference blueprints via their canonical name (default/...) rather than downloading them.
Error: Email password-reset uses naturaltime ungrammatically
Cause. Template uses Django's naturaltime filter which produces "now", "1 day", "2 days" — readable in English but ungrammatical when used as in "Token expires {{ ... }}". Tracked in #22757.
Solution. Override the template by mounting your own at /templates/email/password_reset.html in the worker container.
Prevention. Custom email templates are a one-time fix.
Error: outpost shows disconnected immediately after creation
Cause. Outpost token not propagated yet (auto-deploy mode), or the outpost can't reach AUTHENTIK_HOST.
Solution.
- Check the outpost logs:
docker logs <outpost-container>. Look for "connected" or any TLS/network errors. - Verify
AUTHENTIK_HOSTis reachable from the outpost:docker exec <outpost> wget -qO- $AUTHENTIK_HOST/-/health/ready/. - If the outpost is in a different network/cluster, set
AUTHENTIK_HOST_BROWSERto the user-facing URL andAUTHENTIK_HOSTto the internal URL.
Prevention. Always set AUTHENTIK_HOST_BROWSER separately when behind a reverse proxy.
Error: PostgreSQL "password too long"
Cause. PostgreSQL's libpq does not accept passwords > 99 chars.
Solution. Regenerate PG_PASS with a shorter length:
sed -i '/^PG_PASS=/d' .env
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" >> .env
If you've already run with the long password, you'll also need to ALTER USER in PostgreSQL to match.
Prevention. Stick with the documented openssl rand -base64 36 recipe.
Error: All users sign in but no apps appear
Cause. Users are authenticated but no Application is bound to their group / a policy is denying visibility.
Solution.
- Admin → Applications → Applications → <your app> → Policy Bindings — check if there's a deny policy.
- Directory → Users → <user> → see "Applications" tab — what does this user actually have access to?
- If empty, add a group binding from a group that includes the user.
Prevention. When creating an app, always set at least one group/user binding immediately, or accept "no policy = visible to everyone".
20. FAQ
Q: Is authentik really free? What does Enterprise add?
Yes — the core (everything in authentik/ except authentik/enterprise/) is MIT-licensed and free forever. Enterprise adds: SSF, MS Entra/Google Workspace sync, audit logging, MTLS stage, RAC provider, account lockdown stage, unique-password policy, and a Support center. Pricing at goauthentik.io/pricing.
Q: Do I need Redis?
No. Current authentik uses PostgreSQL LISTEN/NOTIFY for pub/sub and PostgreSQL for caching via django-postgres-cache. PostgreSQL is the sole stateful dependency.
Q: How many users can authentik handle?
With the Docker Compose default (single server + worker, bundled PostgreSQL) you can serve thousands of users comfortably. For tens of thousands, switch to Kubernetes Helm with replicaCount > 1 and an external PostgreSQL with read replicas. authentik scales horizontally — both server and worker are stateless except for the embedded outpost.
Q: Can authentik replace Okta / Entra ID / Auth0? For most SAML and OIDC use cases, yes. For B2C at internet scale (millions of users) you'll want to tune PostgreSQL aggressively. For specific Okta features like AD agent or feature flags, you'd need to build them — authentik covers the protocol surface but Okta has a lot of integration glue you'd reimplement.
Q: How do passwordless / passkey / WebAuthn work?
Configure the authenticator_webauthn stage and bind it as the only required MFA in your flow. Skip the password stage entirely (or make it conditional on absence of WebAuthn). The user enrolls a passkey once, then logs in with just username + passkey.
Q: Can I use authentik with a custom domain per tenant? Yes — that's exactly what Brands are for. Create one Brand per domain. Each brand can override the default flows, branding, and metadata.
Q: Does authentik support SCIM both ways? Yes. As a provider (push users to Slack/Zoom/etc.) and as a source (Okta pushes users to you). Both implement SCIM 2.0 with PATCH semantics.
Q: Where does authentik store sessions?
PostgreSQL. The session table is authentik_core_authenticatedsession. Cookies are HTTP-only, Secure (over HTTPS), with SameSite=Lax by default. The session-cookie age is configurable per brand.
Q: Can users self-register?
Yes — bind a user_write stage at the end of an enrollment flow and link to it from your identification stage. Be aware that without an Email or Captcha stage, you're exposing user creation publicly.
Q: How do I migrate from Keycloak / Authelia / Authentik?
There's no automated importer. Inventory your Realms → Brands, Clients → Providers, Users → exported user CSV (use ak loaddata), then test SSO with one app before flipping the cutover.
Q: Does authentik have an audit log? The Events system covers most of what you'd want: every login, model change, configuration change, impersonation, etc. The Enterprise audit log adds tamper-evidence, retention controls, and structured export.
Q: How do I export and import a single flow?
ak apply_blueprint --export > all.yaml, then hand-extract the flow's section. There's also an API endpoint: GET /api/v3/managed/blueprints/?model=authentik_flows.flow returns blueprint fragments per flow.
Q: Can authentik act as an OIDC client for other apps' authentication? No — authentik is an IdP, not an SP. To pull users from Google/GitHub/etc., use Sources, not Providers.
Q: How do I integrate with Cloudflare Access? Use a Proxy provider in "Forward auth (Cloudflare)" mode, then configure Cloudflare to call authentik's auth endpoint. Full recipe at docs.goauthentik.io/integrations/security/cloudflare-access.
Q: Where are the integration recipes?
goauthentik.io/integrations — the website/integrations/ directory in the source has a one-pager per supported third-party app (~150+ integrations as of 2026.8.0).
Q: How do I delete a user but keep their audit trail?
Use a user_delete stage in a custom flow, or Admin → Directory → Users → checkbox → "Disable" (preserves the user record but blocks login). Hard deletion removes the user from Events too, which usually isn't what you want.
Q: What's the relationship between a Flow and a Brand? A Brand has four "default flow" slots (authentication, invalidation, recovery, unenrollment). When a request hits that brand's domain, authentik uses those flows. The Flow itself is brand-agnostic — one flow can be the default for many brands.
Q: Can I run authentik behind Cloudflare?
Yes. Set AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS to include Cloudflare's IP ranges (or 0.0.0.0/0 if you trust your network), and verify X-Forwarded-For reaches authentik correctly.
Q: How do I rotate the signing certificate for OIDC? Generate a new certificate (Admin → System → Certificates → Generate or upload), then edit your OAuth provider and switch its "Signing Key" to the new one. Existing tokens signed with the old key stay valid until they expire; clients that use JWKS will refresh automatically.
Q: Does authentik support hardware-based MFA (Yubikey)? Yes — Yubikeys appear as WebAuthn devices when used in WebAuthn mode. Plug in, register via the authenticator-webauthn stage, done. Yubikey OTP (not WebAuthn) isn't separately supported.
21. Reference Tables
Container ports
| Port | Protocol | Used by |
|---|---|---|
| 9000 | HTTP | Server (web UI + API + embedded outpost) |
| 9443 | HTTPS | Server (web UI + API + embedded outpost) |
| 3389 | LDAP | LDAP outpost (StartTLS supported) |
| 6636 | LDAPS | LDAP outpost (TLS) |
| 1812 | RADIUS (UDP) | RADIUS outpost |
| 9300 | HTTP | Prometheus metrics (/metrics) |
| 9900 | HTTP | Debug endpoint (internal/debug/debug.go, pprof + dump_config; disabled in prod) |
Defaults from authentik/lib/default.yml lines 36-49.
Health endpoints
| Path | Returns |
|---|---|
/-/health/live/ |
200 if process is running |
/-/health/ready/ |
200 if DB + worker connected and migrations done |
/-/metrics/ |
Prometheus format (on port :9300 by default) |
Useful env vars (AUTHENTIK_* namespace)
All keys in default.yml are env-overridable. Nesting uses __. Examples:
| Env var | What it sets |
|---|---|
AUTHENTIK_SECRET_KEY |
Django secret key (required) |
AUTHENTIK_POSTGRESQL__HOST |
PostgreSQL host |
AUTHENTIK_POSTGRESQL__PORT |
PostgreSQL port |
AUTHENTIK_POSTGRESQL__NAME |
DB name |
AUTHENTIK_POSTGRESQL__USER |
DB user |
AUTHENTIK_POSTGRESQL__PASSWORD |
DB password (also via env://POSTGRES_PASSWORD) |
AUTHENTIK_POSTGRESQL__SSLMODE |
require, prefer, disable |
AUTHENTIK_LISTEN__HTTP |
[::]:9000 |
AUTHENTIK_LISTEN__HTTPS |
[::]:9443 |
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS |
comma-separated CIDR list |
AUTHENTIK_LOG_LEVEL |
debug, info, warning, error |
AUTHENTIK_ERROR_REPORTING__ENABLED |
true enables Sentry |
AUTHENTIK_EMAIL__HOST |
SMTP host |
AUTHENTIK_DISABLE_UPDATE_CHECK |
true for air-gapped |
AUTHENTIK_DISABLE_STARTUP_ANALYTICS |
true for air-gapped |
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE |
Override outpost image prefix (e.g. for internal registry) |
AUTHENTIK_OUTPOSTS__DISCOVER |
true (default) to auto-discover Docker/Kubernetes |
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST |
true to disable the in-server proxy |
AUTHENTIK_BLUEPRINTS_DIR |
Path to mounted blueprints (default /blueprints) |
AUTHENTIK_CERT_DISCOVERY_DIR |
Path watched for external certs (default /certs) |
AUTHENTIK_WEB__WORKERS |
Gunicorn worker processes (default 2) |
AUTHENTIK_WEB__THREADS |
Threads per worker (default 4) |
AUTHENTIK_WORKER__PROCESSES |
Dramatiq worker processes (default 1) |
AUTHENTIK_WORKER__THREADS |
Threads per worker (default 2) |
AUTHENTIK_COMPLIANCE__FIPS__ENABLED |
true for FIPS mode |
AUTHENTIK_TENANTS__ENABLED |
true for multi-tenant (different from Brands) |
AUTHENTIK_GEOIP |
Override /geoip/GeoLite2-City.mmdb path |
For the complete keyspace, run docker compose exec server cat /etc/authentik/config.yml after install — every default is enumerated.
Default flow slugs (in blueprints/default/)
| Slug | Purpose |
|---|---|
default-authentication-flow |
Login |
default-invalidation-flow |
Logout |
default-password-change-flow |
Password change |
default-recovery-flow |
Forgot password |
default-source-enrollment-flow |
First federated login → create local user |
default-source-authentication-flow |
Subsequent federated login |
default-source-pre-authentication-flow |
Pre-source decisions (e.g. terms of service) |
default-provider-authorization-implicit-consent |
Auto-allow OAuth consent |
default-provider-authorization-explicit-consent |
Show OAuth scope screen |
default-provider-invalidation-flow |
Logout for OIDC RP-initiated logout |
default-user-settings-flow |
/if/user/#/settings page |
default-user-settings-flow (MFA tab) |
Enroll/manage MFA devices |
default-flow-pre-authentication |
Run before any other authentication (e.g. brand-level CAPTCHA) |
Key file paths inside the container
| Path | Contains |
|---|---|
/etc/authentik/config.yml |
Merged effective config (read-only, regenerated on start) |
/blueprints/ |
Mount your custom YAML here |
/data/ |
Uploaded files (icons, backgrounds, CSV exports) |
/certs/ |
Externally-discovered cert pairs (<name>.crt + <name>.key) |
/templates/ |
Custom email templates |
/geoip/ |
GeoLite2-City.mmdb + GeoLite2-ASN.mmdb |
/lifecycle/ |
Startup scripts and migrations |
Where to learn more
- Official docs: docs.goauthentik.io
- Integration recipes: goauthentik.io/integrations
- Discord: goauthentik.io/discord
- GitHub Issues: github.com/goauthentik/authentik/issues
- Release notes: docs.goauthentik.io/docs/releases
- Developer Documentation: docs.goauthentik.io/docs/developer-docs
- Helm chart: github.com/goauthentik/helm
For an architectural deep-dive — request flow, code layout, the outpost wire protocol, blueprint internals, performance tuning, and security posture — see the Admin & Developer Guide.