/home/leela projects blog experiments
> pandoc -t html5 /run/user/leela/blog/nixos-compose.md

Running docker-compose projects declaratively using NixOS

Jan 23 2024

Recently, while migrating more of my infrastructure to NixOS, I’ve found myself wanting an easy solution to running docker compose projects in a declarative way.

I’ve managed to come up with a small module that I’m pretty happy with:

{ config, lib, pkgs, ... }: {
  config = {
    systemd.services = lib.pipe config.elia.compose [
      lib.attrsToList
      (map ({ name, value }:
        let config = (pkgs.writeText (name + ".yml") value);
        in {
          name = "docker-" + name;
          value = {
            serviceConfig = {
              ExecStart = "${pkgs.docker}/bin/docker compose -f ${config} up";
              ExecStop = "${pkgs.docker}/bin/docker compose -f ${config} down";
            };
            wantedBy = [ "multi-user.target" ];
            after = [ "docker.service" "docker.socket" ];
          };
        }))
      lib.listToAttrs
    ];
  };

  options = {
    elia.compose = lib.mkOption {
      type = lib.types.attrs;
      default = { };
    };
  };
}

When using this module, config.elia.compose becomes an attrset mapping project names (key) to a docker-compose.yml definition (value, string). If wanted, this could be trivially extended to allow writing the compose file in Nix instead of YAML, but I felt happy with this as-is and didn’t want to have to rewrite all my existing compose files. (If you wish to do so, Nix comes with toJSON, which can be used to write out the compose file with writeText, as seen in my module.)

Compared to just running the compose file imperatively, we get:

I’ve been very happy with this approach and hope this small blog post might be useful to someone else, too.

> cd ..