Run and Auto-Update Docker Containers on NixOS

Run and Auto-Update Docker Containers on NixOS
📆 Thu Nov 21 2024 by Jacek Galowicz
(5 min. reading time)

Learn how to run Docker/Podman containers on NixOS while automatically updating and pruning old images. I use this on a Raspberry Pi 4, but the code in the article will run on any NixOS system.

A year ago, I set up Raspberry Pi 4 to run Home Assistant with a cheap USB Zigbee Gateway. Configuring Home Assistant has a little bit of a fiddly character. Still, it runs great with Zigbee products from all kinds of expensive and cheap Zigbree products like heating thermostats, switches, temperature and humidity sensors, light bulbs, etc.

This article is not about how to set up NixOS on a Raspberry Pi. If you would like to learn how to do that, please have a look at this very good official nix.dev tutorial. (I suggest using a fast USB3 stick instead of an SD card for the system disk.)

We are going to use the original Docker image instead of the native NixOS modules (browse the official Home Assistant NixOS module options here), because this is a nice example of how to run relevant services as a Docker image with less hassle than on other GNU/Linux distributions. NixOS modules and packages are generally well-maintained, but this is also an interesting alternative in case some module is not as fresh as we might need.

Running Home Assistant via Docker on NixOS

To add the Home Assistant Docker image to our system configuration to make it run as a service, add this new NixOS module file to your configuration folder:

# file: homeassistant.nix
{ ... }:

{
  virtualisation.oci-containers = {
    backend = "podman";
    containers.homeassistant = {
      volumes = [ "home-assistant:/config" ];
      environment.TZ = "Europe/Berlin";
      image = "ghcr.io/home-assistant/home-assistant:stable";
      extraOptions = [
        "--network=host"
        "--device=/dev/ttyACM0:/dev/ttyACM0"
      ];
    };
  };

  networking.firewall.allowedTCPPorts = [ 8123 ];
}

The module adds a new container called homeassistant to the system. The extraOptions are necessary to give it the capability to use the host network. The second line there passes through our Zigbee USB dongle into the container. Please note that your Zigbee dongle might pop up with a different /dev/... filesystem path - adapt it accordingly.

The last line about firewall settings is only necessary if your NixOS configuration sets networking.firewall.enable = true;.

Instead of Docker, we’re using the podman backend. Podman is interesting on NixOS because it works without a central daemon but instead starts images directly.

Add this new module file to the imports = [ ... ]; line of your configuration.nix (be that inside a flake or in /etc/nixos/) and rebuild your system with nixos-rebuild switch.

You should now be able to open http://<raspberry-ip>:8123 in your browser and access the configuration dialogue!

Automatically updating Home Assistant

Home Assistant has some self-update capabilities, but this is not the same as updating the Docker image and restarting the service.

We can automatically update all images on the system using systemd timers and the podman pull command:

Create another NixOS module file and add it to your configuration.nix’s imports clause or put its content into the module file we created earlier.

# file: update-containers.nix
{ ... }:

{
  systemd.timers.update-containers = {
    timerConfig = {
      Unit = "update-containers.service";
      OnCalendar = "Mon 02:00";
    };
    wantedBy = [ "timers.target" ];
  };
  systemd.services.update-containers = {
    serviceConfig = {
      Type = "oneshot";
      ExecStart = lib.getExe (pkgs.writeShellScriptBin "update-containers" ''
        images=$(${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)

        for image in $images; do
          ${pkgs.podman}/bin/podman pull "$image"
        done
      '');
    };
  };
}

Every Monday morning at 2:00, this systemd timer triggers an update of all running Podman images.

If you want to trigger this service manually, run sudo systemctl restart update-containers.service.

Updating the images alone does not restart the Home Assistant service. To do this, we write another systemd timer:

# file: restart-homeassistant.nix
{ ... }:

{
  systemd.timers.restart-homeassistant = {
    timerConfig = {
      Unit = "update-containers.service";
      OnCalendar = "Tue 02:00";
    };
    wantedBy = [ "timers.target" ];
  };
  systemd.services.restart-homeassistant = {
    serviceConfig = {
      Type = "oneshot";
      ExecStart = "${pkgs.systemd}/bin/systemctl try-restart podman-homeassistant.service";
    };
  };
}

This module restarts the podman-homeassistant.service, which runs the Docker image, every Tuesday at 2:00 in the morning. Of course, you can set the OnCalendar field to a point in time that is closer to the updates.

Again, save this part of the configuration in another NixOS module file and then add it to your system’s NixOS config imports clause, or add its content to an existing file. Then, switch the system to the new configuration.

Automatically pruning old images

A few months later, we might have multiple versions of Home Assistant images on our system while we only actually use the latest. This wastes disk space.

The podman NixOS module supports periodic weekly pruning out of the box already, so we don’t need to write a third systemd timer:

# file: prune-containers.nix
{ ... }:

{
  virtualisation.podman = {
    enable = true;
    autoPrune = {
      enable = true;
      flags = [ "--all" ];
    };
  };
}

Again, add this new file to your configuration folder and the imports clause of your NixOS configuration module, or add its content to an existing NixOS module. Then, rebuild the system using nixos-rebuild switch.

Summary

This NixOS configuration has been running on my Raspberry Pi hidden behind a shelf for a year now without needing attendance. I log into the system every few months to fix configuration format deprecations if there are any.

The systemd service definitions seem repetitive and can be shortened in different ways. In the Nixcademy Nix & NixOS 101 training, participants learn how to write rock-solid NixOS configurations and also their own configurable NixOS modules with best practice guidance as well as practical exercises, done together with assistance by the teacher if things don’t work out as expected.

Nixcademy on Twitter/X

Follow us on X

Nixcademy on LinkedIn

Follow us on LinkedIn

Get Online Nix(OS) Classes

Nixcademy online classes

Are you looking for...

  • a guided learning experience?
  • motivating group exercises?
  • a personal certificate?
Nixcademy Certificate

The Nixcademy Newsletter: Receive Monthly Nix(OS) Blog Digests

Nixcademy Newsletter

Receive a monthly digest of blog articles and podcasts with useful summaries to stay connected with the world of Nix and NixOS!

Stay updated on our latest classes, services, and exclusive discounts for newsletter subscribers!

Subscribe Now