Your first project
The quickstart
Section titled “The quickstart”mx router install && mx router up # once per machine
mx new my-appcd my-appmx add api --recipe rust-api --domain api.localhostmake doctor # check dependenciesmake init # initialize environmentmake dev # develop at http://api.localhostThat is the whole path. What follows is what each step actually does.
mx new my-app
Section titled “mx new my-app”Creates the project skeleton (Makefile, make/, scripts/, apps/ and the
docker/ tree) and copies the shared Docker config. Nothing is running yet;
this is the folder contract on disk.
Two flags are worth knowing:
mx new my-app --no-prompt # skip the interactive promptsmx new my-app --infra cloudflare # also install make/cloudflare.mk + infra/cloudflare/mx new my-app --with api # add services during creation--infra matters more than it looks: without it, the cf-* make targets simply
are not in the project. See Cloudflare deploy.
mx add api --recipe rust-api --domain api.localhost
Section titled “mx add api --recipe rust-api --domain api.localhost”Applies a recipe. mx recipes list shows what is installed and
mx recipes info <name> shows a recipe’s options, features and the services it
brings with it. mx add lands, for a typical recipe:
- an app skeleton under
apps/api/ docker/compose/api.ymlanddocker/compose/api.dev.ymldocker/dockerfiles/api/appandapp.proddocker/.config/.env.api- Traefik labels wired to the
--domainyou passed
--domain is what makes the service reachable at http://api.localhost rather
than a port. Omit it and the recipe defaults to <service>.localhost. Extra
recipe options go through --opt key=value. mx recipes info lists which ones
a recipe accepts.
Recipes that also declare backing services (Postgres, Redis) drop their compose
files in at the same time, so docker/compose/ grows db.yml and redis.yml
on the first mx add that needs them.
make doctor
Section titled “make doctor”Checks Docker, Compose and Make, then the project structure (Makefile,
make/, scripts/, docker/compose/, docker/.config/, the secrets file and
the Docker network) and lists the services it found. It exits non-zero if
something is missing, so it is safe to put in front of a script.
make init
Section titled “make init”Creates docker/.config/.env.secrets from .env.secrets.template if it does not
exist yet, generates a real development value for every credential still empty or
still a placeholder, resolves any ${VAR} references in the other env files into
literals, and ensures the project network. Idempotent, so run it whenever you are
not sure: a value that is already real, generated earlier or typed by you, is
never overwritten.
That is what makes the quickstart above hold with no hand edits. mx new,
mx add with a recipe that brings Postgres, then make dev, and the database
comes up with credentials the application can actually authenticate with.
make dev runs the same step on every start, so a service added later is covered
too. There is nothing to fill in first.
REDIS_PASSWORD stays blank deliberately, because the shipped Redis runs without
auth and a password there would break clients that build
redis://:$REDIS_PASSWORD@redis:6379. make doctor knows that and does not flag
it, while it does name any other key left without a value.
make dev
Section titled “make dev”Starts the stack with the dev overrides merged in: source mounts, debug logging, relaxed health checks. Two shapes:
make dev # everything in docker/compose/make dev s=api # just this service (and what it depends on)make dev s="api site" # a named subset, quotes requiredThe quotes matter: make splits on whitespace, so s=api site would read site
as a second goal. dev, up, down, stop, restart and logs accept a
list. build, run, exec and sh act on one container or image and say so
rather than quietly taking the first name.
make dev stops the existing services first, so it is also the “restart into a
clean state” command. make up is the same thing without the dev overrides:
the baseline compose files alone, which is what ships.
Then:
make logs s=api # tail itmake sh s=api # shell into itmake ps # what is runningmake down # stop and removeFull verb list: CLI reference.
Why the hostname works
Section titled “Why the hostname works”api.localhost resolves to 127.0.0.1 in every current browser and in
getaddrinfo on macOS and Linux. Port 80 is the router; the router reads the
Host header, matches it against the Host() rule in your compose labels, and
forwards to the container over the shared devmesh-traefik network. Your service
publishes no HTTP host port at all, which is why two projects can both have a
service listening on 3000 internally and never collide.
- The folder contract: what you just created
- The router: how the hostname routing works
- Recipes: what is available, and what each carries