If you’ve been running a homelab for any length of time, you know the authentication problem well. You’ve got Portainer over here, Nextcloud over there, Grafana on another port, maybe Gitea or Vaultwarden somewhere else — and every single one of them has its own username and password. Managing those credentials becomes a headache fast, and if you’re anything like me, you either reuse passwords (bad) or forget half of them within a week (worse).
The enterprise answer to this is Single Sign-On (SSO) — one identity provider that handles authentication for every service. And thanks to Authentik, that enterprise-grade solution is now completely available for self-hosters.
Authentik is an open-source identity provider built for exactly this use case. It supports OAuth2, OpenID Connect (OIDC), SAML, LDAP, and RADIUS — so it can authenticate against pretty much any service you’d want to run in a homelab. It’s written in Python/Django and Go, ships as a Docker image, and has a genuinely polished web UI that makes configuration much less painful than alternatives like Keycloak.
In this guide, I’ll walk you through a complete Authentik deployment on Docker, then connect it to several common homelab services so you can see exactly how the integration works.
What You’ll Need
- A Linux host running Docker and Docker Compose (if you haven’t set that up yet, check out our complete Docker beginner’s guide)
- A domain name or local DNS entry pointing to your server (Authentik needs a stable hostname)
- At least 2GB of RAM — Authentik’s worker and server processes are reasonably hungry
- A reverse proxy (Nginx Proxy Manager, Traefik, or Caddy) — we’ll cover the proxy integration
For this guide I’m using a Proxmox VM with Ubuntu 24.04 LTS, but the steps are the same on bare metal or any other VM platform.
Understanding Authentik’s Architecture
Before diving into the YAML, it helps to understand what Authentik actually runs. There are two main services:
- authentik-server: The Django application that handles the web UI and authentication flows.
- authentik-worker: A Celery-based worker that handles background tasks like email sending, outpost health checks, and flow executor jobs.
Both services share the same Docker image and require a PostgreSQL database and Redis instance. Redis is used as the message broker for Celery and for session caching.
There’s also the concept of an Outpost in Authentik. An outpost is a separate process (also a Docker container) that proxies requests and enforces authentication in front of services that don’t natively support OAuth2. Think of it as a sidecar auth layer — traffic hits the outpost first, gets authenticated, then gets forwarded to your actual service. This is how you can add SSO to apps that have no native OAuth2 support.
Step 1: Generate Secrets and Environment Variables
First, generate the secrets Authentik needs:
# Generate a secure secret key
python3 -c "import secrets; print(secrets.token_urlsafe(50))"
# Generate a strong PostgreSQL password
python3 -c "import secrets; print(secrets.token_hex(24))"
Save both of those outputs — you’ll use them in the next step. Never commit these to version control.
Create a working directory for your Authentik stack:
mkdir -p ~/docker/authentik/{media,certs,custom-templates}
cd ~/docker/authentik
Step 2: The Docker Compose File
Create docker-compose.yml in your ~/docker/authentik directory:
version: "3.8"
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:?database password required}
POSTGRES_USER: ${PG_USER:-authentik}
POSTGRES_DB: ${PG_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"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- redis:/data
server:
image: ghcr.io/goauthentik/server:2025.10.0
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_EMAIL__HOST: ${EMAIL_HOST:-localhost}
AUTHENTIK_EMAIL__PORT: ${EMAIL_PORT:-25}
AUTHENTIK_EMAIL__FROM: ${EMAIL_FROM:-authentik@example.com}
volumes:
- ./media:/media
- ./custom-templates:/templates
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgresql
- redis
worker:
image: ghcr.io/goauthentik/server:2025.10.0
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
- ./media:/media
- ./certs:/certs
- ./custom-templates:/templates
depends_on:
- postgresql
- redis
volumes:
database:
driver: local
redis:
driver: local
The worker gets access to the Docker socket because it manages outpost containers — this is optional if you’re running outposts manually or as separate Compose services. Check the Authentik releases page and replace 2025.10.0 with the latest stable version before deploying.
Now create the .env file (in the same directory):
PG_PASS=your_postgres_password_here
PG_USER=authentik
PG_DB=authentik
AUTHENTIK_SECRET_KEY=your_secret_key_here
AUTHENTIK_EMAIL__HOST=smtp.example.com
AUTHENTIK_EMAIL__PORT=587
AUTHENTIK_EMAIL__FROM=authentik@yourdomain.com
Substitute your generated values for your_postgres_password_here and your_secret_key_here.
Step 3: Start Authentik
docker compose up -d
docker compose logs -f server
On first startup, Authentik will run database migrations — this takes 30-60 seconds. Watch the logs until you see something like:
authentik.server | INFO Starting on :9000 (HTTP) and :9443 (HTTPS)
Now navigate to http://your-server-ip:9000/if/flow/initial-setup/ to complete the initial setup. You’ll create the first admin user here — use a strong password and save it somewhere secure.
Step 4: Initial Configuration in the Admin UI
Once logged in, head to the Admin Interface (the gear icon, or navigate to /if/admin/). A few things to configure before connecting any services:
Set Your Default Domain
Go to System > Settings and set the Default domain to your server’s hostname or IP (e.g., auth.yourdomain.com). This is used in redirect URIs and email links.
Create a Test User
Navigate to Directory > Users and create a user account for yourself. Add it to the authentik Admins group if you want admin access. You’ll use this account to test SSO logins.
Review the Default Flows
Authentik ships with pre-built flows for login, logout, enrollment, and password recovery. For most homelab setups, the defaults work fine. If you want email verification or MFA, those get layered in as flow stages — but that’s an advanced topic for another day.
Step 5: Connect Nextcloud via OIDC
Let’s walk through a real integration. Nextcloud supports OpenID Connect natively through the Social Login app, so this is a good first integration to verify everything is working.
In Authentik: Create a Provider
- Go to Applications > Providers → Create
- Choose OAuth2/OpenID Provider
- Name:
Nextcloud - Authorization flow:
default-provider-authorization-implicit-consent - Client type: Confidential
- Copy the auto-generated Client ID and Client Secret — you’ll need these in Nextcloud
- Redirect URIs:
https://nextcloud.yourdomain.com/apps/social_login/custom_oidc/Authentik - Scopes: leave defaults (openid, email, profile)
- Save
In Authentik: Create an Application
- Go to Applications > Applications → Create
- Name:
Nextcloud, Slug:nextcloud - Provider: select the one you just created
- Launch URL:
https://nextcloud.yourdomain.com - Save
In Nextcloud: Install and Configure Social Login
# Install the Social Login app
occ app:install sociallogin
# Or install it from the Nextcloud app store in the web UI
Then in Nextcloud Settings → Social Login, add a Custom OpenID Connect provider:
- Name:
Authentik - Title:
Login with Authentik - Authorize URL:
https://auth.yourdomain.com/application/o/nextcloud/authorize/ - Token URL:
https://auth.yourdomain.com/application/o/token/ - User info URL:
https://auth.yourdomain.com/application/o/userinfo/ - Client ID and Secret: from the Authentik provider you created
- Scope:
openid email profile - User ID claim:
sub
Save the settings, log out of Nextcloud, and you should now see a “Login with Authentik” button on the login page. Your Authentik user will be able to sign in directly.
Step 6: Proxy Authentication with the Authentik Outpost
Some homelab services don’t support OAuth2 at all. For these, Authentik provides a Proxy Provider and an outpost that sits in front of the service and handles authentication for it. This is incredibly useful for things like Portainer, older Grafana instances, or any service that only supports basic HTTP auth.
Create a Proxy Provider
- Go to Applications > Providers → Create → Proxy Provider
- Name:
Portainer Proxy - Authorization flow:
default-provider-authorization-implicit-consent - Mode: Forward auth (single application)
- External host:
https://portainer.yourdomain.com - Save, then create an Application pointing to this provider
Deploy the Outpost
Go to Applications > Outposts → Create. Choose type Proxy, give it a name, and select your Portainer Proxy provider. Authentik will either manage the outpost container automatically (if the worker has Docker socket access) or give you a Docker Compose snippet to run manually.
The outpost listens on port 9000 internally. In your reverse proxy, you’ll route portainer.yourdomain.com to the outpost instead of directly to Portainer. The outpost handles the Authentik auth challenge, then forwards authenticated requests to the real Portainer container.
With Tailscale running on your homelab hosts, this setup lets you add a proper auth layer to any service without that service even needing internet exposure — the SSO flow happens over your Tailscale network.
Step 7: Enforce MFA for Extra Security
Authentik makes adding TOTP (Google Authenticator, Authy, etc.) multi-factor authentication straightforward. Navigate to Flows and Stages > Stages and you’ll find a pre-built default-authentication-totp-setup stage. To enforce it:
- Create a new stage of type Authenticator Validation Stage
- Set “Not Configured Action” to Deny (forces enrollment) or Configure (prompts first-time setup)
- Add this stage to the
default-authentication-flowafter the password stage
With MFA enforced at the Authentik level, every service behind SSO gets MFA protection — even ones that have no native MFA support of their own. That’s a significant security win for your homelab.
Step 8: LDAP Outpost for Legacy Applications
Some homelab services only speak LDAP — things like older NAS units, some monitoring tools, or custom internal applications. Authentik handles this too. Deploy an LDAP outpost and your Authentik user directory becomes an LDAP directory that any LDAP-compatible service can query.
# Test your LDAP outpost is working
ldapsearch -H ldap://your-server-ip:389 \
-x \
-D "cn=ldapservice,ou=serviceaccounts,dc=ldap,dc=goauthentik,dc=io" \
-w "your-service-account-password" \
-b "dc=ldap,dc=goauthentik,dc=io" \
"(objectClass=user)"
Configure the bind DN to a dedicated service account user in Authentik, and you’ll see all your Authentik users returned as LDAP entries. This pairs nicely with monitoring setups where you want central auth for tools like Grafana or LibreNMS — both support LDAP login out of the box.
Troubleshooting Common Issues
Authentik is generally reliable, but a few issues come up regularly when first setting it up.
Redirect URI Mismatch
The most common OAuth2 error is a redirect URI mismatch. Authentik is strict about these — the URI your application sends must exactly match one configured in the provider. Check for trailing slashes, HTTP vs HTTPS differences, and make sure the full path is correct. The error log at Events > System Tasks will show the mismatched URI so you can copy it exactly.
Worker Not Starting
If the worker container exits immediately, it’s usually a permissions issue with the Docker socket or with the media directory. Check docker compose logs worker — if you see a socket permission error, run:
sudo chmod 666 /var/run/docker.sock
In production, it’s better to add your user to the docker group than to chmod the socket globally, but for a homelab this gets you running quickly.
Session Expiry Too Aggressive
By default Authentik sessions expire after a relatively short period. If users are getting logged out too frequently across all their SSO’d services, go to System > Settings and increase the token duration. Alternatively, configure individual providers with longer token lifetimes — this is preferable if you want different expiry windows for different applications.
Outpost Can’t Reach Authentik
Outpost containers communicate with the Authentik server using a WebSocket connection. If your outpost shows as offline in the admin UI, check that the outpost can reach the Authentik server on port 9443. On Docker networks, use the container name as the hostname — the outpost’s AUTHENTIK_HOST environment variable should point to https://server:9443 if both containers are on the same network.
Maintenance and Backups
Keep Authentik healthy with a few operational habits:
Database Backups
# Dump the PostgreSQL database
docker compose exec postgresql pg_dump -U authentik authentik > ~/backups/authentik_$(date +%Y%m%d).sql
# Restore if needed
docker compose exec -T postgresql psql -U authentik authentik < ~/backups/authentik_YYYYMMDD.sql
Updating Authentik
# Pull the new image
docker compose pull
# Restart with zero-downtime (worker first, then server)
docker compose up -d worker
docker compose up -d server
Authentik generally handles database migrations automatically on startup, but check the release notes for any version that involves major schema changes before upgrading across multiple versions at once.
Health Monitoring
Authentik exposes a health endpoint at /healthz/. If you're running Uptime Kuma, add a monitor for this endpoint — it will alert you if the server becomes unhealthy before your users do.
Wrapping Up
Authentik transforms how authentication works across a self-hosted stack. Instead of managing separate credentials for every service, you maintain a single user directory and grant access through Authentik's flow-based system. New services get SSO support in about five minutes. Users get a consistent login experience. And you get the ability to enforce MFA or disable a user account in one place and have it propagate everywhere immediately.
The initial setup investment — maybe two to three hours for a thorough deployment — pays dividends every day after that. Once you've got Nextcloud, Grafana, Portainer, and your other services all behind Authentik, you'll wonder how you ran a homelab without it.
The official Authentik documentation is genuinely excellent and covers the more advanced topics like custom branding, policy enforcement, external LDAP sources, and SAML providers. If you're running a local AI setup with Ollama or any other service that exposes a web UI, Authentik is a clean way to add a proper auth layer in front of it without modifying the service itself.
Got questions or running into issues with a specific integration? Drop a comment below — I check in on them regularly and the Authentik community on GitHub and Discord is also very active.