Compose
nspawn compose reads docker compose’s files and takes its commands and flags,
with output in docker compose’s format: in a directory with a compose.yaml,
sudo nspawn compose up -d brings the services up and
sudo nspawn compose down takes them away. It matches docker compose 5.6.0,
and is a client of the service like every other
command, so it takes the same permissions.
A project
services:
web:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
db:
condition: service_healthy
db:
image: postgres:18-alpine
environment:
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 2s
volumes:
- db:/var/lib/postgresql
volumes:
db:
In a directory shop with that file:
$ sudo nspawn compose up -d
Image postgres:18-alpine Pulling
Image postgres:18-alpine Pulled
Image nginx:alpine Pulling
Image nginx:alpine Pulled
Network shop_default Creating
Network shop_default Created
Volume shop_db Creating
Volume shop_db Created
Container shop-db-1 Creating
Container shop-db-1 Created
Container shop-web-1 Creating
Container shop-web-1 Created
Container shop-db-1 Starting
Container shop-db-1 Started
Container shop-db-1 Waiting
Container shop-db-1 Healthy
Container shop-web-1 Starting
Container shop-web-1 Started
$ sudo nspawn compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
shop-db-1 postgres:18-alpine "docker-entrypoint.s…" db 4 seconds ago Up 3 seconds (healthy)
shop-web-1 nginx:alpine "/docker-entrypoint.…" web 3 seconds ago Up Less than a second 0.0.0.0:8080->80/tcp
$ sudo nspawn compose exec db psql -U postgres -c 'select 1'
?column?
----------
1
(1 row)
The services are nspawn’s machines, and every other command sees them:
$ sudo nspawn ps
MACHINE IMAGE MODE COMMAND STATE UP PID NETWORK OS
shop-db-1 docker.io/library/postgres:18-alpine app docker-entrypoint.sh postgres running (healthy) 3s 19093 shop_default:10.99.1.2 Alpine Linux v3.24
shop-web-1 docker.io/library/nginx:alpine app /docker-entrypoint.sh nginx -g daemon... running 0s 19262 shop_default:10.99.1.3 8080->80/tcp Alpine Linux v3.24
sudo nspawn compose down -v stops and removes the machines, the network and,
with -v, the volume.
How a project becomes machines
- A service becomes a machine per replica, named as docker names its
containers,
PROJECT-SERVICE-Nwith_written-. The machines carry docker compose’scom.docker.compose.*labels, which is howps,logs,stop,downand the rest find a project again, from its files or from-p NAMEalone. - An image that names no registry comes from Docker Hub and is kept under its
local name (
postgres:18-alpineispostgres-18-alpine); every machine of it is made from that image. - A network of the project is an nspawn network,
PROJECT_KEY, and a volume an nspawn volume, alsoPROJECT_KEY. A secret or config from a file is bound read-only where the service reads it; one from the environment or written in the file is an nspawn secret, removed bydown. depends_onorders what compose does, as with docker compose:up,startandrunbring the services up in order and wait for their conditions, andrestartalso restarts the services whosedepends_onsetsrestart: true. The machines carry no--depends-on, so a service that others depend on is made anew, scaled down or removed as with docker.- A machine is made anew when its service changed (docker compose’s config hash) or its image did.
Builds
A service with a build section is built by nspawn’s
builder whenever docker compose would build it: when its
image is not here (an image it names is pulled first, and built when the pull
fails), every time with pull_policy: build, and always with up --build or
compose build; --no-build builds nothing. Its image is the one it names or
PROJECT-SERVICE. A name without a registry or a namespace is nspawn’s own,
under the configured registry as nspawn build -t puts it, so
sudo nspawn run NAME takes it; one like user/app stays Docker Hub’s, where
compose build --push sends it. down --rmi local removes the images the
project built.
The commands
config, up, down, create, start, stop, restart, kill, pause,
unpause, rm, run, exec, ps, ls, logs, top, port, images,
pull, build, scale, wait, cp, stats, events, volumes and
version, with docker compose’s flags. alpha, attach, bridge, commit,
export, publish, push and watch are refused with an error that names an
alternative where there is one; --dry-run is refused by the commands that
change something, as nothing simulates their changes.
What is supported
The files load as docker compose loads them: interpolation, .env, profiles,
include, extends, overrides, !reset and !override, and the schema’s
checks with docker compose’s messages, so sudo nspawn compose config prints
what docker compose config prints. What nspawn cannot do (remote build
contexts, SSH forwarding into a build, Swarm-only attributes) config shows as
warnings, and up, create, run and scale refuse it for the services they
start.
COMPOSE.md lists every attribute and option with what nspawn compose does with it: yes, partly (with the difference), ignored with a warning, or refused with the reason.