nspawn pulls OCI images from the hub at
hub.nspawn.org, from Docker Hub or from any other registry, stores them as
shared layers and starts, inspects and stops the machines through the D-Bus
APIs of systemd-machined and systemd itself.
Images from any registry
nspawn search fedora looks on the hub and on Docker Hub at once;
nspawn pull fetches from either, and login keeps credentials per registry.
Layers are downloaded once, verified against their digests and shared between
machines. Remove an image and the layers nobody uses go with it.
Machines and apps
Images that ship systemd boot like machinectl start does. Anything else, for
example a docker image, runs its entrypoint as an app under a stub init, or as
PID 1 when it carries s6-overlay, as linuxserver.io’s images do, with -p, -e,
-v, --label, --restart, -m and --entrypoint as in docker. ps,
inspect, exec, shell, cp, logs, stop and rm work on both, and
docker compose files run with nspawn compose.
Networking that works everywhere
A docker0 style bridge with NAT, fixed addresses, published ports and machine names that resolve, managed by nspawn itself. It behaves the same whether the host runs systemd-networkd, NetworkManager, docker, firewalld or nothing at all.
The whole tool in a screenful
sudo nspawn hub ls # repositories and tags on the hub
sudo nspawn search fedora # images on the hub and on Docker Hub
sudo nspawn login docker.io -u me # credentials for a registry (the hub by default)
sudo nspawn pull fedora:44 # download and assemble an image
sudo nspawn images ls # local images, nspawn's and any other machined lists
sudo nspawn start fedora-44 # boot it as a machine
sudo nspawn create fedora-44 db # another machine from the same image
sudo nspawn run -d --name web -p 8080:80 -e KEY=v -v /srv/data:/data docker.io/library/nginx:1.27
sudo nspawn start db --restart unless-stopped -m 512m # back when it dies, and at boot
sudo nspawn ps # running machines: image, mode, command, uptime
sudo nspawn inspect web # everything about a machine, as JSON
sudo nspawn cp ./nginx.conf web:/etc/nginx/
sudo nspawn exec fedora-44 -- systemctl is-system-running
sudo nspawn shell fedora-44
sudo nspawn logs fedora-44 # console output; --inside reads its journal
sudo nspawn stop fedora-44
sudo nspawn rm -f db # remove a machine, stopping it first
sudo nspawn images rm fedora-44 # also frees layers and blobs nobody uses
sudo nspawn volume ls # named volumes and who uses them
sudo nspawn build -t team/app:1 ./app # its Dockerfile (or its mkosi.conf), a local image
sudo nspawn push team/app:1 # upload it; layers already there are skipped
sudo nspawn compose up -d # the compose.yaml of this directory
Every command is a call to nspawn’s service on the system bus, which asks
polkit who you are: sudo always works, an administrator is asked for a
password, and a rule of your own can hand the actions to a group.
Build Dockerfiles, push anywhere
nspawn build builds a Dockerfile with nspawn’s own builder, as docker build
does, or runs mkosi on a directory with a mkosi.conf, and imports the result as
a local image; push uploads it to the hub or any other registry.
Open images
Every image on the hub is built from public mkosi definitions with the distribution’s own package manager. Read them, rebuild them, send fixes.
Small and self-contained
One binary, written in Rust. Its state lives under /var/lib/nspawn, its
machines under /var/lib/machines; the units, drop-ins and .nspawn files it
generates are plain files you can read, and its network rules live in one
nftables table, ip nspawn.