Table of Contents
Docker Compose stacks with Traefik and Authelia
Core idea
The repository uses per-stack Docker Compose files plus optional Traefik integration. Most stacks can run standalone on localhost, or join the shared traefik Docker network when the Traefik compose variant is enabled.
The common pattern is:
request → Traefik router → middleware chain → Docker service
Most application stacks do not hardcode whether they are public or private. Instead, they use a shared variable:
TRAEFIK_ACCESS_POLICY=default-access@file
So the same stack can be used on:
- a public VPS,
- a partially exposed Home Lab,
- a private LAN-only server.
The host's actual security posture is mostly controlled by Traefik dynamic config.
Important moving parts
| Object | Defined in | Meaning |
|---|---|---|
traefik Docker network |
external Docker network | Shared network between Traefik and proxied stacks. |
default-access@file |
traefik/config/dynamic/default-access.yml |
Default access policy for most stacks. Can be public or LAN-only. |
public-access@file |
public-access.yml |
Explicit public override. |
public-auth-access@file |
public-access.yml |
Public route protected by Authelia Forward Auth. |
chain-default@file |
shared.yml |
Common lightweight middleware chain, currently mostly compression. |
auth.lab.name.me |
Authelia stack | SSO portal for Home Lab services. |
lab.name.me cookie domain |
Authelia config | Shared SSO cookie scope for *.lab.name.me. |
Typical Traefik-enabled app labels:
traefik.enable: "true"
traefik.docker.network: "${TRAEFIK_NETWORK:-traefik}"
traefik.http.routers.app.rule: "Host(`app.lab.name.me`)"
traefik.http.routers.app.entrypoints: "websecure"
traefik.http.routers.app.middlewares: "${TRAEFIK_ACCESS_POLICY:-default-access@file},chain-default@file"
traefik.http.services.app.loadbalancer.server.port: "8080"
Domain layout example
name.me
├── name.me → public VPS, personal site
├── git.name.me → public VPS, Forgejo
├── matrix.name.me → public VPS, Continuwuity
└── lab.name.me
├── lab.name.me → Home Lab public IP
├── *.lab.name.me → Home Lab public IP
├── auth.lab.name.me → Authelia portal
├── speed.lab.name.me → public or LAN-only service
├── gatus.lab.name.me → Authelia-protected service
└── app.lab.name.me → private service
For Home Lab SSO:
Authelia URL: https://auth.lab.name.me
Cookie domain: lab.name.me
Protected zone: *.lab.name.me
This means Authelia can share one login session across Home Lab subdomains like:
gatus.lab.name.me
grafana.lab.name.me
whoami.lab.name.me
It does not automatically apply to git.name.me or matrix.name.me, because those are outside the lab.name.me cookie domain.
DNS model
For the Home Lab, use split-horizon DNS:
| Client location | DNS answer for *.lab.name.me |
|---|---|
| WAN / Internet | Home public IP |
| LAN | Local Home Lab server IP |
Example:
WAN:
gatus.lab.name.me → public IP → router port-forward → Traefik
LAN:
gatus.lab.name.me → local server IP → Traefik
This avoids hairpin NAT and keeps LAN clients seen as LAN clients by Traefik.
Important caveat: DNS alone does not choose a different Traefik middleware. If a route uses public-auth-access@file, Traefik still calls Authelia. To bypass Authelia for LAN clients on the same route, configure an Authelia LAN/VPN bypass rule before the wildcard two_factor rule.
Scenario 1: Public VPS
Use this when the whole host is meant to expose public services by default.
Config idea:
# traefik/config/dynamic/default-access.yml
http:
middlewares:
default-access:
ipAllowList:
sourceRange:
- "0.0.0.0/0"
- "::/0"
Every Traefik-enabled stack using the default policy becomes reachable from WAN.
Examples:
| Host | Service |
|---|---|
name.me |
personal site |
git.name.me |
Forgejo |
matrix.name.me |
Continuwuity |
auth.name.me |
optional Authelia, if used on VPS |
%%{init: {"themeVariables": {"fontSize": "11px"}, "flowchart": {"htmlLabels": true, "curve": "basis"}} }%%
flowchart TB
subgraph top[" "]
direction LR
W[WAN] --> D[public DNS<br/>name.me]
D --> T[Traefik<br/>VPS]
end
T --> A[default<br/>public]
subgraph apps[" "]
direction RL
A --> S[site<br/>name.me]
A --> G[Forgejo<br/>git]
A --> M[Matrix<br/>matrix]
end
In this mode, default-access@file is effectively “allow Internet”.
Scenario 2: Home Lab with partial WAN access
Use this when the host is reachable from WAN, but most services should stay LAN-only by default.
Config idea:
# traefik/config/dynamic/default-access.yml
http:
middlewares:
default-access:
ipAllowList:
sourceRange:
- "192.168.1.0/24"
# optionally:
# - "10.0.0.0/8"
# - "172.16.0.0/12"
Then override only selected stacks:
# LAN-only default
TRAEFIK_ACCESS_POLICY=default-access@file
# Public, no Authelia
TRAEFIK_ACCESS_POLICY=public-access@file
# Public, protected by Authelia
TRAEFIK_ACCESS_POLICY=public-auth-access@file
Examples:
| Host | Middleware | Result |
|---|---|---|
app.lab.name.me |
default-access@file |
LAN allowed, WAN blocked. |
speed.lab.name.me |
public-access@file |
LAN and WAN allowed. |
gatus.lab.name.me |
public-auth-access@file |
WAN/LAN pass through Authelia unless bypassed in Authelia rules. |
auth.lab.name.me |
Authelia route | SSO portal, cookie domain lab.name.me. |
%%{init: {"themeVariables": {"fontSize": "10px"}, "flowchart": {"htmlLabels": true, "curve": "basis"}} }%%
flowchart TB
subgraph entry["entry"]
direction LR
W["WAN"] --> PDNS["pub DNS<br/>*.lab to pub IP"]
PDNS --> R["router<br/>port forward"]
R --> T["Traefik<br/>Lab"]
L["LAN"] --> LDNS["router DNS<br/>*.lab to local IP"]
LDNS --> T
end
T --> MR{"matched<br/>router"}
MR --> D["default<br/>LAN only"]
D -->|LAN| P["private<br/>app"]
D -->|WAN| X["403"]
MR --> PA["public"]
PA --> ST["speed<br/>test"]
MR --> FAA["public-auth<br/>ForwardAuth"]
FAA --> AU["Authelia<br/>auth.lab<br/>cookie: lab"]
AU -->|OK| GS["gatus<br/>or app"]
AU -->|login| LI["redirect to<br/>auth<br/>portal"]
In this mode, default-access@file means “private unless explicitly overridden”.
The clean mental model:
default-access@file → LAN-only
public-access@file → public
public-auth-access@file → public, but SSO-protected
Scenario 3: Private LAN server
Use this for an additional LAN-only machine using the same repository and Traefik pattern, but without public DNS, port forwarding, or Authelia.
Config idea:
# traefik/config/dynamic/default-access.yml
http:
middlewares:
default-access:
ipAllowList:
sourceRange:
- "192.168.1.0/24"
Rules:
- no
public-access@file, - no
public-auth-access@file, - no Authelia stack needed,
- no WAN port forwarding,
- services use
default-access@file.
%%{init: {"themeVariables": {"fontSize": "11px"}, "flowchart": {"htmlLabels": true, "curve": "basis"}} }%%
flowchart LR
W[WAN] -. no route .-> N[not reachable]
L[LAN] --> D[local DNS]
D --> T[Traefik<br/>LAN host]
T --> A[default<br/>LAN only]
A --> S1[app]
A --> S2[admin UI]
This is the simplest mode. Network exposure and Traefik access policy both say the same thing: LAN only.
Forward Auth: Traefik + Authelia
Authelia runs as:
https://auth.lab.name.me
For Home Lab SSO, its session cookie should be scoped to:
lab.name.me
That allows the browser to reuse the same Authelia session across:
gatus.lab.name.me
grafana.lab.name.me
app.lab.name.me
Forward Auth flow:
%%{init: {"themeVariables": {"fontSize": "10px"}, "sequence": {"mirrorActors": false}} }%%
sequenceDiagram
participant B as Browser
participant T as Traefik
participant A as Authelia<br/>auth.lab
participant S as Service
B->>T: GET gatus.lab
T->>A: auth check
A-->>T: no session
T-->>B: redirect/login
B->>A: login + 2FA
A-->>B: cookie for lab.name.me
B->>T: retry with cookie
T->>A: auth check
A-->>T: 2xx + Remote-* headers
T->>S: request + identity headers
S-->>B: response
Traefik does not authenticate users by itself here. It asks Authelia:
Is this request allowed?
If Authelia says yes, Traefik forwards the original request to the backend service.
The backend may receive identity headers like:
Remote-User
Remote-Groups
Remote-Email
Remote-Name
But Authelia SSO does not automatically log into every backend app. The app must support trusted headers, OIDC, or its own SSO integration. Otherwise, Authelia only protects access before the app's own login page.
Practical decision table
| Machine type | default-access@file |
Public overrides | Authelia | Default exposure |
|---|---|---|---|---|
| Public VPS | 0.0.0.0/0, ::/0 |
Optional | Optional | Public |
| Home Lab | LAN/VPN ranges | Yes | Yes, for protected WAN services | Private by default |
| Private LAN server | LAN/VPN ranges | No | No | LAN-only |
Rule of thumb
Use:
default-access@file
for the host's normal default.
Use:
public-access@file
only when the service is intentionally public.
Use:
public-auth-access@file
when the service should be reachable from WAN but protected by Authelia SSO/2FA.
For Home Lab:
auth.lab.name.me
cookie domain: lab.name.me
is the clean SSO boundary.