Creating containers with systemd

This post is mostly a note to myself, as I can never remember the exact steps. But I think it would be useful for other people.

As much as I dislike systemd, it has to be said that their implementation of a container manager is neat. Simple, lightweight, and well integrated across the systemd tools.

As usual, the Arch wiki has excellent documentation on the matter. I will write another post on how to set up the host, but not today (as I can't remember the details!). Suffice to say that a pre-requisite is to have the systemd-container package installed and configured.

Then, to spin a new instance that will have its own init system and will be started at boot, you only need the following commands:

# Sample values.
DISTRO=stretch
MACH=my_container
# Install base system.
$ debootstrap $DISTRO /var/lib/machines/$MACH
# You need dbus for proper integration from the host.
$ chroot /var/lib/machines/$MACH apt install dbus
# Now it is the time to do any other customisation directly on the filesystem (see below).
# Place your customisations in the override file.
$ mkdir -p /etc/systemd/system/systemd-nspawn@$MACH.service.d/
$ echo $CUSTOM > /etc/systemd/system/systemd-nspawn@$MACH.service.d/override.conf
# Reload systemd, and enable and start the container.
$ systemctl daemon-reload 
$ systemctl enable systemd-nspawn@$MACH
$ systemctl start systemd-nspawn@$MACH
# Profit!
$ machinectl shell $MACH /bin/bash

The override file is important if you -like me- want to use the containers in the same network namespace as the host.

The default configuration for systemd-nspawn uses the following arguments:

$ grep ExecStart /lib/systemd/system/systemd-nspawn@.service
ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i

Note the --network-veth parameter. This creates a virtual ethernet connection between host and guest, which you must then route and/or masquerade properly. It is a sane setting for isolation, but in my case, I usually use these containers as glorified chroots, so I want to share the network. So, in the override file, I clear the ExecStart setting, and then put a modified command line:

$ cat /etc/systemd/system/systemd-nspawn@$MACH.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest -U --settings=override --machine=%i

If you are using this configuration, you should also copy network-related files into the container, before the first start (see below why):

$ cp /etc/hosts /etc/resolv.conf /var/lib/machines/$MACH/etc/

It is important to know that during the first execution with the -U flag (which is the default), systemd-nspawn will chown all the files in the container to use private user IDs. So, you should never modify these files directly, or log in with the chroot command, as you will make them inaccessible for the processes inside the container. The libnss-mymachines package does a nice job of mapping these private users in the host environment.