Home / Alt manpages / systemd-nspawn(1)

  • systemd-nspawn(1)
  • User command
  • linux

Run a command safely in a systemd-nspawn container

You will run either one command or a complete Linux system inside a systemd-nspawn container, then make the useful parts repeatable with a .nspawn file. Allow 15 minutes for an existing OS tree, or longer if you still need to build one. This guide uses systemd 255.4, package version 255.4-1ubuntu8.17, as installed on the reference machine.

1. Check the tool and choose an OS tree

systemd-nspawn needs a directory containing a Linux filesystem, including /etc/os-release or /usr/lib/os-release when booting it. The directory can come from a distribution bootstrap tool, an image unpacked by your normal provisioning process, or an existing machine under /var/lib/machines/. It is not a general-purpose sandbox for an arbitrary empty directory.

$ systemd-nspawn --version
systemd 255 (255.4-1ubuntu8.17)
$ test -f /path/to/container/etc/os-release && echo 'OS metadata found'

Use an absolute placeholder path until you have replaced it. Most container setup and mounting operations require elevated privileges, so the examples that start a container use sudo. The command inside the container is still governed by the container's user database and filesystem.

2. Run one command with a stub init

For an arbitrary command, use --as-pid2. Without it, nspawn runs the selected command as PID 1. PID 1 has special duties, such as reaping orphaned processes and handling signals, which ordinary utilities do not implement. The stub init stays as PID 1 and runs your command as PID 2.

$ sudo systemd-nspawn \
    --directory=/path/to/container \
    --machine=demo \
    --as-pid2 \
    /usr/bin/printf 'container says: %s\n' 'hello'
container says: hello

The command's output is the useful result. Check the exit status immediately if a script will act on it:

$ printf 'exit status: %s\n' "$?"
exit status: 0

That status belongs to the command just run. Do not insert a separate inspection command before printing it. The machine name is used for registration and the initial container hostname; keeping it short also avoids awkward network interface names later.

3. Open a shell or boot the whole system

If you omit a command and do not use --boot, nspawn starts a shell. This is useful for an interactive repair or inspection session:

$ sudo systemd-nspawn --directory=/path/to/container --machine=demo
root@demo:~# cat /etc/os-release
root@demo:~# exit

Use --boot when the directory contains a complete system and you want its init program as PID 1:

$ sudo systemd-nspawn --boot --directory=/path/to/container --machine=demo

Do not combine --boot and --as-pid2. The first asks nspawn to find and run an init system; the second is for a single payload command. The interactive command line defaults to the single-command mode, while the [email protected] template uses boot mode and other defaults, so do not assume a service invocation behaves like the command you tested by hand.

4. Make a run disposable before testing changes

Use --ephemeral for a temporary snapshot of the container filesystem. Changes made during the run are discarded when nspawn exits:

$ sudo systemd-nspawn \
    --ephemeral \
    --directory=/path/to/container \
    --as-pid2 \
    /usr/bin/sh -c 'touch /tmp/throw-away; test -f /tmp/throw-away && echo temporary-ok'
temporary-ok

The command must still be able to create its runtime mounts, and snapshot support is more efficient on filesystems such as Btrfs or suitable XFS than on traditional filesystems. Treat the original tree as unchanged only after the process has exited, and verify a file you expected to retain was not modified. There is no recovery step for data deliberately written only inside an ephemeral run.

5. Add a persistent, reviewable configuration

Put administrator-owned settings in /etc/systemd/nspawn/, named after the machine. The file is optional and uses an INI-like syntax. A file beside an image or in its parent directory is treated more cautiously: potentially privileged settings are ignored. That distinction prevents an image vendor or downloaded image from silently requesting host access.

Create the file with an editor as root:

$ sudo install -d /etc/systemd/nspawn
$ sudoedit /etc/systemd/nspawn/demo.nspawn

For a command container, enter:

[Exec]
Boot=no
Parameters=/usr/bin/printf nspawn-config-ok
Environment=DEMO_MODE=review
WorkingDirectory=/tmp

Now the machine name selects both the root tree and the settings file:

$ sudo systemd-nspawn --machine=demo
nspawn-config-ok

Parameters= is a whitespace-separated command line, with quotes available for arguments containing spaces. Command-line options normally override settings-file values. The --settings=override mode reverses that precedence, while --settings=trusted allows all settings from any discovered file to take effect. Avoid both modes unless you have reviewed every setting source.

6. Limit persistence and host reach

--read-only mounts the container root read-only, but additional bind and temporary mounts can still be writable. For a disposable stateful test, --volatile=state leaves the OS tree read-only and puts /var/ on temporary storage. --volatile=overlay gives the tree a temporary writable overlay. All such changes disappear when the container terminates.

$ sudo systemd-nspawn \
    --read-only \
    --volatile=state \
    --directory=/path/to/container \
    --as-pid2 \
    /usr/bin/sh -c 'printf "state is temporary\n"; test -w /var'

Use --private-network when the payload should have its own network namespace, or --network-veth when you also want a virtual Ethernet link. Both require the host's networking setup and privileges. A bind mount is an explicit host-to-container access path, so review it like a privilege decision; use --bind-ro=/host/path:/container/path when the payload only needs to read.

7. Diagnose the common traps

  • If nspawn rejects booting the tree, check both /etc/os-release and /usr/lib/os-release. A missing OS metadata file is a safety check failure, not proof that the directory is a valid image.
  • If a command behaves strangely as PID 1, rerun it with --as-pid2. Use --boot only for an init system.
  • If the expected command is not found, check that its path exists inside the container, not only on the host. The root filesystem changes the meaning of /usr/bin/....
  • If a setting appears to be ignored, check the file's location and the selected --settings mode. Settings beside an image do not automatically receive privileged effects.
  • If the container has no network, that may be deliberate. Interactive nspawn does not imply a veth; the service template has different defaults.

Done means

  • You verified the installed systemd-nspawn version and selected a real OS tree.
  • A one-shot command runs with --as-pid2 and returns the status you expect.
  • You use --boot only when the tree contains an init system.
  • Tests that must leave no changes use --ephemeral or an appropriate volatile mode.
  • Persistent settings live in a reviewed /etc/systemd/nspawn/machine.nspawn file, and every bind or network option has been treated as a host-access decision.