Skip to main content

Self-Host Authentik: Identity Provider and SSO 2026

·OSSAlt Team
authentikssoidentity-provideroauth2self-hostingdocker2026

TL;DR

Authentik (MIT, ~14K GitHub stars, Python/Django) is a comprehensive self-hosted identity provider. It provides SSO for all your self-hosted apps via OAuth2/OIDC, SAML 2.0, and a proxy authentication mode that protects apps that don't natively support authentication. Okta charges $2/user/month; Auth0 charges $23/month for 1,000 users. Authentik self-hosted is free for unlimited users and supports every major authentication protocol.

Key Takeaways

  • Authentik: MIT, ~14K stars, Python — IdP with OAuth2, OIDC, SAML 2.0, LDAP, and proxy auth
  • Proxy provider: Protect any app (even those without auth) behind Authentik's login
  • Flows: Visual policy engine — customize login, enrollment, recovery, and MFA flows
  • Outposts: Lightweight proxy components deployed next to apps for proxy authentication
  • LDAP provider: Expose users as LDAP directory for legacy apps
  • MFA: TOTP, WebAuthn (hardware keys), FIDO2 — all built in

Authentik vs Authelia vs Keycloak

FeatureAuthentikAutheliaKeycloak
Admin UIExcellentBasicExcellent
Proxy authYes (Outposts)Yes (built-in)No
OAuth2/OIDC ProviderYesNoYes
SAML ProviderYesNoYes
LDAP ProviderYesNoYes
Flow customizationVisual editorConfig fileLimited UI
RAM usage~500MB~50MB~512MB+
Setup complexityMediumSimpleComplex
User managementFull UIConfig fileFull UI

Part 1: Docker Setup

# 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}"]
      interval: 5s
      start_period: 20s
    volumes:
      - authentik_db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: "${PG_PASS}"
      POSTGRES_USER: authentik
      POSTGRES_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: 5s
    volumes:
      - authentik_redis:/data

  server:
    image: ghcr.io/goauthentik/server:latest
    restart: unless-stopped
    command: server
    environment: &env
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: "${PG_PASS}"
      AUTHENTIK_SECRET_KEY: "${AUTHENTIK_SECRET_KEY}"
      AUTHENTIK_EMAIL__HOST: mail.yourdomain.com
      AUTHENTIK_EMAIL__PORT: 587
      AUTHENTIK_EMAIL__USERNAME: authentik@yourdomain.com
      AUTHENTIK_EMAIL__PASSWORD: "${MAIL_PASSWORD}"
      AUTHENTIK_EMAIL__USE_TLS: "true"
      AUTHENTIK_EMAIL__FROM: authentik@yourdomain.com
    volumes:
      - authentik_media:/media
      - authentik_templates:/templates
    ports:
      - "9000:9000"
      - "9443:9443"
    depends_on:
      - postgresql
      - redis

  worker:
    image: ghcr.io/goauthentik/server:latest
    restart: unless-stopped
    command: worker
    environment: *env
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - authentik_media:/media
      - authentik_certs:/certs
      - authentik_templates:/templates
    depends_on:
      - postgresql
      - redis

volumes:
  authentik_db:
  authentik_redis:
  authentik_media:
  authentik_certs:
  authentik_templates:
# Generate secrets:
echo "PG_PASS=$(openssl rand -base64 36)" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60)" >> .env

docker compose up -d

# Get initial admin password from logs (first run):
docker compose logs server | grep "Initial admin credentials"

Part 2: HTTPS with Caddy

auth.yourdomain.com {
    reverse_proxy localhost:9000
}

Visit https://auth.yourdomain.com/if/flow/initial-setup/ → set admin password.


Part 3: OAuth2/OIDC Provider (Protect an App)

Protect an app that supports OAuth2/OIDC natively (Gitea, Nextcloud, Grafana, etc.):

Create an OAuth2 Provider

  1. Applications → Providers → Create → OAuth2/OpenID Provider
  2. Name: Gitea
  3. Authorization flow: default-provider-authorization-explicit-consent
  4. Client type: Confidential
  5. Copy: Client ID and Client Secret
  6. Redirect URIs: https://gitea.yourdomain.com/user/oauth2/authentik/callback
  7. Scopes: openid, email, profile

Create an Application

  1. Applications → Applications → Create
  2. Name: Gitea
  3. Provider: select Gitea (the provider you just created)
  4. Launch URL: https://gitea.yourdomain.com

