Traefik with Authelia SSO
Overview
Traefik with Authelia SSO is an edge appliance that terminates inbound HTTPS traffic, routes it to your backend services, and requires every visitor to sign in first. Traefik handles routing, load balancing and TLS. Authelia is the authentication and authorization server behind it. It provides a login portal, brute-force protection, optional two-factor authentication and a single session shared across every service behind the proxy.
This image wires the two together for you. Traefik holds each request at the edge and asks Authelia whether the caller has a valid session before the backend is reached. Callers without a session are redirected to the login portal. Callers with a session are proxied through, and their identity is passed upstream in request headers. That is single sign-on: one login, one session, every protected service.
What you get:
- Traefik 3.7.10 (a single static Go binary) at
/usr/local/bin/traefik - Authelia 4.39.20 from the official package, on loopback
127.0.0.1:9091and never exposed directly - The Authelia
forwardAuthmiddleware, already applied to the demo resource and to the Traefik dashboard web(:80, permanently redirected to HTTPS),websecure(:443) and a loopbackping(127.0.0.1:8082) entrypoint- Local SQLite storage and in-memory sessions, so the appliance runs standalone with no database or Redis to deploy
- Brute-force protection on the portal (3 attempts, 2 minute window, 5 minute ban)
- Two dedicated Azure data disks, at
/var/lib/traefikand/var/lib/authelia, re-provisioned with every VM - File-provider dynamic configuration in
/etc/traefik/dynamic/for your own routers and services - A pre-configured Let's Encrypt certificate resolver (HTTP challenge)
- Unique per-VM secrets, admin password and TLS certificate generated on first boot, and a start-up guard that refuses to run with a template or published credential
- 24/7 SecureAnyCloud support
How the single sign-on works
Every request carrying the authelia middleware flows through this loop:
browser --> Traefik :443 (Host: app.yourdomain.com) | | forwardAuth, BEFORE the backend is reached v Authelia 127.0.0.1:9091 /api/authz/forward-auth | +-- no valid session --> 302 to https://auth.yourdomain.com/?rd=<original URL> | (the login portal; the visitor signs in) | +-- valid session --> 200 + Remote-User / Remote-Groups / Remote-Name / Remote-Email --> Traefik proxies to your backendRemove the middleware from a router and that route becomes public. Add it to a new router and that service is protected, with no changes to the service itself. That is the whole integration. It lets you publish a service that has no authentication of its own through this appliance.
Single sign-on requires a domain name
Plan this before you deploy. Authelia is domain-based. The session cookie it issues must be scoped to a domain shared by the login portal and the services behind it. Authelia rejects a bare IP address, and a browser cannot store a cookie scoped to an IP literal either. No configuration avoids this, because it is how cookies work.
The image ships with the reserved documentation domain example.com as a stand-in that you replace with your own. It uses two host names:
Host Serves
auth.yourdomain.com the Authelia login portal
app.yourdomain.com the protected demo resource and the Traefik dashboard
Until you point DNS at the VM, its public IP address shows an unauthenticated setup page that repeats these instructions. This is expected, and it is the only page reachable without signing in.
Prerequisites
Before you deploy, you need:
- An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region
- A VM size of Standard_B2ms (2 vCPU / 8 GiB RAM) as a starting point. Authelia's argon2id password hashing is deliberately memory-hard, so do not go below 4 GiB
- NSG inbound rules that allow
22/tcpfrom your management network, and80/tcpand443/tcpfor proxied traffic. Authelia on9091and the demo backend on8090are bound to loopback, so never open them - A domain you control, so that you can create the two DNS records above
Step 1 - Deploy from the Azure Marketplace
In the Azure portal, choose Create a resource. Search for Traefik with Authelia SSO on Ubuntu 24.04 LTS by SecureAnyCloud and select Create. Pick your subscription, resource group and region, and choose the Standard_B2ms size. Select SSH public key authentication with the username azureuser, and allow inbound 22, 80 and 443. Then select Review + create.
Step 2 - Deploy from the Azure CLI
az vm image terms accept --urn sac1647283583153:traefik-authelia:default:latest
az vm create \
--resource-group my-resource-group \
--name traefik-authelia-01 \
--image sac1647283583153:traefik-authelia:default:latest \
--size Standard_B2ms \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku StandardThen open the ports that the proxy needs:
az vm open-port --resource-group my-resource-group --name traefik-authelia-01 --port 80 --priority 1010
az vm open-port --resource-group my-resource-group --name traefik-authelia-01 --port 443 --priority 1020Step 3 - Point your DNS at the VM
Create two A records for the domain you control, both pointing at the VM's public IP address:
auth.yourdomain.com. A YOUR_VM_PUBLIC_IP app.yourdomain.com. A YOUR_VM_PUBLIC_IPStep 4 - Connect to your VM
ssh azureuser@YOUR_VM_PUBLIC_IPStep 5 - Confirm the services are running
First boot generates this VM's secrets and starts both daemons. Confirm that they are running:
systemctl is-active traefik authelia sac-landingAll three report active. Check the versions:
/usr/local/bin/traefik version | head -3 authelia --versionCheck Traefik's health endpoint. It is unauthenticated, listens on loopback and answers immediately:
curl -sf -o /dev/null -w 'traefik ping: %{http_code}\n' http://127.0.0.1:8082/pingStep 6 - Retrieve your per-VM credentials
This image ships with no known password. First boot generates the administrator account, the three Authelia secrets and the default TLS certificate on this VM. Print them:
sudo cat /root/traefik-authelia-credentials.txtRecord the AUTHELIA_ADMIN_PASSWORD value in a safe place. It is unique to this VM and cannot be recovered from the image.
Step 7 - Set your own domain
Two files carry the shipped example domain. In /etc/authelia/configuration.yml, set the session cookie domain and the portal URL:
session: cookies: - name: 'authelia_session' domain: 'yourdomain.com' authelia_url: 'https://auth.yourdomain.com/'In /etc/traefik/dynamic/routers.yml, replace both host rules:
routers: authelia-portal: rule: "Host(`auth.yourdomain.com`)" traefik-dashboard: rule: "Host(`app.yourdomain.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))" protected-app: rule: "Host(`app.yourdomain.com`)"Then restart both services:
sudo systemctl restart authelia traefikAuthelia refuses to start on an invalid configuration instead of starting insecurely. If it does not come back, read the reason with sudo journalctl -u authelia -n 30 and sudo tail /var/lib/authelia/authelia.log.
Step 8 - Watch the forward-auth loop
This step shows the integration working. Request the protected resource without a session. Traefik sends you to the portal instead of the backend:
curl -sS --resolve app.example.com:443:127.0.0.1 -k -o /dev/null \
-w 'unauthenticated: %{http_code} -> %{redirect_url}\n' https://app.example.com/You get 302 and a redirect to the Authelia portal, carrying your original URL in ?rd=. Now try a deliberately wrong password. The portal refuses it:
curl -sS --resolve auth.example.com:443:127.0.0.1 -k \
-X POST -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"not-the-password","keepMeLoggedIn":false}' \
-w '\nHTTP %{http_code}\n' https://auth.example.com/api/firstfactorThat returns {"status":"KO"} and HTTP 401. Now sign in with the correct password and keep the session cookie:
curl -sS --resolve auth.example.com:443:127.0.0.1 -k -c /tmp/authelia-jar \
-X POST -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"<AUTHELIA_ADMIN_PASSWORD>","keepMeLoggedIn":false}' \
-w '\nHTTP %{http_code}\n' https://auth.example.com/api/firstfactorThat returns {"status":"OK"} and HTTP 200. Request the same protected URL again, this time with the session:
curl -sS --resolve app.example.com:443:127.0.0.1 -k -b /tmp/authelia-jar \
-o /dev/null -w 'authenticated: %{http_code}\n' https://app.example.com/The response is 200. The URL and the appliance are the same, and the only difference is a valid Authelia session.
The --resolve flags above make the request use the shipped host names from the VM itself, so this works before your DNS has propagated. Once your own records are live, drop the flags and use your real host names from a browser.
Step 9 - Sign in from a browser
Browse to https://app.yourdomain.com/. You are redirected to the Authelia portal:
Sign in as admin with the password from Step 6. Authelia establishes the session and sends you back to the URL you first requested:
The default certificate is self-signed and unique to this VM, so your browser shows a warning on the first visit. The warning stops when you switch to Let's Encrypt, as described below.
Step 10 - Open the Traefik dashboard
The Traefik dashboard is not published unauthenticated. api.insecure is false, and the dashboard router carries the same authelia middleware as every other route. Because you now hold a session, the dashboard opens at https://app.yourdomain.com/dashboard/:
Open HTTP Middlewares to confirm that the integration is live. authelia@file is listed with type forwardauth and a green status. This means Traefik loaded it and consults Authelia on every protected request:
You can confirm the same thing from the shell:
sudo grep -A3 'forwardAuth' /etc/traefik/dynamic/authelia.ymlStep 11 - Protect your own service
Add a router for your service and give it the authelia middleware. Nothing else is required, and your service does not need to know about Authelia:
# /etc/traefik/dynamic/my-app.yml http: routers: my-app: rule: "Host(`intranet.yourdomain.com`)" entryPoints: [websecure] service: "my-app" middlewares: [authelia] tls: {} services: my-app: loadBalancer: servers: - url: "http://127.0.0.1:3000"Traefik watches /etc/traefik/dynamic/ and picks up the file with no restart. Add an A record for the new host name and set the session cookie domain to a parent of it. The same single login then covers both services.
Your application can read the signed-in user from the headers Authelia adds: Remote-User, Remote-Groups, Remote-Name and Remote-Email. Many applications can use these headers directly for trusted-header authentication, which removes their own login screen.
Step 12 - Access control rules
The shipped policy requires a successful login for everything. To vary that per host, path or group, add rules to /etc/authelia/configuration.yml. The first matching rule wins:
access_control: default_policy: 'deny' rules: - domain: 'public.yourdomain.com' policy: 'bypass' - domain: 'app.yourdomain.com' policy: 'one_factor' - domain: 'admin.yourdomain.com' policy: 'two_factor' subject: ['group:admins']Once your rules are written, change default_policy to deny. A host you forget to list is then refused instead of being asked to log in.
Step 13 - Add two-factor authentication
Authelia supports time-based one-time passwords out of the box. Set a rule's policy to two_factor and restart Authelia. Then sign in and follow the registration prompt. The image uses the filesystem notifier, so the registration link is written to a file instead of being emailed:
sudo test -f /var/lib/authelia/notification.txt && echo "notifier file present" || echo "no notifications yet"For production, switch notifier to SMTP so that password resets and 2FA registrations reach users by email.
Managing users
Users live in /etc/authelia/users_database.yml. Generate a hash and add an entry. Never write a plaintext password, because the start-up guard refuses to start the appliance if you do:
sudo authelia crypto hash generate argon2 --password 'a-strong-password' --no-confirmAdd the resulting digest as the user's password value:
users: alice: disabled: false displayname: 'Alice Smith' password: 'PASTE_THE_DIGEST_HERE' email: 'alice@yourdomain.com' groups: - adminsAuthelia reloads the file automatically, so no restart is needed to add a user.
The start-up security guard
Both services run traefik-authelia-guard.sh before they start, as a deliberate fail-closed gate. It refuses to start the appliance in any of these cases:
- First boot has not completed
- Any of the three Authelia secrets is still a build-time template value or one of Authelia's published example values
- The users database is the build-time one or contains Authelia's published demo account
- A password is not an argon2id hash
- The TLS key is missing or world-readable
api.insecurehas been turned on- The forward-auth middleware has been removed
You can run the guard yourself at any time:
sudo /usr/local/sbin/traefik-authelia-guard.shIf the guard refuses, it prints the exact reason, and the appliance stays down instead of serving traffic in an insecure state.
Enabling Let's Encrypt certificates
The certificate resolver is pre-configured and needs only your email address. Set the address in /etc/traefik/traefik.yml:
certificatesResolvers: letsencrypt: acme: email: 'you@yourdomain.com' storage: '/var/lib/traefik/acme.json' httpChallenge: entryPoint: webThen add the resolver to each router's TLS block in /etc/traefik/dynamic/routers.yml:
tls: certResolver: letsencryptBoth host names must resolve publicly to this VM, and port 80 must be reachable because the HTTP challenge is served there. Certificates are stored on the /var/lib/traefik data disk, so they survive a resize or a reboot.
Getting the real client IP behind a load balancer
If you place an Azure Load Balancer, Application Gateway or Front Door in front of this VM, add its ranges to forwardedHeaders.trustedIPs for both entrypoints in /etc/traefik/traefik.yml. Without this setting, Authelia's brute-force protection sees the load balancer's address instead of the real visitor, and its rate limiting is ineffective.
Maintenance
Both data disks are separate from the OS disk, so the SQLite database, certificates and logs survive a VM resize. Back up /var/lib/authelia/db.sqlite3, /etc/authelia/users_database.yml and /var/lib/traefik/acme.json. Logs rotate daily and are kept for 14 days in /var/lib/traefik/ and /var/lib/authelia/. Unattended security upgrades are enabled, as on the stock Ubuntu image.
To review what the appliance is doing, run:
sudo journalctl -u authelia -n 20 --no-pagerSupport
SecureAnyCloud publishes and supports this image. For help, contact sales@secureanycloud.com. Traefik is distributed under the MIT license and Authelia under the Apache 2.0 license. SecureAnyCloud is not affiliated with either project.
Deploy on Azure
Launch Traefik with Authelia SSO on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com