Most panels route traffic through one central proxy. dockxo does the opposite: every server runs its own dockxo-traefik, and the agent on that server writes its configuration. There is no shared state to lose and no single proxy to overload.
Why a file provider
Traefik can discover containers by reading Docker labels itself. We do not use that. The agent already watches Docker events and knows every container’s routes, ports and the IP it holds on the dockxo-proxy network, so it generates /var/lib/dockxo/traefik/dynamic/routes.yml from that knowledge and Traefik simply reloads the file.
That buys three things:
- Deterministic output. The routes file is the whole truth. Nothing is inferred at request time.
- No socket for Traefik. Only the agent talks to
docker.sock. - Rolling deploys are simple. The agent starts the new container, waits for the port check, rewrites the file with the new IP, and only then removes the old container.
Three kinds of routers
dx-<service>-<port> :443 le cert resolver normal domains
dx-<lb>-lbm :8099 ipAllowList load-balancer members
lb-<id>.yml :443 Host(domain) the balancer itself
A normal domain gets a router on :443 with the Let’s Encrypt resolver; the certificate is issued by HTTP-01 on the server that serves the domain and never touches the panel. HTTP→HTTPS redirect is a per-router middleware, so a domain can be switched to HTTP-only when it sits behind Cloudflare Flexible SSL.
A load-balancer member exposes the balanced domain only on the lb entrypoint (:8099), protected by an ipAllowList that contains just the balancer host. The balancer host gets its own lb-<id>.yml with a Host(domain) router on :443 pointing at http://<member-ip>:8099 for each member, with health checks whose hostname is the balanced domain and an optional sticky cookie.
Events, not polling
The agent subscribes to Docker’s event stream. A container starting or stopping — by dockxo, by a crash, by docker restart on the host — triggers a regeneration within milliseconds. If you docker stop something by hand, its routes disappear; when it comes back, they return.
Free subdomains
Each server owns one wildcard record, *.<server>.<base-domain>, created by the panel through Cloudflare when the agent connects (or shown to you to create yourself). A service can take any number of labels under it; they are just more Host() rules on the same router. Private servers opt out entirely so their IP is never in DNS.
What this looks like from the panel
You never see any of it. Add a domain, see a green TLS mark a few seconds later. But if you ever want to look, cat /var/lib/dockxo/traefik/dynamic/routes.yml on the server tells you exactly what is routed and why.