How to Set Up Authentik: SSO for Your Entire Self-Hosted Stack
Stop managing 12 separate logins. Set up Authentik to get single sign-on across all your self-hosted apps — Nextcloud, Grafana, Gitea, and more.
For the longest time, I had a password manager tab open 24/7 because every self-hosted app had its own login. Nextcloud. Grafana. Gitea. Uptime Kuma. Jellyfin. Each one a separate account, separate username, separate password. My Vaultwarden was starting to look like a phone book.
Then I set up Authentik, and suddenly everything clicked. One login. Every app. If you’ve built any kind of homelab with more than three services, this guide is for you.
What Even Is SSO?
Single Sign-On means you log in once, and all your apps just… work. You’ve already used it — when you click “Log in with Google” on some website, that’s SSO. Google verifies who you are, tells the website, done.
Authentik lets you run that same infrastructure yourself. You control the identity provider. Your credentials never leave your server. And you can add things like:
- Two-factor authentication (TOTP, WebAuthn) across all apps at once
- Groups and permissions — your family can access Jellyfin but not Grafana
- Audit logs — see every login, every failed attempt
I’ll be honest, I put this off for months because it sounded scary. “Identity Provider” has big Enterprise Energy. But the actual setup is maybe 45 minutes if you follow a clear guide. This is that guide.
What You’ll Need
- Docker and Docker Compose (you’ve got this already, right?)
- A domain with DNS pointing to your server —
auth.yourdomain.comis the conventional subdomain - A reverse proxy (Nginx Proxy Manager, Traefik — either works fine)
- About 2GB RAM free. Authentik runs Redis + PostgreSQL internally, so it’s a bit chunky compared to lightweight apps
I’m running this on a Hetzner CAX21 (4 vCPU, 8GB RAM, €7.49/month) and Authentik uses about 600MB at idle. Totally manageable.
Step 1: Create the Docker Compose File
First, let’s grab the official .env and docker-compose.yml from Authentik’s docs:
mkdir -p ~/authentik && cd ~/authentik
# Download the official compose file
curl -o docker-compose.yml https://goauthentik.io/docker-compose.yml
# Create .env with a secret key
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '=+/' | head -c 36)" > .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '=+/' | head -c 50)" >> .env
# Optional: set your email for error notifications
echo "[email protected]" >> .env
Take a look at the .env file and verify the values got written correctly:
cat .env
Should look something like:
PG_PASS=someLongRandomString
AUTHENTIK_SECRET_KEY=anotherEvenLongerRandomString
If either is empty, something went wrong with the openssl pipeline. Run those lines again manually.
Step 2: Start Authentik
docker compose up -d
First run will pull about 1.5GB of images. Go make coffee.
Once it’s up, check the logs:
docker compose logs -f server
Wait until you see something like:
INFO authentik.core Starting gunicorn WSGI app
Authentik is running. By default it exposes port 9000 (HTTP) and 9443 (HTTPS with self-signed cert).
Step 3: Initial Setup
Navigate to http://your-server-ip:9000/if/flow/initial-setup/ in your browser.
You’ll see a setup wizard asking for an admin email and password. Use something real for the email — Authentik sends you alerts when logins fail.
After setup, you’re dropped into the admin UI. It’s dense. Don’t panic.
The main sections you’ll actually use:
- Directory → Users, Groups
- Applications → Where you define which apps use Authentik
- Providers → How apps authenticate (OAuth2, SAML, LDAP, RADIUS, Proxy)
- Flows → Login flows, enrollment flows, password resets
For our purposes, we’ll mostly touch Applications and Providers.
Step 4: Point a Domain at It
Before you start connecting apps, let’s put Authentik behind a proper domain with HTTPS. Without this, some OAuth2 redirects won’t work.
If you’re using Nginx Proxy Manager, add a new proxy host:
- Domain Names:
auth.yourdomain.com - Scheme:
http - Forward Hostname/IP:
authentik-server(container name) or your server IP - Forward Port:
9000 - Enable SSL: Yes, Let’s Encrypt
If you’re using Traefik, add the standard labels to your Authentik compose service:
labels:
- "traefik.enable=true"
- "traefik.http.routers.authentik.rule=Host(`auth.yourdomain.com`)"
- "traefik.http.routers.authentik.entrypoints=websecure"
- "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
After DNS propagates (can be instant, can be 30 minutes), visit https://auth.yourdomain.com and confirm it loads.
Step 5: Connect Your First App — The Proxy Provider Way
Authentik has multiple ways to integrate with apps. The simplest by far is the Proxy Provider — you don’t need to change anything in the app itself. Authentik sits in front and handles auth.
This approach works brilliantly for apps that don’t support OAuth2 natively — things like Grafana OSS, Portainer, or your custom tools.
In Authentik Admin UI:
Go to Applications → Providers → Create
Choose Proxy Provider and fill in:
- Name:
grafana-proxy - Authorization flow:
default-provider-authorization-implicit-consent(for internal apps, this skips the “allow access?” prompt) - Type: Forward auth (single application)
- External host:
https://grafana.yourdomain.com
Hit Save.
Then go to Applications → Applications → Create:
- Name:
Grafana - Slug:
grafana - Provider: Select the
grafana-proxyyou just made
Now the app exists in Authentik — but nothing is connected yet until you wire up your reverse proxy.
In Nginx Proxy Manager, on the Grafana proxy host:
Go to Advanced tab and add:
auth_request /outpost.goauthentik.io/auth/nginx;
error_page 401 = @goauthentik_proxy_signin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
auth_request_set $authentik_username $upstream_http_x_authentik_username;
auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
location @goauthentik_proxy_signin {
internal;
add_header Set-Cookie $auth_cookie;
return 302 /outpost.goauthentik.io/start?rd=$request_uri;
}
And add a new proxy host for the Authentik outpost (the component that handles redirects):
- Domain:
grafana.yourdomain.com - Add a location block pointing to the Authentik outpost at port
9000
I know — the outpost setup is the fiddly bit. The official docs on proxy providers have exact copy-paste configs for both Nginx and Traefik. I’d recommend opening those alongside this guide.
Step 6: Connect Apps Natively with OAuth2
For apps that support OAuth2 natively — like Nextcloud, Gitea, Grafana (paid), or Jellyfin — native integration is cleaner. The user experience is better (real “Log in with SSO” button) and you get more detailed audit logs.
Create an OAuth2/OpenID Provider in Authentik:
Go to Providers → Create → OAuth2/OpenID Provider:
- Name:
nextcloud-oauth - Client type:
Confidential - Redirect URIs:
https://nextcloud.yourdomain.com/apps/sociallogin/custom_oidc/Authentik - Scopes:
openid,email,profile
After saving, Authentik shows you a Client ID and Client Secret. Copy these — you’ll need them in Nextcloud.
In Nextcloud:
Install the Social Login app from the Nextcloud app store. Then go to Settings → Social Login and add a Custom OpenID Connect provider:
- Name:
Authentik - Authorize URL:
https://auth.yourdomain.com/application/o/nextcloud-oauth/authorize/ - Token URL:
https://auth.yourdomain.com/application/o/nextcloud-oauth/token/ - User info URL:
https://auth.yourdomain.com/application/o/nextcloud-oauth/userinfo/ - Client ID: (from Authentik)
- Client Secret: (from Authentik)
Save. On your next Nextcloud login page, you’ll see “Login with Authentik.”
The pattern repeats for every app: create a provider in Authentik, grab the client credentials, configure the app. Each app has slightly different field names but the concept is identical.
Step 7: Set Up Groups and Access Control
This is where Authentik gets genuinely useful. I have two groups set up:
- admins — me, full access to everything
- family — my partner, access to Jellyfin, Nextcloud, and Immich only
Create groups under Directory → Groups.
Restrict app access per group: Go to your Application in Authentik, edit it, and under Policy/Group/User Bindings, add a binding that requires membership in a specific group.
Now anyone who’s not in that group gets a friendly “You don’t have access” page instead of the app’s login form. No leaking that the app even exists.
Step 8: Enable Two-Factor Auth
This is one click. Go to Directory → Users → [your user] and under MFA Devices, you can enroll a TOTP authenticator app.
For all users in a group, you can enforce MFA via Flows. Go to Flows → default-authentication-flow, click on the MFA stage, and set it to required.
Every new login will prompt for TOTP. I use this for admin access and it’s made me significantly less paranoid about my Grafana dashboards being exposed to the internet.
Connecting More Apps
Here’s the quick reference for apps I’ve personally connected:
| App | Method | Difficulty |
|---|---|---|
| Nextcloud | OAuth2 (Social Login app) | Medium |
| Grafana | OAuth2 | Easy |
| Gitea/Forgejo | OAuth2 | Easy |
| Jellyfin | LDAP or OIDC plugin | Medium |
| Portainer | LDAP or OAuth2 | Easy |
| Uptime Kuma | Proxy Provider | Easy |
| Paperless-ngx | Proxy Provider | Easy |
| Vaultwarden | Proxy Provider (or standalone) | Debatable* |
*I actually kept Vaultwarden with its own login even after setting up Authentik. If Authentik is down and I need a password… I need that password. Chicken-and-egg problem.
What I Wish I’d Known
The outpost configuration is the hardest part. Once you get that right for one app, the rest is copy-paste. Don’t get discouraged on the first one.
Container networking matters. If Authentik and your apps are on different Docker networks, they can’t communicate directly. Put everything on a shared proxy network:
networks:
proxy:
external: true
And reference it in each app’s compose file.
HTTPS everywhere before you start. OAuth2 redirects won’t work over plain HTTP. Get your SSL certs sorted first. I learned this the hard way after 20 minutes of debugging redirect_uri_mismatch errors.
Backup your PostgreSQL database. Authentik stores all your app configs, user accounts, and policies there. If it goes poof, you start from scratch. Add it to your backup routine.
Hardening Authentik for the Paranoid
Once Authentik is working, here are a few things I’d recommend doing:
Limit failed logins. Under Flows → default-authentication-flow, add a User Reputation Policy. It tracks failed logins by IP and username. After N failures, the flow blocks further attempts for a cooldown period. Set it to 5 attempts.
Set session timeouts. By default, Authentik sessions last a long time. Under Tenants → [your tenant] → default-authentication-flow, you can set session expiry. I use 12h for general users and 4h for admin.
Configure branding. This sounds cosmetic but has a real purpose — if you customize the login page with your own domain and logo, users will immediately notice if they land on a fake login page (phishing). Go to Tenants → default → Branding and add your domain name.
Watch the audit log. Events → Logs shows every authentication event. I have a bookmark to this page and glance at it weekly. Any login from an unexpected IP or at an odd hour stands out immediately.
Use WebAuthn if you can. TOTP (Google Authenticator etc.) is fine, but WebAuthn (YubiKey, passkeys, your phone’s fingerprint reader) is phishing-proof. Authentik supports it natively. Under MFA Devices, you can enroll a WebAuthn authenticator alongside TOTP as a backup.
Troubleshooting Common Issues
Things that tripped me up and wasted hours:
Redirect loops after login — Almost always a misconfigured External host in the proxy provider. Double-check it matches exactly what’s in your reverse proxy config, including whether there’s a trailing slash.
“Invalid redirect_uri” OAuth2 error — The redirect URI in Authentik must match character-for-character what the app sends. No trailing slashes, exact protocol (https vs http), exact port. Copy-paste directly from the app’s OAuth config page.
Outpost not responding — Check that the Authentik outpost container (authentik-worker) is running and on the same Docker network as your reverse proxy. Proxy providers won’t work if the outpost can’t be reached by Nginx/Traefik.
Session not persisting across apps — This usually means your apps are on different subdomains without a shared cookie domain. In Authentik → Tenants, set the domain to .yourdomain.com (with the leading dot) to allow cookies across all subdomains.
“Flow not applicable” — This error appears when the Flow conditions don’t match the current request context. Usually it means you assigned a login flow intended for direct users to an OAuth2 application. Create a separate “provider authorization” flow for OAuth2 apps.
Is It Worth the Setup Time?
If you have 5+ services? Absolutely yes. The initial setup takes a few hours. But after that:
- No more separate accounts per app
- MFA everywhere for free
- Kick someone out of all your apps in one click (family changes, shared homelabs, etc.)
- Every login attempt is logged
If you have 2-3 apps? Probably not worth it. Just use Bitwarden/Vaultwarden and move on.
FAQ
Does Authentik work if I’m offline? Yes. Authentik runs entirely on your server. No external calls needed after initial setup.
Can I use Authentik with a VPN-only homelab (no public exposure)?
Yes. You don’t need a public domain. You can use a local domain like auth.local if all your devices are on the same network or VPN.
What happens if Authentik goes down? Any app configured with proxy provider becomes inaccessible. Apps with native OAuth2 may still be accessible if they cache sessions locally. This is why I keep some apps with their own login as a backup. Plan your uptime accordingly.
Authentik vs Authelia — which is better? I’ve tried both. Authentik is more feature-rich but heavier. Authelia is lighter and simpler, but has fewer integrations. If you just want basic forward auth, Authelia is great. If you want full OAuth2/SAML support and a nice admin UI, Authentik wins.
Is Authentik free? The self-hosted version (Community Edition) is fully free and open source. There’s an Enterprise tier for companies that need support contracts, but for homelab use you’ll never need it.
Authentik is one of those tools I wish I’d set up from day one instead of retrofitting it into an existing stack. Start it early, configure apps as you add them, and future-you will thank past-you every morning when they log in once and everything just works.
Got questions? Hit me up — I’ve gone through enough OAuth2 headaches that I can probably help.
Stay in the loop 📬
Get self-hosting tutorials, tool reviews, and infrastructure tips delivered to your inbox. No spam, unsubscribe anytime.
Join 0 self-hosters. Free forever.