Self-Hosted Authentik SSO Setup Guide
A complete guide to setting up Authentik as a self-hosted Single Sign-On (SSO) and OAuth2/OIDC identity provider with YubiKey MFA support.
Table of Contents
- Overview
- Prerequisites
- Installation
- Reverse Proxy Configuration
- Initial Setup
- YubiKey MFA Configuration
- Integration with Applications
- Troubleshooting
Overview
What is Authentik?
Authentik is a powerful, open-source identity provider (IdP) that provides:
- Single Sign-On (SSO) for multiple applications
- OAuth2 and OIDC provider capabilities
- Multi-Factor Authentication (MFA) including WebAuthn/FIDO2 for hardware keys
- User management with flows and policies
- LDAP and SAML support
Use Cases:
- Centralized authentication for self-hosted services
- Replace cloud providers like Auth0 or Okta
- Hardware security key (YubiKey) authentication
- Fine-grained access control with policies
Prerequisites
Required Infrastructure
- Linux server (VPS or homelab) with Docker
- Reverse proxy (Traefik, Caddy, nginx, or Pangolin)
- Domain name with DNS configured (e.g.,
auth.example.com) - SSL/TLS certificates (handled by reverse proxy)
Required Software
- Docker and Docker Compose
- Container orchestration tool (Dockge, Portainer, or docker-compose CLI)
System Requirements
- RAM: Minimum 1GB, recommended 2GB+
- Storage: ~500MB for containers, additional space for database
- CPU: 1 core minimum, 2+ recommended
Installation
Step 1: Prepare Directory Structure
Create the configuration directory:
# Using /opt/configs as the base path
mkdir -p /opt/configs/authentik/{database,redis,media,certs,custom-templates}
Note: If you use a different base path, adjust the CONFIG_PATH environment variable accordingly.
Step 2: Generate Secret Key
Generate a secure random secret key for Authentik:
openssl rand -base64 60
Save this key - you’ll need it in the next step.
Step 3: Create Docker Compose File
Create /opt/stacks/authentik/compose.yaml (or use your container orchestration tool):
services:
postgresql:
image: docker.io/library/postgres:16-alpine
container_name: authentik-db
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:
- ${CONFIG_PATH}/authentik/database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: ${PG_USER:-authentik}
POSTGRES_DB: ${PG_DB:-authentik}
networks:
- authentik-internal
redis:
image: docker.io/library/redis:alpine
container_name: authentik-redis
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:
- ${CONFIG_PATH}/authentik/redis:/data
networks:
- authentik-internal
server:
image: ghcr.io/goauthentik/server:2024.12.1
container_name: authentik
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"
AUTHENTIK_COOKIE_DOMAIN: ${AUTHENTIK_DOMAIN:-auth.example.com}
volumes:
- ${CONFIG_PATH}/authentik/media:/media
- ${CONFIG_PATH}/authentik/custom-templates:/templates
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgresql
- redis
networks:
- authentik-internal
- web # External network for reverse proxy
worker:
image: ghcr.io/goauthentik/server:2024.12.1
container_name: authentik-worker
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}
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${CONFIG_PATH}/authentik/media:/media
- ${CONFIG_PATH}/authentik/certs:/certs
- ${CONFIG_PATH}/authentik/custom-templates:/templates
depends_on:
- postgresql
- redis
networks:
- authentik-internal
networks:
authentik-internal:
web:
external: true # Reverse proxy network
Step 4: Create Environment File
Create a .env file in the same directory:
# Config directory path
CONFIG_PATH=/opt/configs
# PostgreSQL credentials
PG_PASS=your-secure-postgres-password-here
PG_USER=authentik
PG_DB=authentik
# Authentik configuration
AUTHENTIK_SECRET_KEY=paste-your-generated-secret-key-here
AUTHENTIK_DOMAIN=auth.example.com
Important: Replace the placeholder values with:
- A strong PostgreSQL password
- The secret key you generated in Step 2
- Your actual domain name
Step 5: Start Authentik
cd /opt/stacks/authentik
docker compose up -d
Watch the logs to ensure migrations complete:
docker logs authentik-worker -f
You should see database migrations running. This is normal on first startup and may take 2-5 minutes. Wait for them to complete.
Reverse Proxy Configuration
Authentik requires a reverse proxy to handle SSL/TLS termination. Here are examples for common reverse proxies.
Pangolin
If using Pangolin reverse proxy:
- Create a new resource in Pangolin
- Configure:
- Domain:
auth.example.com - Target Address:
authentik(container name) - Target Port:
9000 - Authentication: Disabled (Authentik handles its own auth)
- SSL/HTTPS: Enabled
- Domain:
Important: The authentik container must be on the same Docker network as Pangolin (typically called web).
Traefik (File Provider)
Add to your dynamic_config.yml:
http:
routers:
authentik:
rule: "Host(`auth.example.com`)"
service: authentik
entryPoints:
- websecure
tls:
certResolver: letsencrypt
services:
authentik:
loadBalancer:
servers:
- url: "http://authentik:9000"
Traefik (Docker Provider)
Add labels to the server container:
server:
# ... existing config ...
labels:
- "traefik.enable=true"
- "traefik.http.routers.authentik.rule=Host(`auth.example.com`)"
- "traefik.http.routers.authentik.entrypoints=websecure"
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
Nginx
server {
listen 443 ssl http2;
server_name auth.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Caddy
auth.example.com {
reverse_proxy authentik:9000
}
Initial Setup
Step 1: Access Initial Setup Flow
Navigate to: https://auth.example.com/if/flow/initial-setup/
Note: The path /if/flow/initial-setup/ is important - don’t forget it!
Step 2: Create Admin Account
Fill in the form:
- Email: Your email address
- Username: Choose an admin username (e.g.,
adminor your name) - Password: Strong password (you can change this later or disable password auth after setting up YubiKey)
Click Continue
Step 3: Verify Installation
After creating the admin account, you’ll be logged in. You should see the Authentik admin interface.
Navigate through:
- Dashboard: Overview of your Authentik instance
- Directory → Users: Shows your admin user
- Applications: Empty for now (you’ll add apps here later)
YubiKey MFA Configuration
Step 1: Navigate to User Settings
- Click your username in the top right
- Select Settings
Step 2: Enroll First YubiKey
- Scroll to the MFA Devices section
- Click Enroll new device
- Select WebAuthn device (not TOTP)
- WebAuthn/FIDO2 is the modern standard for hardware security keys
- TOTP is for app-based authenticators like Google Authenticator
- Give it a friendly name (e.g., “YubiKey 5 - Primary”)
- Click Enroll
- Your browser will prompt you to insert and touch your YubiKey
- Touch the YubiKey when it blinks
Step 3: Enroll Backup YubiKey (Recommended)
Repeat the enrollment process for a second YubiKey:
- Enroll new device → WebAuthn device
- Name it (e.g., “YubiKey 5 - Backup”)
- Touch the second YubiKey when prompted
Why a backup? If you lose or break your primary YubiKey and haven’t enrolled a backup, you could be locked out.
Step 4: Make MFA Mandatory (Optional)
Warning: Only do this after successfully testing MFA login with your YubiKey!
-
Navigate to Flows & Stages
-
Find default-authentication-flow
-
Click the ▶ (play/execute) icon to view stage bindings
-
You’ll see stages:
- Order 10: Identification
- Order 20: Password
- Order 30: MFA Validation
- Order 100: User Login
-
Click on default-authentication-mfa-validation stage (Order 30)
-
Click Edit Stage (not Edit Binding)
-
In the stage configuration:
- Device classes: Ensure “WebAuthn” is selected
- Other settings can remain default
-
Click Update
-
To make MFA required (not optional):
- Click Edit Binding on the MFA Validation stage
- Change Order to ensure it runs after password
- Set Required to true if available, or
- Create a policy that requires MFA for all users
Step 5: Test MFA Login
Before making MFA mandatory, test it works:
- Log out of Authentik
- Log back in with username and password
- You should be prompted for MFA
- Insert your YubiKey and touch it
- You should be logged in successfully
If this works, you can safely make MFA mandatory.
Integration with Applications
Creating an OAuth2/OIDC Provider
Authentik can provide SSO for other applications using OAuth2/OIDC. Here’s how:
Step 1: Create Provider
- Navigate to Applications → Providers
- Click Create
- Select OAuth2/OpenID Provider
- Configure:
- Name: Application name (e.g., “Netbird”)
- Authentication flow:
default-authentication-flow - Authorization flow:
default-provider-authorization-explicit-consent - Client type:
Confidential - Redirect URIs/Origins (RegEx):
https://app.example.com/oauth2/callback https://app.example.com/auth - Signing Key:
authentik Self-signed Certificate
- Click Create
- Copy the Client ID and Client Secret - you’ll need these for your application
Step 2: Create Application
- Navigate to Applications → Applications
- Click Create
- Configure:
- Name: Application name
- Slug:
app-name(lowercase, no spaces) - Provider: Select the provider you just created
- Click Create
Step 3: Configure Your Application
In your application’s settings, configure OAuth/OIDC:
- Provider Type: Generic OIDC or Authentik (if available)
- Issuer URL:
https://auth.example.com/application/o/app-name/ - Client ID: From Step 1
- Client Secret: From Step 1
- Scopes:
openid profile email
Note: The Issuer URL format is important - it must end with /application/o/<slug>/
Example: Integrating with Netbird
# Netbird configuration
IdpManagerConfig:
ManagerType: "authentik"
ClientConfig:
Issuer: "https://auth.example.com/application/o/netbird/"
ClientID: "your-client-id"
ClientSecret: "your-client-secret"
RedirectURLs:
- "https://netbird.example.com/oauth2/callback"
Scopes:
- "openid"
- "profile"
- "email"
Troubleshooting
Container Startup Issues
Migrations taking a long time:
- This is normal on first startup (2-5 minutes)
- Watch logs:
docker logs authentik-worker -f - Migrations should complete and show “OK”
Containers won’t start:
# Check all containers
docker ps -a | grep authentik
# View logs
docker logs authentik
docker logs authentik-worker
docker logs authentik-db
docker logs authentik-redis
Reverse Proxy Issues
502 Bad Gateway:
- Verify Authentik is running:
docker ps | grep authentik - Test locally:
curl http://localhost:9000/-/health/live/- Should return:
{"status": "ok"}
- Should return:
- If using containerized reverse proxy:
- Ensure both are on the same Docker network
- Use container name instead of
localhost - Example:
http://authentik:9000instead ofhttp://localhost:9000
SSL/Certificate errors:
- Verify your reverse proxy SSL configuration
- Check DNS points to correct server
- Test with
curl -v https://auth.example.com
Authentication Issues
Can’t access initial setup:
- Ensure you’re accessing:
https://auth.example.com/if/flow/initial-setup/ - Check reverse proxy is forwarding correctly
- Verify
AUTHENTIK_COOKIE_DOMAINmatches your domain
Locked out after enabling MFA:
- If you didn’t enroll a backup device, you’ll need to reset
- Access PostgreSQL database and disable MFA:
docker exec -it authentik-db psql -U authentik authentik - Better: Always enroll 2+ YubiKeys before making MFA mandatory
YubiKey not recognized:
- Ensure browser supports WebAuthn (Chrome, Firefox, Edge do)
- Check YubiKey is inserted properly
- Try a different USB port
- Test YubiKey at https://demo.yubico.com/webauthn
Network Configuration
Containers can’t communicate:
# Verify network exists
docker network ls | grep web
# Inspect network
docker network inspect web
# Verify authentik is on the network
docker network inspect web | grep authentik
Fix: Container not on network
# Stop stack
docker compose down
# Update compose.yaml to include web network
# See Installation section for correct configuration
# Restart
docker compose up -d
Advanced Configuration
Custom Flows
Authentik uses “flows” to control authentication, authorization, and enrollment processes. You can customize these:
- Navigate to Flows & Stages
- Duplicate existing flows to create custom versions
- Add/remove stages to modify behavior
- Bind policies to stages for conditional logic
User Self-Service Registration
Enable users to create their own accounts:
- Navigate to Flows & Stages → Flows
- Find
default-source-enrollmentflow - Click Edit
- Configure stages to collect user information
- Set Create users as inactive if you want admin approval
- Share enrollment URL:
https://auth.example.com/if/flow/default-source-enrollment/
LDAP Support
Authentik can act as an LDAP server:
- Navigate to Applications → Outposts
- Create LDAP outpost
- Bind to users and groups
- Configure client applications to use Authentik LDAP
SAML Provider
For applications requiring SAML instead of OIDC:
- Navigate to Applications → Providers
- Create SAML Provider
- Configure metadata URL
- Set ACS URL and audience from your application
Security Best Practices
- Use strong passwords for PostgreSQL and Authentik admin
- Enable MFA for all users, especially administrators
- Enroll multiple YubiKeys for redundancy
- Regular backups of
/opt/configs/authentik/database - Update regularly:
docker compose pull && docker compose up -d - Monitor logs for suspicious activity
- Use separate networks for internal communication
- Limit outpost exposure - only expose necessary ports
- Review policies regularly to ensure proper access control
- Test recovery procedures before you need them
Backup and Recovery
Backup
Critical data to backup:
- PostgreSQL database:
/opt/configs/authentik/database - Media files:
/opt/configs/authentik/media - Environment file:
.env(contains secrets!)
Backup command:
# Stop containers
docker compose down
# Create backup
tar -czf authentik-backup-$(date +%Y%m%d).tar.gz /opt/configs/authentik .env
# Restart
docker compose up -d
Recovery
# Stop existing installation
docker compose down
# Restore data
tar -xzf authentik-backup-YYYYMMDD.tar.gz -C /
# Start containers
docker compose up -d