dockxo beta
FeaturesPricingBlogChangelogGet started Sign in
blog/how-dockxo-routes-traffic

How dockxo routes traffic: one Traefik per server, generated from what is running

A tour of the routing layer: why every server runs its own Traefik with a file provider, how routes are regenerated from Docker events, and how load-balancer members hide behind :8099.

Sep 24, 2026·dockxo team·2 min read #architecture#routing#traefik

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:

  1. Deterministic output. The routes file is the whole truth. Nothing is inferred at request time.
  2. No socket for Traefik. Only the agent talks to docker.sock.
  3. 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.