Recently I was looking to install an application on Linux with a large number of dependencies: TeXstudio and it’s TeXLive libraries. I wasn’t sure I wanted the packages sitting around on my computer long term, and I didn’t feel like micro-managing the dependencies with a minimal install. I thought this would be the perfect chance to experiment with Linux containers.
There are multiple implementations of containerization right now on Linux, ranging from the extremely customizable, and more general – LXC – to the more specific application deployment technology – Docker. Since I wasn’t going to be deploying software images, I didn’t think using Docker made any sense. My use also didn’t involve something that needed to be extremely customizable, and since I wanted a relatively quick setup, I chose to go with an alternative: systemd-nspawn.
As you may have guessed from the name, systemd-nspawn is part of systemd. freedesktop.org describes it as being used to “run a command or OS in a light-weight namespace container. In many ways it is similar to chroot(1), but more powerful 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 is extremely easy to use. In its most basic configuration it can be used to “spawn” a container simply by invoking
systemd-nspawn in a directory. Taking full advantage of its features, however, involves using more specific options, as well as, the many systemd commands that integrate well with it.
- Container Setup
- Running Graphical Applications
- Closing thoughts
If you are using btrfs or ZFS you might want to create an individual subvolume or dataset for your containers.
I created a new ZFS dataset for my machines
New dataset for the container
Mounted them in fstab
I’m using Arch so it made sense to install an Arch base. Depending on what you’re comfortable with, or what your system is, you may want to set up an alternative. One of the really cool aspects of containers is that, like virtualization you can set up whatever type of system you choose.
Because containers can share the kernel of the host, when you’re setting up the container it’s not necessary to install another Linux kernel. On arch use
--ignore linux and the
-i option to avoid auto-confirmation.
Now it’s time to boot into the container. The container will share the host’s IP.
You should be sitting at a login prompt. login as root with no password
While not entirely necessary for my use, a seperate IP can be given to the container. It can be configured to use DHCP to obtain an IP, or be manually assigned a static one. Alternatively, the container can share the networking and IP of the host. Depending on the application being used, for example in server applications, having an individual IP for each container can be very useful.
I chose to take the route of setting up a network bridge on the host allowing the container to have its own IP.
The easiest way to configure networking is to use systemd-networkd on the host.
On the host and container we can use systemd-resolved in conjunction with systemd-networkd to manage DNS.
Start and enable
systemd-resolved, then delete
/etc/resolv.conf and link
Create a Bridge
This step is not required, but will give the container its own IP by giving it a virtualized interface.
Host Networking Setup
On the host remove any config files.
First create a bridge.
Setup the bridge network.
Assign the bridge an interface:
Restart systemd-networkd after setup.
Container Networking Setup
systemd-resolved to manage DNS on the host, it can be used inside the container to manage
resolv.conf. Otherwise edit
/etc/resolv.conf in the container manually.
If a static IP is being used, the file
/usr/lib/systemd/network/80-container-host0.network that would have setup DHCP needs to be masked.
Start and enable
Now the containers networking can be set up.
The virtual interface we are matching is called
host0. Create the configuration file and fill in your settings. Mine is setup to have a static IP.
The container can now be booted with the bridge:.
Check to make sure the static ip specified is in use in the container.
The bridge should be visible on the host as well.
Configure systemd-nspawn Unit Override
While it’s possible to continue starting the container with
systemd-nspawn, there is a tool called
machinectl that can be used to simplify the process, and give a nice command line interface. In order to use our new configuration with
machinectl we need to edit the default systemd unit.
Here’s the original unedited ExecStart.
Edit the configuration to match your network setup, and add any additional settings you require.
Now it is possible to start the container with
Root Login With machinectl
Because of how
pam_security works on Arch, it is it not possible to login as root with machinectl. To allow root log in with machinectl
pts/0 needs to be added to
/etc/securetty on the container.
Now you can start and login to the container
The system can now be configured like any other system where the command line is accessible.
Running Graphical Applications
It is possible to run graphical applications inside of the container by sharing the hosts xorg server with the container, but it does require a little additional setup.
Setup Access to X
The xhost command gives permission to connect to a user’s X server. Run it on the host as a regular user that you want the containers application to connect to. An alternative is using xauth and sharing access using your Xauthority file.
xhost exposes your xserver to everyone, only use it on trusted networks.
It can be set back to normal with
Check the host’s $DISPLAY environment variable as the regular user, the container should be set to match it.
In the container, set the variable to match.
e.g. if it was
I typically set up an alias to open my xserver using xhost when I start an application, and close it after.
Bind X Variables
Xorg has certain variables that need to be passed in, add them to the start options:
I want to run texstudio in the container. I made it a user on the container so that it’s not running as root.
Now I am able to set the display environment variable in the users shell configuration file “~/.zshrc”.
In order to give the application running in the container access to the host system I shared a directory from the host.
My final unit file ended up looking like this:
To run my containerized application, I now just execute the
machinectl login command and login.
It surprised me how easy it was to run a graphical application in a container - when I ran texstudio I must admit that I didn’t expect to see it pop up on the first try.
I set up the container partly for fun, but also because I didn’t feel like installing the massive number of dependencies texlive has on my main system, so I put them in the container instead. This way when I’m finished with using it, I can just destroy the container and that will be that. I can also store the container somewhere else until I need it, i.e. on my server, and then when the time comes to use it I can transfer it back to my computer. Containerization is such an interesting technology and it seems like it’s really getting its time on Linux.