{ config, pkgs, lib, ... }: with lib; { options.services.dockerStacks = mkOption { type = types.attrsOf (types.submodule { options = { path = mkOption { type = types.str; }; envFile = mkOption { type = types.nullOr types.path; default = null; }; ports = mkOption { type = types.listOf types.int; default = [ ]; }; # New option to pass raw systemd serviceConfig serviceConfig = mkOption { type = types.attrs; default = { }; description = "Extra systemd serviceConfig options for this stack."; }; }; }); default = { }; }; config = { virtualisation.docker.enable = true; virtualisation.docker.daemon.settings.dns = [ "1.1.1.1" "8.8.8.8" ]; networking.firewall.allowedTCPPorts = flatten (mapAttrsToList (name: value: value.ports) config.services.dockerStacks); systemd.services = mapAttrs' (name: value: nameValuePair "${name}_stack" { description = "Docker Compose stack: ${name}"; after = [ "network.target" "docker.service" "docker.socket" "agenix.service" ]; wants = [ "docker.socket" "agenix.service" ]; requires = [ "docker.service" ]; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ git docker docker-compose bash ]; # We merge the base config with the custom 'serviceConfig' from the submodule serviceConfig = recursiveUpdate { Type = "oneshot"; WorkingDirectory = value.path; User = "root"; ExecStartPre = "${pkgs.bash}/bin/bash -c 'while [ ! -S /var/run/docker.sock ]; do sleep 1; done'"; ExecStart = "${pkgs.docker-compose}/bin/docker-compose up -d --remove-orphans"; ExecStop = "${pkgs.docker-compose}/bin/docker-compose down"; RemainAfterExit = true; EnvironmentFile = mkIf (value.envFile != null) [ value.envFile ]; } value.serviceConfig; }) config.services.dockerStacks; }; }