Containers? On MY machine?

By chimo on (updated on )

“It’s more likely than you think."

This is another post about different things I've been messing around with over the years, coming together and merging into what became my day-to-day machine.

The Uninteresting Part

The back-story. I won’t be offended if you skip this part. I get it; I always scroll past that part on recipes websites. At least there are no ads here :D

I’ve been messing with computers ever since I can remember, and hosting my own services since around 2009. Things changed a lot and often along the way, of course, and one of the major changes was that I started looking into LXD and eventually migrated all my services into separate containers.

A side-effect of playing around with containers is: I started dealing more and more with Alpine Linux since it’s a very popular distribution to use in that context. It turned out that I quite enjoy Alpine, so I recently decided to give it a spin as my main, day-to-day operating system.

One of the things I try to do with my systems is to keep them as “minimal” as possible (yes, “minimal” means different things to different people. I like setting up everything from scratch, explicitly install every single package I need and know exactly how everything works, as much as possible. No kitchen sink, no blackbox magic). My first distribution was Slackware, I hopped around for a bit until I landed on Archlinux and stuck with it for several years (I still use it on all my machines except the XPS, which is running Alpine now).

One of the things that happens when you run the same system over a certain length of time is: you install things, configure them, try them out, uninstall some, keep others, install new things, etc., etc. Which inevitably leads to a bit of clutter (~/.config files, /etc/ files, and so on), if you’re not diligent with removing all of those (which I am not).

So, in an effort to keep my brand-new, fresh-and-clean Alpine Linux install clutter-free (but mostly as an experiment and just for fun) I decided to keep the packages installed on the host at a minimum and install extra things in containers. As you can probably guess, I chose LXD to run these extra things. There are other, possibly better-suited, options out there. I do realize that, but I like LXD and I’m having fun with the experiment, which is what matters to me.

Side-note: as a bonus, those “extra things” run in a sandboxed environment. (woo!)

The More Interesting Part

The “recipe” (finally!).

The Host

For the most part, on top of alpine-base, the kernel and drivers, I have:

  • bluez
  • foot
  • git
  • gpg
  • lxd
  • nftables
  • openssh
  • pipewire / wireplumbler
  • sway / swaylock / swayidle
  • tmux
  • vim
  • wayland
  • wireguard
  • wl-clipboard
  • wpa_supplicant

At the time of writing, the total count of /etc/apk/world is 60 packages. Granted, this number doesn’t really mean much. Different distros package things differently. Sometimes things get split-up or merged, but I’m writing this here so I can look back and compare, because why not?

The dotfiles

Like many people do, I keep my dotfiles in a git repository, which I have cloned locally in ~/devel/dotfiles/ and I symlink from ~/.config/ and others to there:

aerc -> /home/chimo/devel/dotfiles/.config/aerc/
irssi -> /home/chimo/devel/dotfiles/.config/irssi/
lxc/
mbsync -> /home/chimo/devel/dotfiles/.config/mbsync/
mpv -> /home/chimo/devel/dotfiles/.config/mpv/
ncmpcpp -> /home/chimo/devel/dotfiles/.config/ncmpcpp/
newsboat -> /home/chimo/devel/dotfiles/.config/newsboat/
pulse/
qutebrowser -> /home/chimo/devel/dotfiles/.config/qutebrowser/
sway/config -> /home/chimo/devel/dotfiles/.config/sway/config
sway/swaybar/ -> /home/chimo/devel/swaybar/

The containers

I currently have the following containers running:

aerc
Mail client.
devel
Ephemeral container for development stuffs (right now there's nginx, php, etc. on there)
hugo
Static site generator. To preview posts I'm writing for this blog.
irssi
IRC client.
mbsync
Syncs my mailboxes locally.
mopidy
Music server. Play music from various sources, MPD daemon.
mpv
Watch YouTube, Twitch.
ncmpcpp
Music player client. MPD client for mopidy container (above).
newsboat
RSS reader syncing with my FreshRSS instance.
nextcloud
`nextcloudcmd` running periodically to sync files between my Nextcloud instance and my local machine.
opensmtpd
SMTP "relay" to send mail from aerc.

This allows me to "send" emails while offline and they'll get delivered whenever I get online again. This setup was inspired by Drew DeVault's blog post.

qutebrowser
Browsing the intertubes.
rbw
Password manager integrating with my vaultwarden instance.
signal
Instant messaging.
tut
Mastodon client.

GUI and Audio in Containers

I’ve setup a “gui” lxd profile and a “audio” lxd profile based on the forum posts below. Whenever a container needs either one, or both, I attach the appropriate profile(s) to it and things “just work”(TM).

File Sharing

No, not the peer-to-peer kind. :)

Whenever containers need to share files, for example: I want “aerc” and “mbsync” to share the same mbox directory. Or whenever the host and containers need to share files, for example: I want a container to read config files from ~/devel/dotfiles, I use LXD’s shared folders (idmapped mounts) mechanism.

I’ve automated the creation of my containers and have pre-sets to apply either the “gui” and/or “audio” profiles and setup the environment as-needed.

I can also provide one-off LXD profiles for containers that have specific needs.

The script is pretty rudimentary. I have a bunch of TODOs planned and a bit of a revamp after some “lessons learned”, but it does the trick for my use-case.

Again, I know things that do this already exist. I’m also aware of cloud-init and all that. But I prefer something that would work on a bare-metal host, on cloud-init environments, as well as non-cloud-init environments, etc.

Most importantly, I’m just tinkering and having fun. /shrug

Container Backups and Updates

I have a script that shows me pending updates in my swaybar, and another script I can run to update one, several, or all containers. It takes a snapshot before applying any upgrade.

I’ve written about how I handle updates and backups on my VPS before. Those processes run on a schedule since the VPS is running 24/7.

Interacting with the Containers

I have a set of scripts in ~/.local/bin/ (this folder is part of my $PATH environment variable) that translate calls to container binaries into a lxc exec command.

This is a terrible explanation. Maybe some examples are better.

I have ~/.local/bin/aerc.sh in which the content is essentially just lxc exec aerc -- aerc.

Some of the other scripts, such as newsboat.sh set some options: lxc exec newsboat -- sh -c 'newsboat -c ~/.config/newsboat/cache.db -C ~/.config/newsboat/config'

Others (ex: irssi.sh) have different behaviours depending on the arguments they are called with.

There might be better approaches, but this works for me and I find it quite flexible.