The router
The mx router is a single Traefik container per workstation. Every mx project
attaches to the same Docker network and declares a Host() rule in its compose
labels; Traefik reads those labels off the Docker socket and routes by hostname.
Application services publish no HTTP host port at all, so two ecosystems can both
run a web app on container port 3000 and never collide.
Install once, per machine
Section titled “Install once, per machine”mx router install # copies the Traefik config to ~/.mech-crate/router, creates devmesh-traefikmx router up # start (or re-apply config to) the routermx router status # installed / running / network / dashboard URLmx router status prints something like:
Router Status──────────────────────────────────────── Installed: installed Status: running Network: devmesh-traefik Dashboard: http://localhost:7680 Location: /Users/you/.mech-crate/routerThe rest of the verbs: down, restart, logs, inspect, network,
uninstall. Aliases exist for most (start/up, stop/down, ps/status,
info/inspect, remove/uninstall).
Connecting a service
Section titled “Connecting a service”mx add --domain <host> writes these labels for you. This is what they look
like, and what you would write by hand for a service mx did not scaffold:
services: myapp: networks: - default # internal: db, redis, siblings - devmesh-traefik # external: how Traefik reaches you labels: - traefik.enable=true - traefik.http.routers.${COMPOSE_PROJECT_NAME}-myapp.rule=Host(`${MYAPP_ROUTER_HOST:-myapp.localhost}`) - traefik.http.routers.${COMPOSE_PROJECT_NAME}-myapp.entrypoints=web - traefik.http.services.${COMPOSE_PROJECT_NAME}-myapp.loadbalancer.server.port=3000 - traefik.docker.network=devmesh-traefik
networks: devmesh-traefik: external: trueFour things matter and are easy to get wrong:
- Both networks.
devmesh-traefikis how the router reaches the container;defaultis how the container reaches its database. Only the service that receives external traffic needs the first one. Postgres, Redis and workers stay ondefaultonly. traefik.docker.network. A container on two networks has two addresses. Without this label Traefik may pick the wrong one and you get a 502.loadbalancer.server.portis the container’s port, not a host port. There is no host port.- The router, service and middleware names carry the compose project.
Traefik keeps one table of each per machine, so a bare
myappis a name every mx project on the workstation shares.${COMPOSE_PROJECT_NAME}-myappis resolved by Compose from the project name it already knows, so it needs nothing exported and works under a hand-rolleddocker compose -f …too.
The routing rule is a Traefik matcher, so the usual composition works:
Host(...) || Host(...), Host(...) && PathPrefix(...), HostRegexp(...).
Two ecosystems at once
Section titled “Two ecosystems at once”This is the point of the whole arrangement. Project A’s api.localhost and
project B’s admin.localhost are two containers on one shared network behind one
Traefik. Switching between them is cd: no teardown, no port bookkeeping, no
lost container state. Start the subset you need:
make dev s=api # in project Amake dev s=admin # in project B — both reachable, simultaneouslymake dev s="api web" # or a named subset, quotes requiredContainer names no longer stand in the way of this either. No shipped compose
file pins container_name, so each project’s containers are named
<COMPOSE_PROJECT_NAME>-<service>-<index> and two projects that both declare a
db service coexist. See
container names if you have
a script that addressed one by a fixed name.
Two copies of the same shape coexist as well, and that took two separate
repairs. Dev overrides no longer pin host ports, so the second stack’s Postgres
does not find 5432 taken. Traefik router, service and middleware names now carry
the compose project, so two stacks that each scaffold a service called api
write two entries into Traefik’s one router table instead of colliding in it. See
host ports for the port classes
and their override names.
Tuning
Section titled “Tuning”| Variable | Default | Effect |
|---|---|---|
MX_ROUTER_HOME | ~/.mech-crate/router | State directory |
MX_ROUTER_NETWORK | devmesh-traefik | Docker network name |
MX_ROUTER_DASHBOARD_PORT | auto | Pin the dashboard port |
MX_ROUTER_DASHBOARD_RANGE | 7680-7799 | Range the dashboard is allocated from |
When a hostname does not answer
Section titled “When a hostname does not answer”Work down this list; it is ordered by how often each one is the cause.
mx router status. Is the router even running?docker ps. Is the service running, and is it healthy?- Is the service on
devmesh-traefik? A container that is not on the network is invisible to Traefik no matter how good its labels are. - Open the dashboard (
mx router statusprints the URL) and look at Routers. A missing router means the labels did not parse; a router with no service behind it usually meanstraefik.docker.networkis wrong or absent. - A 502 rather than a 404 means Traefik found the container and could not reach
the port. Check
loadbalancer.server.portagainst what the app binds.
Going deeper
Section titled “Going deeper”The full guide covers HTTPS with mkcert and Let’s Encrypt, the dynamic file provider, middlewares, custom routes, routing to non-Docker host services, load balancing, and the complete troubleshooting matrix. It is published in the corpus: