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. No machinectl, no importctl:

the machines are ordinary systemd-nspawn@.service units that the rest of the

system already knows.

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, with

-p, -e, -v, --label, --restart, -m and --entrypoint as in docker.

ps, inspect, exec, shell, cp, logs, stop and rm work the same on

both.

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.

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 (all of them, not only ours)

sudo nspawn start fedora-44 # boot it as a machine

sudo nspawn create fedora-44 db # another machine from the same image

sudo nspawn start web -p 8080:80 -e KEY=v -v /srv/data:/data -v pgdata:/var/lib/pg

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 # mkosi --format=oci, imported as an image

sudo nspawn push team/app:1 # upload it; layers already there are skipped

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 with mkosi, push anywhere

nspawn build runs mkosi with --format=oci on a directory with a

mkosi.conf, imports the result as a local image and push uploads it to the

hub or to any registry you point it at.

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, and everything it generates is a plain

systemd unit or settings file you can read.