Welcome to the “Container technologies in Fedora” series! This is the first article in a series of articles that will explain how you can use the various container technologies available in Fedora. This first article will deal with



What is a container?

A container is a user-space instance which can be used to run a program or an operating system in isolation from the system hosting the container (called the host system). The idea is very similar to a


or a virtual machine. The processes running in a container are managed by the same kernel as the host operating system, but they are isolated from the host file system, and from the other processes.

What is systemd-nspawn?

The systemd project considers container technologies as something that should fundamentally be part of the desktop and that should integrate with the rest of the user’s systems. To this end, systemd provides


, a tool which is able to create containers using various Linux technologies. It also provides some container management tools.

In many ways,


is similar to


, but is much more powerful. It virtualizes the file system, process tree, and inter-process communication of the guest system. Much of its appeal lies in the fact that it provides a number of tools, such as


, for managing containers. Containers run by


will integrate with the systemd components running on the host system. As an example, journal entries can be logged from a container in the host system’s journal.

In Fedora 24,


has been split out from the systemd package, so you’ll need to install the


package. As usual, you can do that with a

dnf install systemd-container


Creating the container

Creating a container with


is easy. Let’s say you have an application made for Debian, and it doesn’t run well anywhere else. That’s not a problem, we can make a container! To set up a container with the latest version of Debian (at this point in time, Jessie), you need to pick a directory to set up your system in. I’ll be using


for now.

Once the directory has been created, you need to run


, which you can install from the Fedora repositories. For Debian Jessie, you run the following command to initialize a Debian file system.

$ debootstrap --arch=amd64 stable ~/DebianJessie

This assumes your architecture is x86_64. If it isn’t, you must change


to the name of your architecture. You can find your machine’s architecture with

uname -m


Once your root directory is set up, you will start your container with the following command.

$ systemd-nspawn -bD ~/DebianJessie

You’ll be up and running within seconds. You’ll notice something as soon as you try to log in: you can’t use any accounts on your system. This is because


virtualizes users. The fix is simple: remove


from the previous command. You’ll boot directly to the root shell in the container. From there, you can just use


to set a password for root, or you can use


to add a new user. As soon as you’re done with that, go ahead and put the


flag back. You’ll boot to the familiar login console and you log in with the credentials you set.

All of this applies for any distribution you would want to run in the container, but you need to create the system using the correct package manager. For Fedora, you would use DNF instead of


. To set up a minimal Fedora system, you can run the following command, replacing the absolute path with wherever you want the container to be.

$ sudo dnf --releasever=24 --installroot=/absolute/path/ install systemd passwd dnf fedora-release

Debian Jessie in systemd-nspawn

Setting up the network

You’ll notice an issue if you attempt to start a service that binds to a port currently in use on your host system. Your container is using the same network interface. Luckily,


provides several ways to achieve separate networking from the host machine.

Local networking

The first method uses the


flag, which only creates a loopback device by default. This is ideal for environments where you don’t need networking, such as build systems and other continuous integration systems.

Multiple networking interfaces

If you have multiple network devices, you can give one to the container with the


flag. To give


to my container, I would add the flag


. While an interface is assigned to a container, the host can’t use it at the same time. When the container is completely shut down, it will be available to the host again.

Sharing network interfaces

For those of us who don’t have spare network devices, there are other options for providing access to the container. One of those is the


flag. This forwards a port on the container to the host. The format is


, where protocol is either






is a valid port number on the host, and


is a valid port on the container. You can omit the protocol and specify only


. I often use something similar to



You can enable complete, host-only networking with the


flag, which creates a virtual Ethernet interface between the host and the container. You can also bridge two connections with



Using systemd components

If the system in your container has D-Bus, you can use systemd’s provided utilities to control and monitor your container. Debian doesn’t include


in the base install. If you want to use it with Debian Jessie, you’ll want to run

apt install dbus



To easily manage containers, systemd provides the


utility. Using


, you can log in to a container with

machinectl login <var>name</var>

, check the status with

machinectl status <var>name</var>

, reboot with

machinectl reboot <var>name</var>

, or power it off with

machinectl poweroff <var>name</var>


Other systemd commands

Most systemd commands, such as




, and


, support containers with the


option. For example, if you want to see the journals of a container named “foobar”, you can use

journalctl --machine=foobar

. You can also see the status of a service running in this container with

systemctl --machine=foobar status <var>service</var>


Status of the systemd-nspawn container Debian Jessie

Working with SELinux

If you’re running with SELinux enforcing (the default in Fedora), you’ll need to set the SELinux context for your container. To do that, you need to run the following two commands on the host system.

$ semanage fcontext -a -t svirt_sandbox_file_t "/path/to/container(/.*)?"
$ restorecon -R /path/to/container/

Make sure you replace “/path/to/container” with the path to your container. For my container, “DebianJessie”, I would run the following:

$ semanage fcontext -a -t svirt_sandbox_file_t "/home/johnmh/DebianJessie(/.*)?"
$ restorecon -R /home/johnmh/DebianJessie/