Meta description: Deploy Authelia on Ubuntu with Docker Compose and Traefik ForwardAuth, including secure config, SSO setup, verification, and troubleshooting.
If your team runs multiple internal web tools, authentication sprawl eventually becomes an operational risk: inconsistent MFA policies, duplicate user stores, and emergency changes that are hard to audit. This guide implements a production-ready pattern using Authelia as centralized access control with Traefik ForwardAuth in front of your applications. You get one policy layer, consistent sign-in flows, and a clearer incident response path.
Real-world use case: a platform team hosts Grafana, internal admin panels, runbooks, CI utilities, and lightweight business tools. Each app has different auth quality and update cadence. Rather than trust app-by-app security settings, the team introduces a stable identity gateway and enforces access rules by domain and user group.
Architecture and flow overview
- Traefik receives HTTPS traffic and routes by hostname.
- Protected routes call Authelia ForwardAuth before upstream app handling.
- Authelia evaluates session state, 2FA requirements, and rule matching.
- If allowed, Traefik forwards request to app with identity headers.
- If denied, user is redirected to Authelia sign-in portal.
This model decouples application logic from identity enforcement. Your app teams move faster because auth policy changes happen in one place, and your security team gains predictability across the whole self-hosted estate.
Prerequisites
- Ubuntu 22.04+ server with static IP
- DNS records for
auth.example.comand protected app domains - Network policy allowing inbound ports 80/443
- Ownership for identity policy lifecycle, 2FA rollouts, and break-glass access
- Secure storage for generated secrets and encrypted off-host backups
Before go-live, define who approves policy changes, who can execute emergency bypass, and how each access change is reviewed after incidents. Operations clarity prevents outage chaos later.
sudo apt update && sudo apt -y upgrade
sudo apt -y install ca-certificates curl gnupg lsb-release jq ufw
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update && sudo apt -y install docker-ce docker-ce-cli containerd.io docker-compose-pluginIf the copy button does not work in your browser, manually copy from the code block above.
mkdir -p ~/authelia-prod/{authelia,traefik,dynamic,secrets,backups}
cd ~/authelia-prod
openssl rand -base64 48 > secrets/session_secret.txt
openssl rand -base64 48 > secrets/storage_encryption_key.txt
chmod 600 secrets/*.txtIf the copy button does not work in your browser, manually copy from the code block above.
Step-by-step deployment
1) Build the baseline stack
Deploy Traefik and Authelia in one Compose project. Keep Docker socket read-only and avoid exposing internal control ports externally.
version: "3.9"
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports: ["80:80", "443:443"]
command:
- --providers.docker=true
- --providers.file.directory=/dynamic
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- [email protected]
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./dynamic:/dynamic
- ./traefik:/letsencrypt
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
volumes:
- ./authelia:/config
- ./secrets:/secrets:ro
labels:
- traefik.enable=true
- traefik.http.routers.authelia.rule=Host(`auth.example.com`)
- traefik.http.routers.authelia.entrypoints=websecure
- traefik.http.routers.authelia.tls.certresolver=le
- traefik.http.services.authelia.loadbalancer.server.port=9091If the copy button does not work in your browser, manually copy from the code block above.
2) Configure Authelia policies and storage
Start with default deny. Explicitly define who can access what and require two-factor where the risk justifies it. This protects you from accidental exposure when a new app route is introduced.
theme: auto
default_2fa_method: totp
log: { level: info }
server: { address: tcp://0.0.0.0:9091 }
authentication_backend:
file: { path: /config/users_database.yml }
access_control:
default_policy: deny
rules:
- domain: "*.example.com"
policy: two_factor
session:
secret: "{{ secret "/secrets/session_secret.txt" }}"
cookies:
- name: authelia_session
domain: example.com
authelia_url: https://auth.example.com
storage:
encryption_key: "{{ secret "/secrets/storage_encryption_key.txt" }}"
local: { path: /config/db.sqlite3 }
notifier:
filesystem: { filename: /config/notification.txt }If the copy button does not work in your browser, manually copy from the code block above.
3) Configure ForwardAuth middleware
Define middleware once and reuse across app routers. This gives consistent behavior and simpler auditing.
http:
middlewares:
authelia-forwardauth:
forwardAuth:
address: http://authelia:9091/api/authz/forward-auth
trustForwardHeader: true
authResponseHeaders: [Remote-User, Remote-Groups, Remote-Email, Remote-Name]If the copy button does not work in your browser, manually copy from the code block above.
4) Attach middleware to applications
Apply middleware labels to each protected service. Pilot with one low-risk app first, then expand to operationally critical systems.
services:
whoami:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.whoami.rule=Host(`app.example.com`)
- traefik.http.routers.whoami.entrypoints=websecure
- traefik.http.routers.whoami.tls.certresolver=le
- traefik.http.routers.whoami.middlewares=authelia-forwardauth@fileIf the copy button does not work in your browser, manually copy from the code block above.
5) Bring up stack and bootstrap users
Start containers, check logs, create initial user credentials, and verify sign-in flows before broad rollout.
cd ~/authelia-prod
docker compose up -d
docker compose logs -f --tail=100 authelia
docker exec -it authelia authelia crypto hash generate argon2 --password 'ChangeMeNow!'
# add hash to users_database.yml and reload containerIf the copy button does not work in your browser, manually copy from the code block above.
Configuration and secret-handling best practices
Most long-term failures are not installation issuesβthey are process failures: unmanaged policy growth, stale secrets, and undocumented emergency actions. Treat this deployment like platform code.
- Version-control all Authelia/Traefik configs with peer review.
- Store generated secrets outside repository and rotate on a fixed cadence.
- Separate high-privilege domains from standard internal apps.
- Require MFA for admin routes before expanding to general dashboards.
- Publish an access governance matrix that maps groups to domains.
For enterprise environments, map policy tiers (critical, standard, low-risk) to explicit controls: MFA strictness, session duration, and device trust behavior. This prevents one-size-fits-none policy drift.
Also define incident playbooks for identity outages: how to confirm scope, how to apply temporary emergency policy, how to revert safely, and how to run postmortem hardening. Teams that rehearse this recover faster and with fewer risky shortcuts.
Verification checklist
- Unauthenticated users are redirected to Authelia portal
- Authenticated users with matching policy reach protected apps
- Users outside required groups receive deny response
- TLS certificates are valid and auto-renew
- Restart test passes without manual repair
- Audit-relevant logs are retained centrally
curl -I https://app.example.com
curl -s https://auth.example.com/api/health | jq
# Optional check for routers if dashboard/API is enabled:
# curl -s http://127.0.0.1:8080/api/http/routers | jqIf the copy button does not work in your browser, manually copy from the code block above.
Operational hardening and day-2 runbook
Production-readiness depends on day-2 controls, not just initial setup. Add a lightweight weekly routine: verify certificate expiry windows, check failed auth trends, review policy changes, and validate backup artifacts. Security tooling without operational rhythm degrades quickly.
Establish objective SLO-style indicators for identity gateway health: median auth response latency, successful login ratio, and policy-deny ratio by domain. Sudden shifts in these metrics usually reveal upstream DNS, proxy routing, or session-store regressions before users escalate tickets.
For change management, introduce phased rollouts:
- Phase 1: One non-critical app with a pilot team.
- Phase 2: Shared engineering tools and dashboards.
- Phase 3: Administrative and operational control planes.
At each phase, capture rollback conditions in plain language. Example: if login failure rate exceeds 3% for 10 minutes, revert last policy commit and restore known-good middleware config.
Run quarterly recovery drills: restore config backup to a staging node, rehydrate users, and verify that protected routes enforce expected policy. A backup that cannot be restored under time pressure is not a backup strategy.
Common issues and fixes
Redirect loops between app and auth portal
Usually cookie domain mismatch or wrong authelia_url. Ensure hostnames, scheme, and session domain are consistent.
401 responses for valid users
Check rule ordering. Broad deny rules above specific allow rules will block legitimate traffic.
Identity headers missing upstream
Confirm authResponseHeaders and ensure intermediate proxies are not stripping headers.
ACME certificate issuance failure
Validate port 80 reachability and DNS propagation. Temporary DNS mismatch often breaks initial issuance.
Frequent session invalidation
Session secrets changed or clock skew on host nodes. Keep secrets stable and NTP healthy.
Maintenance automation examples:
0 2 * * * /usr/bin/docker compose -f /home/ubuntu/authelia-prod/docker-compose.yml pull && /usr/bin/docker compose -f /home/ubuntu/authelia-prod/docker-compose.yml up -d
15 2 * * 0 /usr/bin/docker image prune -af --filter "until=168h"
30 2 * * * /usr/bin/tar -czf /home/ubuntu/authelia-prod/backups/authelia-config-$(date +\%F-\%H\%M).tar.gz /home/ubuntu/authelia-prod/authelia /home/ubuntu/authelia-prod/dynamicIf the copy button does not work in your browser, manually copy from the code block above.
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo apt -y install fail2banIf the copy button does not work in your browser, manually copy from the code block above.
FAQ
Can I use Authelia without Traefik?
Yes. It supports multiple reverse proxies. This guide selects Traefik for clean ForwardAuth integration in containerized stacks.
Do I need LDAP immediately?
No. File users are fine for small teams. Migrate to LDAP/IdP when user lifecycle management becomes operationally expensive.
Should default policy be deny or one_factor?
For production, default deny is safer. Explicit allows reduce accidental exposure and improve auditability.
What should I protect first?
Start with admin systems, observability, and deployment control planes. Expand after validation.
How do I roll policy updates safely?
Use staged rollout, clear rollback triggers, and peer-reviewed config changes with runbook references.
Which logs are essential during incidents?
Correlate Traefik access logs, Authelia decision logs, and application logs to isolate routing vs auth vs app failures quickly.
Related guides
Talk to us
Need help implementing a hardened authentication layer across your self-hosted stack? We can help with policy architecture, staged rollout, and production operations.