Traefik Dynamic Config
This directory is what Traefik’s file provider reads. The compose mount is relative to
the project (./dynamic:/etc/traefik/dynamic), so on the traefik host the live path is the
git checkout itself.
A route change therefore ships the normal way — commit → push → git pull on the host.
The provider watches the directory and picks it up in seconds. No restart, and never
docker restart, which only reloads static config from the compose command: flags. See
Operations.
Older docs describe this directory as a review-only mirror of
/mnt/lxcshare/docker-data/traefik/dynamic. That is stale — writing there now changes
nothing, and nothing mounts that path any more.
traefik-manager does not edit this directory either, though it is meant to. Its mount
is ./traefik/dynamic while Traefik’s is ./dynamic, so it lands on a sibling
traefik/dynamic/ that Docker created empty and git does not track. The UI therefore
shows no config at all. Correcting the mount is a one-word compose change plus a
bws run … up -d recreate.
Two rules for this directory:
- Never put backups in it. The provider parses every file present, so a
services.yml.bakis a duplicate router definition, not an inert copy. Back up to/root/traefik-dynamic-backups/. - Match ownership. Existing files are
1000:1000; a root-owned file is still read but cannot be edited fromtraefik-manager.
Every file here is live — services.yml, middlewares.yml, middleware-chains.yml,
tls-opts.yml, certificates.yaml, certificates-resolvers.yml, forgejo-ssh.yml,
opengist-ssh.yml,
proxmenux.yml (the node’s own web dashboard), docker-apps-cifs.yml (11 routers for
VM 217) and ugreen-nas.yml (6 for the NAS). The two *-ssh.yml files are tcp
routers, not http ones — raw git-SSH passthrough to VM 217, one port each.
What the Proxmox provider supports
Section titled “What the Proxmox provider supports”Everything else on the fleet is routed by the traefik-proxmox-provider plugin, which reads labels out of each guest’s Proxmox description. It implements its own label parser, so only these do anything — anything else is read and discarded without warning. Taken from the vendored source in the running container (v0.7.6).
Router — traefik.http.routers.<name>. | Service — traefik.http.services.<name>.loadbalancer. |
|---|---|
rule | server.url — full override, wins over everything else |
entrypoints (or singular entrypoint) | server.scheme — only https is special-cased |
middlewares | server.port |
priority | server.ip |
service | passhostheader |
tls, tls.certresolver | healthcheck.path / .interval / .timeout |
tls.domains, tls.options | sticky.cookie.name / .secure / .httponly |
responseforwarding.flushinterval |
server.url short-circuits the scheme/port/ip logic entirely, and nothing re-checks that
the URL belongs to the guest whose notes carried it. That is how the UGREEN NAS
(192.168.1.29, not a Proxmox guest at all) used to be routed from VM 217’s
description rather than from a file here — one routing mechanism for the fleet instead
of two. It is now in ugreen-nas.yml; the mechanism is still worth knowing, since it is
what made that arrangement possible at all.
scheme=https changes the default port to 443, so pair it with an explicit server.port
unless the backend really is on 443.
Why VM 217 and the NAS are routed from files
Section titled “Why VM 217 and the NAS are routed from files”Not a plugin limitation — the description simply ran out of room. Proxmox caps that field
at 8192 characters. VM 217’s held nine label blocks for its own services plus six more
pointing at the UGREEN NAS, totalling 7808, and Comicarr’s seven-line block needed 397 of
the 384 left. qm set rejects an over-length value whole
(400 Parameter verification failed. description: value may only be 8192 characters long)
and writes nothing, so there was no way to add a tenth service the old way.
Both groups moved here rather than trimming to buy a few hundred characters and meeting
the same wall next time. docker-apps-cifs.yml carries the ten VM 217 routers,
ugreen-nas.yml the six NAS ones. Splitting by backend keeps each file’s servers pointing
at a single address, so a renumbered host is one file to edit.
The NAS entries in particular no longer depend on an unrelated guest. They only ever lived
in VM 217’s description because server.url overrides the guest IP (see below) and there
was nowhere better; now they survive that guest being renumbered or retired.
The description must not keep the labels. A label block and a file entry for the same
host produce two routers — forgejo@file and
forgejo@plugin-traefik-proxmox-provider — both matching Host(forgejo.toolsera.lan).
VM 217’s description was replaced with a pointer to these files.
Anything on the node needs a file
Section titled “Anything on the node needs a file”The traefik-proxmox-provider plugin reads labels out of each guest’s description.
pve-dell is not a guest — it has no description field to write labels into — so a
service running on the node itself can never be routed by the plugin, however it is named
and whatever tags it carries.
proxmenux.yml is the first of these: ProxMenux Monitor on http://192.168.1.131:8008,
a proxmenux-monitor.service unit installed under /usr/local/share/proxmenux. Plain
HTTP, so it needs no serversTransport — just a router and a backend URL.
This is a different reason from the PegaProx case below. That one was a guest and could have carried labels; it needed a file because the plugin drops the one label that would have made its self-signed HTTPS work. Both end up here, for unrelated reasons.
PegaProx: removed
Section titled “PegaProx: removed”pegaprox.yml used to live here. CT 268 was destroyed on 2026-08-20 and the file
outlived it, leaving a router that resolved and answered 502 — including on the Dashy
tile, which is why both were removed together on 2026-08-23.
It is documented rather than simply deleted because the reason it needed a file at all
still applies to anything like it. PegaProx served HTTPS on :5000 with a self-signed
certificate, and the plugin silently drops loadbalancer.serverstransport — the one
label that would let a notes-based route skip verification. The result is a router that
resolves, a backend reporting UP, and a 500 on every request. Defined in a file it
could reference the insecure-tls transport already in services.yml, exactly as lxd
(https://192.168.1.148:2956) still does.
The alternatives did not survive contact: PegaProx had no cleartext port (5001 and 5002
are WebSocket endpoints answering 426), Traefik’s container does not carry the
Smallstep root so re-issuing the certificate would still fail without
--serversTransport.rootCAs, and --serversTransport.insecureSkipVerify=true would
disable verification for every backend. The last two are static config needing a
recreate anyway.