Setting Up Authentik as an Identity Provider for Self-Hosted Apps
I used to manage passwords for a dozen self-hosted applications across my homelab—Nextcloud, Jellyfin, Gitea, Vaultwarden, Immich—and it was a nightmare. Every app had its own user database, password reset flows, and login page. Then I discovered Authentik, and it changed everything. Now I have a single identity provider that handles authentication for all my services, with proper OAuth2/OIDC support, passwordless login, and even multi-factor authentication. In this guide, I'll walk you through deploying Authentik and connecting your existing apps to it.
Why Authentik Instead of the Alternatives?
You've probably heard of Keycloak—it's more powerful but also heavier and overkill for most homelabs. I chose Authentik because it's purpose-built for self-hosters: lighter resource footprint, easier configuration, and great documentation. Unlike roll-your-own solutions, you get proper OAuth2/OIDC flows, which means your apps don't need to store passwords at all.
The key advantage is centralized identity management. Instead of resetting a password in five places when you change it, you change it once in Authentik and it propagates everywhere. You also get audit logs, session management, and the ability to implement policies—like requiring MFA only when accessing sensitive apps.
Prerequisites and Planning
You'll need a server with Docker and Docker Compose installed. I'm running this on a 2-core, 4GB RAM VPS from RackNerd (if you're looking for affordable hosting, RackNerd's KVM VPS is a solid choice), but Authentik runs fine on even modest hardware. You'll also need a reverse proxy—I use Caddy, but Nginx or Traefik work too. Finally, you need a domain or subdomain for Authentik itself (e.g., auth.example.com).
Authentik uses PostgreSQL as its database and Redis for caching. The full stack includes these components, which is why Docker Compose is perfect here.
Deploying Authentik with Docker Compose
Here's the production-ready Docker Compose file I use. This includes PostgreSQL, Redis, and Authentik itself, all properly networked and with persistent storage:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: authentik-postgres
environment:
POSTGRES_USER: authentik
POSTGRES_PASSWORD: ${PG_PASS}
POSTGRES_DB: authentik
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- authentik
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: authentik-redis
networks:
- authentik
restart: unless-stopped
authentik:
image: ghcr.io/goauthentik/authentik:2026.3
container_name: authentik
command: server
environment:
AUTHENTIK_SECRET_KEY: ${SECRET_KEY}
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_LOG_LEVEL: info
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgres
- redis
volumes:
- authentik_media:/media
networks:
- authentik
restart: unless-stopped
authentik-worker:
image: ghcr.io/goauthentik/authentik:2026.3
container_name: authentik-worker
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${SECRET_KEY}
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_REDIS__HOST: redis
depends_on:
- postgres
- redis
volumes:
- authentik_media:/media
networks:
- authentik
restart: unless-stopped
volumes:
postgres_data:
authentik_media:
networks:
authentik:
driver: bridge
Create a .env file with secure values:
SECRET_KEY=$(openssl rand -base64 32)
PG_PASS=$(openssl rand -base64 16)
echo "SECRET_KEY=$SECRET_KEY" > .env
echo "PG_PASS=$PG_PASS" >> .env
Then start the stack:
docker-compose up -d
Authentik runs on port 9000 internally. You'll need to expose it through your reverse proxy.
Exposing Authentik Through Your Reverse Proxy
I use Caddy. Here's my configuration:
auth.example.com {
reverse_proxy localhost:9000 {
header_uri X-Authentik-Upstream {http.reverse_proxy.upstream.hostport}
header_uri X-Script-Name /
}
}
If you're using Nginx Proxy Manager, just create a proxy host pointing to localhost:9000 and enable SSL. For Traefik with Docker Compose labels, add this to your Authentik service:
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"
Once that's running, navigate to https://auth.example.com and log in with the default credentials (admin/admin—change this immediately).
Creating an OAuth2 Application for Your First App
Now I'll show you how to connect an app. Let's use Nextcloud as an example. In the Authentik Admin Panel, go to Applications → Applications and click Create.
Fill in these details:
- Name: Nextcloud
- Slug: nextcloud
- Provider: (create a new OAuth2 provider)
For the provider, set:
- Name: Nextcloud OAuth2
- Client type: Confidential
- Redirect URIs:
https://nextcloud.example.com/apps/social_login/custom_oidc/callback - Token Validity: Hours (default is fine)
After creating the provider, copy the Client ID and Client Secret—you'll need them in Nextcloud.
Connecting Nextcloud to Authentik
In Nextcloud, install the "Social Login" app if you haven't already. Then SSH into your Nextcloud container and edit config/config.php to add:
'social_login' => [
'custom_oidc' => [
'client_id' => 'YOUR_CLIENT_ID_HERE',
'client_secret' => 'YOUR_CLIENT_SECRET_HERE',
'scope' => 'openid profile email',
'authorize_url' => 'https://auth.example.com/application/o/authorize/',
'access_token_url' => 'https://auth.example.com/application/o/token/',
'user_info_url' => 'https://auth.example.com/application/o/userinfo/',
'icon' => 'icon-external-white.png',
],
],
Nextcloud will now show a "Log in with Authentik" button on the login page. The first user to log in via Authentik will be auto-provisioned as long as the email matches an existing user or a new account is being created.
Mapping Users and Groups
By default, Authentik passes the user's email as the username. In Nextcloud, this might be awkward. You can customize what information Authentik sends using property mappings in the OAuth2 provider settings. Go to the provider and add a property mapping:
Scope Mapping: Create a new scope mapping with type "OpenID" and add custom claims. For example, to send the username instead of email, use:
preferred_username: user.username
name: user.name
email: user.email
groups: user.groups
This gives your applications full control over what user attributes they receive.
Adding Passwordless Login
One of Authentik's coolest features is passwordless authentication. In the Admin Panel, go to Flows and Stages, then create a new stage of type "Identification." Configure it to accept email and automatically redirect to a passwordless prompt. Your users can then log in by clicking a link sent to their email—no password needed.
To set this up, create a new Authentication Flow:
- Name: Default Authentication Flow
- Designation: Authentication
Add stages in order: Identification, Password (optional), and MFA (optional). Save and assign it to your applications. When users hit the login page, they can choose to authenticate with email verification instead of a password.
Securing Authentik in Production
A few critical hardening steps:
- Enable HTTPS only. Your reverse proxy should enforce TLS. Add this to your Caddy config:
@insecure { protocol http } redir @insecure https://{host}{uri} - Set AUTHENTIK_OUTPOSTS__DOCKER_TLS_VERIFICATION to false if using Docker outposts. Otherwise, keep it true.
- Enable audit logging. Check System → Audit Log for every login and admin action.
- Use strong database passwords. I generate mine with
openssl rand -base64 32. - Backup your database regularly. PostgreSQL backups are simple:
docker-compose exec postgres pg_dump -U authentik authentik > backup.sql
Connecting Other Apps
Once you've done Nextcloud, the pattern is identical for other apps. For Jellyfin, there's a plugin. For Gitea, use OAuth2 in Admin → Settings. For Vaultwarden, use the OIDC environment variables. Each app's OAuth2 integration is slightly different, but they all follow the same principle: create an application in Authentik, get the credentials, and plug them in.
Common apps I've successfully connected:
- Jellyfin: Use the JellyfinSSO plugin from the app store
- Gitea: Admin Panel → Settings → OAuth2 → Add source
- Vaultwarden: Use
OIDC_*environment variables - Immich: Settings → OAuth2 → OIDC URL is
https://auth.example.com/application/o/immich/.well-known/openid-configuration - Uptime Kuma: Settings → Reverse Proxy → Enable proxy authentication
Troubleshooting Common Issues
Blank login screen or CSS not loading: This usually means Authentik can't reach itself via HTTPS. Check that your domain resolves and your reverse proxy is configured correctly. In the Authentik logs, look for SSL verification errors.
OAuth2 callback fails with "Redirect URI mismatch": Ensure the redirect URL in Authentik exactly matches what your app is sending. Some apps add trailing slashes or port numbers—these must match byte-for-byte.
Users created but can't log in: Check that users have an email address set and that AUTHENTIK_OUTPOSTS__DOCKER is set correctly if using Docker outposts for forward auth.
Database connection refused: Make sure the PostgreSQL container has started and the password in your environment matches. Run docker-compose logs postgres to debug.
Next Steps
You now have a working identity provider. Next, I'd recommend