As companies scale, fragmented login systems create avoidable risk: inconsistent MFA, manual onboarding, weak audit trails, and delayed offboarding. This guide shows how to deploy Authentik with Docker Compose and Traefik in a production-ready pattern.
The objective is dependable operations: clear secret boundaries, HTTPS by default, observable runtime checks, and lifecycle practices that survive real incidents.
Identity is a platform dependency, not just an app feature. Assign ownership across platform, security, and support teams so incident routing is clear when login error rates spike. Keep a runbook for certificate renewal failures, session anomalies, and dependency outages.
Change discipline matters: use staging promotion for every auth policy change, capture before/after verification evidence, and avoid ad-hoc edits directly on production hosts. This lowers blast radius and makes post-incident analysis much faster.
Architecture/flow overview
The stack runs Authentik server and worker, with PostgreSQL for persistent state and Redis for queue/ephemeral coordination. Traefik handles TLS termination and routes only the identity host to the Authentik web service.
This separation narrows public exposure and simplifies troubleshooting. Edge, app, and data concerns can be validated independently under pressure.
- Edge: Traefik + ACME certificates
- App: Authentik server/UI/API
- Worker: asynchronous tasks and policy execution
- Data: PostgreSQL + Redis
For compliance-heavy environments, map this deployment to concrete controls: retention periods, privileged-access approvals, MFA exception handling, and periodic review of dormant accounts. The technical stack is only half the solution; governance completes it.
Prerequisites
- Linux host with Docker Engine and Compose plugin
- DNS record for
auth.example.compointed to Traefik host - Traefik with resolver
letsencrypt - At least 2 vCPU, 4 GB RAM, SSD storage
- Defined owner for patching, backups, and on-call support
Verify NTP synchronization and outbound connectivity before rollout. Authentication tokens and certificate issuance are both sensitive to time/network drift.
Change discipline matters: use staging promotion for every auth policy change, capture before/after verification evidence, and avoid ad-hoc edits directly on production hosts. This lowers blast radius and makes post-incident analysis much faster.
Step-by-step deployment
1) Prepare folders and secrets
Separate compose, secrets, and data paths from day one. This keeps audits, backup jobs, and troubleshooting deterministic.
mkdir -p /opt/authentik/{compose,secrets,postgres,redis,backups}
cd /opt/authentik/compose
umask 077
touch ../secrets/postgres_password.txt ../secrets/authentik_secret_key.txt
openssl rand -base64 32 > ../secrets/postgres_password.txt
openssl rand -base64 64 > ../secrets/authentik_secret_key.txt
chmod 600 ../secrets/*.txt
If the copy button does not work in your browser, manually select the code block and copy it.
2) Define environment values
Keep non-sensitive values in environment files while loading credentials through secret files. Never hard-code secrets in versioned compose manifests.
# /opt/authentik/compose/.env
TZ=UTC
PG_DB=authentik
PG_USER=authentik
AUTHENTIK_HOST=auth.example.com
[email protected]
If the copy button does not work in your browser, manually select the code block and copy it.
3) Deploy compose stack with Traefik labels
Attach Authentik server to the external Traefik network and route only the intended host. Keep database and cache internal.
version: "3.9"
services:
postgresql:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${PG_DB}
POSTGRES_USER: ${PG_USER}
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
volumes: ["../postgres:/var/lib/postgresql/data"]
secrets: [postgres_password]
redis:
image: redis:7-alpine
restart: unless-stopped
server:
image: ghcr.io/goauthentik/server:2026.2
restart: unless-stopped
env_file: .env
command: server
labels:
- traefik.enable=true
- traefik.http.routers.authentik.rule=Host(`${AUTHENTIK_HOST}`)
- traefik.http.routers.authentik.entrypoints=websecure
- traefik.http.routers.authentik.tls.certresolver=letsencrypt
- traefik.http.services.authentik.loadbalancer.server.port=9000
- traefik.docker.network=traefik-public
networks: [default, traefik-public]
worker:
image: ghcr.io/goauthentik/server:2026.2
restart: unless-stopped
env_file: .env
command: worker
secrets:
postgres_password: {file: ../secrets/postgres_password.txt}
networks:
traefik-public: {external: true}
If the copy button does not work in your browser, manually select the code block and copy it.
4) Launch and inspect first boot
Validate compose syntax before startup, then watch logs during migrations. Most first-run failures are hostname, TLS, or dependency-order issues.
docker network create traefik-public || true
docker compose config
docker compose up -d
docker compose logs -f --tail=100 server worker
If the copy button does not work in your browser, manually select the code block and copy it.
Identity is a platform dependency, not just an app feature. Assign ownership across platform, security, and support teams so incident routing is clear when login error rates spike. Keep a runbook for certificate renewal failures, session anomalies, and dependency outages.
Configuration and secrets handling
Rotate secrets quarterly, enforce least privilege for host access, and require peer review for auth-policy changes. Limit administrative access to a small trusted group and log every privileged action.
Roll out application integrations gradually: one pilot application, then broader expansion after real-user validation.
For compliance-heavy environments, map this deployment to concrete controls: retention periods, privileged-access approvals, MFA exception handling, and periodic review of dormant accounts. The technical stack is only half the solution; governance completes it.
Verification
Use both infrastructure checks and functional checks (actual user login, MFA flow, audit log entries). Verification must be reproducible by any on-call engineer.
curl -I https://auth.example.com/
docker compose ps
docker compose exec -T postgresql psql -U authentik -d authentik -c "select now();"
docker compose logs --tail=80 server | grep -Ei "ready|error|exception"
If the copy button does not work in your browser, manually select the code block and copy it.
Document expected outputs for each check so responders can quickly distinguish degradation from normal variance.
Common issues and fixes
TLS certificate does not issue
Check DNS target, open ports 80/443, and ACME resolver names in Traefik labels.
Service is up but page is unreachable
Confirm host rule and external network attachment for the Authentik server container.
Background jobs are delayed
Validate Redis reachability and consistent environment values for server and worker services.
Intermittent login loops
Inspect redirect URIs and host clock skew.
Database growth accelerates
Tune retention policy and monitoring thresholds before storage pressure occurs.
Change discipline matters: use staging promotion for every auth policy change, capture before/after verification evidence, and avoid ad-hoc edits directly on production hosts. This lowers blast radius and makes post-incident analysis much faster.
Backups and lifecycle operations
Backups without restore drills are assumptions. Automate backups and test restoration in staging on a fixed cadence.
cat >/usr/local/bin/authentik-backup.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F-%H%M)
OUT=/opt/authentik/backups/pg-${STAMP}.sql.gz
docker compose -f /opt/authentik/compose/docker-compose.yml exec -T postgresql pg_dump -U authentik authentik | gzip > "$OUT"
EOF
chmod +x /usr/local/bin/authentik-backup.sh
( crontab -l 2>/dev/null; echo "15 2 * * * /usr/local/bin/authentik-backup.sh" ) | crontab -
If the copy button does not work in your browser, manually select the code block and copy it.
Before upgrades, capture an on-demand backup, pin image versions, and smoke-test critical authentication paths after deployment.
FAQ
1) Is Redis optional in production?
No. Keep Redis for stable asynchronous behavior and queue processing.
2) Can I expose Authentik directly instead of Traefik?
Not recommended. A reverse proxy provides consistent TLS and edge policy controls.
3) How often should secrets be rotated?
Quarterly baseline, plus immediate rotation after suspected exposure.
4) What is a minimum backup standard?
Daily backup, retention policy, and scheduled restore drill.
5) Can this architecture scale later?
Yes. Start simple, then scale app layer and harden data services.
6) Which alerts should we prioritize first?
TLS renewal failures, login error spikes, worker queue lag, DB latency, and restart storms.
7) Should we separate staging and production identity?
Yes. Separate domains and secrets reduce accidental cross-environment impact.
Internal links
- https://sysbrix.com/blog/guides-3/production-guide-deploy-freshrss-with-docker-compose-traefik-and-postgresql-on-ubuntu-291
- https://sysbrix.com/blog/guides-3/production-guide-deploy-kestra-with-docker-compose-nginx-postgresql-on-ubuntu-290
- https://sysbrix.com/blog/guides-3/how-to-deploy-vaultwarden-with-docker-compose-and-traefik-production-guide-282
Talk to us
If you want this implemented with hardened defaults, observability, and tested recovery playbooks, our team can help.
Copy-code helper
If JS is allowed, this snippet enables one-click copy. If Odoo strips scripts, manual-copy fallback remains under each code block.
<script>
(function(){
const buttons=document.querySelectorAll('.copy-code');
buttons.forEach(btn=>btn.addEventListener('click',async()=>{
const code=btn.parentElement.querySelector('code'); if(!code)return;
try{await navigator.clipboard.writeText(code.innerText);btn.innerText='✅ Copied';}
catch(e){alert('Clipboard blocked. Please copy manually.');}
}));
})();
</script>
If the copy button does not work in your browser, manually select the code block and copy it.