4 Traefik and Authelia for Docker Compose stacks in different scenarios
Alexey Skobkin edited this page 2026-05-16 17:01:06 +00:00

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.