systemd-nspawn is like the chroot command, but it is a chroot on steroids.
systemd-nspawn may be used to run a command or OS in a light-weight namespace container. It is more powerful than chroot since it fully virtualizes the file system hierarchy, as well as the process tree, the various IPC subsystems and the host and domain name.
systemd-nspawn limits access to various kernel interfaces in the container to read-only, such as
/sys/fs/selinux. Network interfaces and the system clock may not be changed from within the container. Device nodes may not be created. The host system cannot be rebooted and kernel modules may not be loaded from within the container.
- 1 Installation
- 2 Examples
- 3 Management
- 4 Tips and tricks
- 4.1 Use an X environment
- 4.2 Run Firefox
- 4.3 Access host filesystem
- 4.4 Configure networking
- 4.5 Run on a non-systemd system
- 4.6 Specify per-container settings
- 4.7 Use Btrfs subvolume as container root
- 4.8 Use temporary Btrfs snapshot of container
- 4.9 Run docker in systemd-nspawn
- 5 Troubleshooting
- 6 See also
systemd-nspawn is part of and packaged with.
Create and boot a minimal Arch Linux distribution in a container
Next, create a directory to hold the container. In this example we will use
Next, we use pacstrap to install a basic arch-system into the container. At minimum we need to install thegroup.
# pacstrap -i -c ~/MyContainer base [additional pkgs/groups]
-ioption will avoid auto-confirmation of package selection. As you do not need to install the Linux kernel in the container, you can remove it from the package list selection to save space. See Pacman#Usage.
systemd-tmpfiles-setup.serviceduring the booting process with
systemd-nspawn. It is possible to install the group but excluding the package and its dependencies when building the container with
pacstrap -i -c ~/MyContainer base --ignore linux [additional pkgs/groups]. The
--ignoreflag will be simply passed to . See FS#46591 for more information.
Once your installation is finished, boot into the container:
# systemd-nspawn -b -D ~/MyContainer
-b option will boot the container (i.e. run
systemd as PID=1), instead of just running a shell, and
-D specifies the directory that becomes the container's root directory.
After the container starts, log in as "root" with no password.
The container can be powered off by running
poweroff from within the container. From the host, containers can be controlled by the machinectl tool.
Ctrland rapidly press
]three times. Non-US keyboard users should use
Create a Debian or Ubuntu environment
Install, and one or both of and (obviously install the keyrings for the distros you want).
systemd-containerpackage is installed on the container system.
From there it's rather easy to setup Debian or Ubuntu environments:
# cd /var/lib/machines # debootstrap --include=systemd-container --components=main,universe <codename> myContainer <repository-url>
For Debian valid code names are either the rolling names like "stable" and "testing" or release names like "stretch" and "sid", for Ubuntu the code name like "xenial" or "zesty" should be used. A complete list of codenames is in
/usr/share/debootstrap/scripts. In case of a Debian image the "repository-url" can be
http://deb.debian.org/debian/. For an Ubuntu image, the "repository-url" can be
Unlike Arch, Debian and Ubuntu will not let you login without a password on first login. To set the root password login without the '-b' option and set a password:
# systemd-nspawn -D myContainer # passwd # logout
If the above did not work. One can start the container and use these commands instead:
# systemd-nspawn -b -D myContainer #Starts the container # machinectl shell root@myContainer /bin/bash #Get a root bash shell # passwd # logout
Creating private users (unprivileged containers)
systemd-nspawn supports unprivileged containers, though the containers need to be booted as root.
The easiest way to do this is to let systemd-nspawn decide everything:
# systemd-nspawn -UD myContainer # passwd # logout # systemd-nspawn -bUD myContainer
Here systemd-nspawn will see if the owner of the directory is being used, if not it will use that as base and 65536 IDs above it. On the other hand if the UID/GID is in use it will randomly pick an unused range of 65536 IDs from 524288 - 1878982656 and use them.
- The base of the range chosen is always a multiple of 65536.
--private-users=pickis the same, if kernel supports user namespaces.
--private-users-chown, see for details.
You can also specify the UID/GID of the container manually:
# systemd-nspawn -D myContainer --private-users=1354956800:65536 --private-users-chown # passwd # logout # systemd-nspawn -bUD myContainer
While booting the container you could still use
--private-users-chown, but it is unnecessarily complicated, let
-U handle it after the assigning the IDs.
Enable container on boot
When using a container frequently, you may want to start it on boot.
First enable the
machines.target target, then
myContainer is an nspawn container in
/etc/systemd/nspawn/myContainer.nspawn. See for all options.
Build and test packages
See Creating packages for other distributions for example uses.
Managing your containers is essentially done with the
machinectl command. See for details.
Spawn a new shell inside a running container:
$ machinectl login MyContainer
Show detailed information about a container:
$ machinectl status MyContainer
Reboot a container:
$ machinectl reboot MyContainer
Poweroff a container:
$ machinectl poweroff MyContainer
Download an image:
# machinectl pull-tar URL name
Much of the core systemd toolchain has been updated to work with containers. Tools that do usually provide a
-M, --machine= option which will take a container name as argument.
See journal logs for a particular machine:
$ journalctl -M MyContainer
Show control group contents:
$ systemd-cgls -M MyContainer
See startup time of container:
$ systemd-analyze -M MyContainer
For an overview of resource usage:
You can take advantage of control groups to implement limits and resource management of your containers with
systemctl set-property, see . For example, you may want to limit the memory amount or CPU usage. To limit the memory consumption of your container to 2 GiB:
# systemctl set-property systemd-nspawn@myContainer.service MemoryMax=2G
Or to limit the CPU time usage to roughly the equivalent of 2 cores:
# systemctl set-property systemd-nspawn@myContainer.service CPUQuota=200%
This will create permanent files in
According to the documentation,
MemoryHigh is the preferred method to keep in check memory consumption, but it will not be hard-limited as is the case with
MemoryMax. You can use both options leaving
MemoryMax as the last line of defense. Also take in consideration that you will not limit the number of CPUs the container can see, but you will achieve similar results by limiting how much time the container will get at maximum, relative to the total CPU time.
--runtimeto make the changes temporary. You can check their results with
Tips and tricks
Use an X environment
You will need to set the
DISPLAY environment variable inside your container session to connect to the external X server.
X stores some required files in the
/tmp directory. In order for your container to display anything, it needs access to those files. To do so, append the
--bind-ro=/tmp/.X11-unix option when starting the container.
/tmp/.X11-unixcontents have to be bind-mounted as read-only, otherwise they will disappear from the filesystem. The read-only mount flag does not prevent using
connect()syscall on the socket. If you binded also
/run/user/1000then you might want to explicitly bind
/run/user/1000/busas read-only to protect the dbus socket from being deleted.
xhost only provides rather coarse access rights to the X server. More fine-grained access control is possible via the
$XAUTHORITY file. Unfortunately, just making the
$XAUTHORITY file accessible in the container will not do the job:
$XAUTHORITY file is specific to your host, but the container is a different host.
The following trick adapted from stackoverflow can be used to make your X server accept the
$XAUTHORITY file from an X application run inside the container:
$ XAUTH=/tmp/container_xauth $ xauth nextract - "$DISPLAY" | sed -e 's/^..../ffff/' | xauth -f "$XAUTH" nmerge - $ sudo systemd-nspawn -D myContainer --bind=/tmp/.X11-unix --bind="$XAUTH" \ -E DISPLAY="$DISPLAY" -E XAUTHORITY="$XAUTH" --as-pid2 /usr/bin/xeyes
The second line above sets the connection family to "FamilyWild", value
65535, which causes the entry to match every display. See for more information.
See Firefox tweaks.
Access host filesystem
--bind-ro in .
If both the host and the container are Arch Linux, then one could, for example, share the pacman cache:
# systemd-nspawn --bind=/var/cache/pacman/pkg
Or you can specify per-container bind using the file:
This assumes you have started
systemd-nspawn with the
-n switch, creating a virtual Ethernet link to the host.
Note the canonical systemd-networkd host and container .network files are from https://github.com/systemd/systemd/tree/master/network .
See systemd-networkd#Usage with containers for more complex examples.
To make it easier to connect to a container from the host, you can enable local DNS resolution for container names. In
mymachines to the
hosts: section, e.g.
hosts: files mymachines dns myhostname
Then, any DNS lookup for hostname
foo on the host will first consult
/etc/hosts, then the names of local containers, then upstream DNS etc.
Use host networking
To disable private networking used by containers started with
machinectl start MyContainer add a
MyContainer.nspawn file to the
/etc/systemd/nspawn directory (create the directory if needed) and add the following:
Parameters set in the
MyContainer.nspawn file will override the defaults used in
systemd-nspawn@.service and the newly started containers will use the host's networking.
Virtual Ethernet interfaces
If a container is started with
systemd-nspawn ... -n, systemd will automatically create one virtual Ethernet interface on the host, and one in the container, connected by a virtual Ethernet cable.
If the name of the container is
foo, the name of the virtual Ethernet interface on the host is
ve-foo. The name of the virtual Ethernet interface in the container is always
When examining the interfaces with
ip link, interface names will be shown with a suffix, such as
@ifN is not actually part of the name of the interface; instead,
ip link appends this information to indicate which "slot" the virtual Ethernet cable connects to on the other end.
For example, a host virtual Ethernet interface shown as
ve-foo@if2 will connect to container
foo, and inside the container to the second network interface -- the one shown with index 2 when running
ip link inside the container. Similarly, in the container, the interface named
host0@if9 will connect to the 9th slot on the host.
Use a network bridge
If you have configured a network bridge on the host system in order to have an IP address assigned to the container as if it was a physical machine in your local network (see, for example, systemd-networkd#DHCP with two distinct IP or systemd-networkd#Static IP network) you can make systemd-nspawn use it by using the option
Run on a non-systemd system
Specify per-container settings
To specify per-container settings and not overrides for all (e.g. bind a directory to only one container), the .nspawn files can be used. Seefor details.
Use Btrfs subvolume as container root
To use a Btrfs subvolume as a template for the container's root, use the
--template flag. This takes a snapshot of the subvolume and populates the root directory for the container with it.
For example, to use a snapshot located at
# systemd-nspawn --template=/.snapshots/403/snapshots -b -D my-container
my-container is the name of the directory that will be created for the container. After powering off, the newly created subvolume is retained.
Use temporary Btrfs snapshot of container
One can use the
-x flag to create a temporary btrfs snapshot of the container and use it as the container root. Any changes made while booted in the container will be lost. For example:
# systemd-nspawn -D my-container -xb
where my-container is the directory of an existing container or system. For example, if
/ is a btrfs subvolume one could create an ephemeral container of the currently running host system by doing:
# systemd-nspawn -D / -xb
After powering off the container, the btrfs subvolume that was created is immediately removed.
Run docker in systemd-nspawn
rw permission of
/sys/fs/cgroup to run its containers, which is mounted read-only by
systemd-nspawn by default due to cgroup namespace. However, it is possible to run Docker in a systemd-nspawn container by bind-mounting
/sys/fs/cgroup from host os and enabling necessary capabilities and permissions.
First, cgroup namespace should be disabled by
systemctl edit systemd-nspawn@myContainer
systemctl edit systemd-nspawn@myContainer
/etc/systemd/nspawn/myContainer.nspawn (create if absent) and add the following configurations.
[Exec] Capability=all SystemCallFilter=add_key keyctl [Files] Bind=/sys/fs/cgroup
This grants all capabilities to the container, whitelists two system calls
keyctl (related to kernel keyring and required by Docker), and bind-mounts
/sys/fs/cgroup from host to the container. After editing these files, you need to poweroff and restart your container for them to take effect.
overlaymodule on the host before starting Docker inside the systemd-nspawn to use the
overlay2storage driver (default storage driver of Docker) properly. Failure to load the driver will cause Docker to choose the inefficient driver
vfswhich copies everything for every layer of Docker containers. Consult Kernel modules#Automatic module loading with systemd on how to load the module automatically.
Root login fails
If you get the following error when you try to login (i.e. using
machinectl login <name>):
arch-nspawn login: root Login incorrect
pam_securetty(login:auth): access denied: tty 'pts/0' is not secure !
Unable to upgrade some packages on the container
It can sometimes be impossible to upgrade some packages on the container,
/sys being mounted as Read Only. The workaround is to remount the directory in Read Write when running
mount -o remount,rw -t sysfs sysfs /sys, do the upgrade then reboot the container.
execv(...) failed: Permission denied
When trying to boot the container via
systemd-nspawn -bD /path/to/container (or executing something in the container), and the following error comes up:
execv(/usr/lib/systemd/systemd, /lib/systemd/systemd, /sbin/init) failed: Permission denied
even though the permissions of the files in question (i.e.
/lib/systemd/systemd) are correct, this can be the result of having mounted the file system on which the container is stored as non-root user. For example, if you mount your disk manually with an entry in fstab that has the options
noauto,user,..., systemd-nspawn will not allow executing the files even if they are owned by root.
Reboot not working
When trying to reboot the container via machinectl or within the container, the container does not reboot.
/usr/lib/systemd/system/systemd-nspawn@.service and remove
Not possible at this time (June 2019).