Configure Gitea

# app.ini
[oauth2]
ENABLED = true

# Add via Gitea admin UI → Site Administration → Authentication Sources → Add
# Provider: OpenID Connect
# Client ID: from Authentik
# Client Secret: from Authentik
# Auto Discovery URL: https://auth.yourdomain.com/application/o/gitea/.well-known/openid-configuration

Part 4: Proxy Provider (Protect Any App)

The proxy provider protects apps that have no built-in authentication:

How it works

Browser → Caddy → Authentik Outpost (proxy) → Your App
                  ↑ checks session with Authentik server

Create a Proxy Provider

  1. Applications → Providers → Create → Proxy Provider
  2. Name: Portainer
  3. Mode: Forward auth (single application)
  4. External host: https://portainer.yourdomain.com
  5. Internal host: http://portainer:9000
  6. Skip path regex (optional): /api/webhooks/.*

Create an Outpost

  1. Applications → Outposts → Create
  2. Name: proxy-outpost
  3. Type: Proxy
  4. Integration: Docker (uses the Docker socket)
  5. Applications: select all apps using proxy providers

Authentik automatically deploys the outpost container.

Configure Caddy for forward auth

portainer.yourdomain.com {
    forward_auth localhost:9000 {
        uri /outpost.goauthentik.io/auth/caddy
        copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email
        trusted_proxies private_ranges
    }
    reverse_proxy localhost:9000
}

Part 5: MFA Configuration

Enable TOTP (Authenticator apps)

  1. Flows & Stages → Stages → Create → Authenticator TOTP Stage
  2. Name: totp-setup
  3. Flows → default-authentication-flow → Edit
  4. Add stage binding: totp-setup after password stage

Enable WebAuthn (Hardware keys / biometrics)

  1. Flows & Stages → Stages → Create → Authenticator WebAuthn Stage
  2. Name: webauthn-setup
  3. Add to authentication flow

Require MFA for specific groups

# In your authentication flow, add a Policy Binding:
# - Expression policy that checks group membership:
return request.user.ak_groups.filter(name="Admins").exists()

Part 6: LDAP Provider

Expose Authentik users as an LDAP directory for legacy apps (Synology, older software):

  1. Applications → Providers → Create → LDAP Provider
  2. Name: ldap
  3. Bind DN: cn=admin,dc=yourdomain,dc=com
  4. Certificate: select your TLS cert

Create LDAP Outpost

  1. Applications → Outposts → Create
  2. Type: LDAP
  3. Port mapping: 389:3389 (LDAP), 636:6636 (LDAPS)

Test LDAP connection

ldapsearch -H ldap://localhost:389 \
  -D "cn=admin,dc=yourdomain,dc=com" \
  -w "admin-password" \
  -b "dc=yourdomain,dc=com" \
  "(objectClass=person)"

Part 7: User Enrollment and Invitation

Self-registration flow

  1. Flows & Stages → Flows → Create
  2. Designation: Enrollment
  3. Stages: Email verification → User write → Username field → Password field
  4. Assign as: default-user-settings-flow

Create single-use or multi-use invitation links:

  1. Directory → Invitations → Create
  2. Expires: optional date
  3. Copy invitation link → send to new user

Disable public registration

# In Authentik admin:
# Flows → default-user-settings-flow → Edit
# Remove "Public" from Flow permissions
# Or: Policies → Expression Policy → "return False" → bind to enrollment flow

Part 8: Backup and Restore

# Backup database:
docker exec authentik-postgresql-1 pg_dump -U authentik authentik \
  | gzip > authentik-db-$(date +%Y%m%d).sql.gz

# Backup media (custom logos, etc.):
tar -czf authentik-media-$(date +%Y%m%d).tar.gz \
  $(docker volume inspect authentik_authentik_media --format '{{.Mountpoint}}')

# Export configuration as YAML blueprint:
docker exec authentik-server-1 ak export_blueprint > authentik-blueprint-$(date +%Y%m%d).yaml

# Restore database:
gunzip < authentik-db-YYYYMMDD.sql.gz | \
  docker exec -i authentik-postgresql-1 psql -U authentik authentik

# Logs:
docker compose logs -f server worker

See also: Authelia — simpler authentication middleware with lower resource requirements

See all open source security tools at OSSAlt.com/categories/security.

Comments