Quick Start: Deploy Authentik in 5 Minutes
Managing isolated credentials across dozens of internal dashboards, staging environments, and customer portals creates password sprawl. It also burns engineering hours every time a team member leaves. Authentik solves this by acting as a single Identity Provider (IdP) supporting OpenID Connect (OIDC), SAML 2.0, forward proxy authentication, and embedded LDAP outposts.
For a team of up to 500 active users, a modest VPS with 2 vCPUs and 4 GB of RAM handles the entire Authentik stack comfortably. Here is how to get it running on Ubuntu or Debian using Docker Compose.
1. Prepare Directory & Environment Variables
mkdir -p /opt/authentik && cd /opt/authentik
# Generate secret keys and database credentials
PG_PASS=$(openssl rand -base64 36 | tr -d '\n')
AUTH_SECRET=$(openssl rand -base64 60 | tr -d '\n')
cat <<EOF > .env
PG_PASS=${PG_PASS}
AUTHENTIK_SECRET_KEY=${AUTH_SECRET}
AUTHENTIK_ERROR_REPORTING__ENABLED=false
AUTHENTIK_PORT_HTTP=9000
AUTHENTIK_PORT_HTTPS=9443
EOF
Avoid basic command-line generators or untrusted online tools for root database credentials and admin service tokens. For sensitive production secrets, I use the password generator at toolcraft.app/en/tools/security/password-generator. It runs client-side in your browser without sending strings across the network. That makes creating 64-character high-entropy secrets both fast and safe.
2. Compose File Definition
# docker-compose.yml
services:
postgresql:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
volumes:
- database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
env_file:
- .env
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"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- redis:/data
server:
image: ghcr.io/goauthentik/server:2024.8.3
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
volumes:
- ./media:/media
- ./custom-templates:/templates
env_file:
- .env
ports:
- "${AUTHENTIK_PORT_HTTP}:9000"
- "${AUTHENTIK_PORT_HTTPS}:9443"
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
worker:
image: ghcr.io/goauthentik/server:2024.8.3
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
volumes:
- ./media:/media
- ./custom-templates:/templates
- /var/run/docker.sock:/var/run/docker.sock
env_file:
- .env
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
database:
driver: local
redis:
driver: local
3. Launch & Initial Bootstrap
docker compose pull
docker compose up -d
Open your browser and navigate to http://<your-server-ip>:9000/if/flow/initial-setup/. This setup wizard prompts you to configure the primary administrator account (akadmin) and set a master recovery password.
Architecture, Flows, and Core Mechanisms
The Problem: Fragmented Authentication
Engineering teams frequently run a mixed environment. Modern tools like Grafana and Nextcloud run right beside legacy self-hosted utilities. When every tool maintains its own local user database, account synchronization fails. Employees leave the company, but their access to internal dashboards often stays active for weeks unnoticed.
The Underlying Bottleneck
Different tools require different authentication protocols. Modern cloud apps expect JSON Web Tokens over OIDC. Enterprise suites require SAML 2.0 assertions. Older network storage devices or VPN appliances only support direct LDAP binds. Authentik acts as a translation layer between all three.
The Authentik Execution Pipeline
Authentik processes every login attempt through three modular concepts:
- Stages: Single action steps, such as asking for a password, issuing a WebAuthn prompt, or sending an email verification code.
- Flows: An ordered sequence of stages that run during authentication, enrollment, or recovery.
- Bindings & Policies: Rules that decide if a stage or application is accessible. You can enforce rules based on group membership, IP subnets, or GeoIP locations.
flowchart LR
User[Client Browser] -->|1. Request Auth| App[Service / App]
App -->|2. Redirect / OIDC Auth| Authentik[Authentik IdP]
subgraph Pipeline [Authentik Pipeline Engine]
Authentik --> Flow[Authentication Flow]
Flow --> MFA[MFA Stage Policy]
MFA --> ClaimMapper[Scope / Claim Mapping]
end
ClaimMapper -->|3. JWT / Tokens Issued| App
App -->|4. Authenticated Session| User
OIDC Integration, Enforced MFA, and Outposts
1. Connecting an Application (Grafana via OIDC)
First, create an OAuth2/OIDC Provider in the Authentik Admin Interface under Applications > Providers > Create:
- Name: Grafana OIDC
- Authorization Flow: default-provider-authorization-implicit-consent
- Client Type: Confidential
- Redirect URI:
https://grafana.example.com/login/generic_oauth
Next, map the credentials inside your grafana.ini configuration file:
[auth.generic_oauth]
enabled = true
name = Authentik
client_id = YOUR_AUTHENTIK_CLIENT_ID
client_secret = YOUR_AUTHENTIK_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' || 'Editor'
2. Enforcing Dynamic MFA via Flow Policies
You can enforce hardware security keys (FIDO2/WebAuthn) or TOTP apps for sensitive user roles without disrupting standard users:
- Go to Flows and Stages > Flows > default-authentication-flow.
- Select your Authenticator Validation Stage.
- Attach an execution policy using the following Python expression:
# Python Policy in Authentik
# Requires MFA if the user is in 'Infrastructure-Ops' or connects from outside the 10.0.0.0/8 office subnet
user_groups = [group.name for group in request.user.ak_groups.all()]
is_admin_group = "Infrastructure-Ops" in user_groups
is_external_ip = not request.http_request.META.get("REMOTE_ADDR", "").startswith("10.0.")
return is_admin_group or is_external_ip
3. Connecting Legacy Systems via LDAP Outpost
Older network hardware and legacy apps often lack modern OAuth support. An Authentik LDAP Outpost bridges this gap. It queries Authentik’s core API in the background while listening on standard LDAP ports for legacy clients.
# Deploy the standalone LDAP outpost container
docker run -d \
--name authentik-ldap-outpost \
--restart unless-stopped \
-p 389:3389 -p 636:6636 \
-e AUTHENTIK_HOST=https://auth.example.com \
-e AUTHENTIK_TOKEN=your_outpost_service_token \
-e AUTHENTIK_INSECURE=false \
ghcr.io/goauthentik/ldap:2024.8.3
Troubleshooting & Production Hardening
- Reverse Proxy Headers: If you use Nginx, Traefik, or Caddy, forward
X-Forwarded-Proto $schemeandX-Forwarded-For $proxy_add_x_forwarded_for. Missing headers cause redirect loops and invalid OAuth state errors. - Clock Drift: Keep host clocks synchronized using
chronyorsystemd-timesyncd. A clock drift as small as 15 seconds will cause TOTP validation failures and premature JWT token expirations. - Automated Database Snapshots: Authentik stores all flow definitions and tokens in Postgres. Back up your database every 24 hours with a simple cron task:
# Production database backup snapshot
docker compose exec -T postgresql pg_dump -U authentik authentik | gzip > /opt/backups/authentik_$(date +%F_%T).sql.gz

