NixOS Docker Compose Autostarter
These nix config files are meant for enabling Docker on NixOS, configuring it, and then launching all docker compose files recursively in a specified directory. The script that launches the compose files also (likely inadvisably) opens ports in the firewall based on the ports specified in the compose files.
nixos-rebuild should tell you if a docker compose service fails to start when run, but you can also check the status of the service: `$ sudo systemctl status runDockerComposes.service`
Use the nix config files by importing them by adding the following to your "imports" section in your "configuration.nix":
imports =
[
./hardware-configuration.nix
...
./services/utilities/docker.enable.nix # Change to the directory you put the docker nix config files.
...
];
docker.enable.nix
{ ... }:
{
imports =
[
./docker.config.nix
];
virtualisation.docker.enable = true;
# Replace "[USERNAME]" to add your user to the "docker" group to manage Docker without sudo
users.users.[USERNAME].extraGroups = [ "docker" ];
virtualisation.docker.storageDriver = "overlay2";
# virtualisation.docker.registryMirrors = [ "https://example-mirror.com/" ];
# Set to the directory you would like docker to store its files
virtualisation.docker.daemon.settings = {
data-root = "/zfszero/docker/var";
};
}
Notes:
- Configurable lines:
- `./docker.config.nix`
- `users.users.[USERNAME].extraGroups = [ "docker" ];`
- `virtualisation.docker.registryMirrors = [ "https://example-mirror.com/" ];`
- `data-root = "/zfszero/docker/var";`
- `./docker.config.nix`
docker.config.nix
{ pkgs, ... }:
let
Compose_Directory = "/zfszero/docker/compose"; # Set to docker compose file root directory
yq_Directory = "${Compose_Directory}/../.misc"; # Set to directory for yq binary file storage, used for yml/port parsing.
bashPath = pkgs.bash;
wgetPath = pkgs.wget;
dockerPath = pkgs.docker;
iptablesPath = pkgs.iptables;
runDockerComposeScript = pkgs.writeScript "run-docker-composes.sh" ''
#!${bashPath}/bin/bash
error_file=$(mktemp)
COMPOSE_DIR="${Compose_Directory}"
COMPOSE_FILES=$(find "$COMPOSE_DIR" -type f -name "*docker-compose.yml")
YQ_DIR="${yq_Directory}"
mkdir -p "$YQ_DIR"
if [ ! -f "$YQ_DIR/yq" ]; then
${wgetPath}/bin/wget https://github.com/mikefarah/yq/releases/download/v4.42.1/yq_linux_amd64 -O $YQ_DIR/yq && chmod +x $YQ_DIR/yq
fi
for DOCKERCOMPOSEFILE in $COMPOSE_FILES; do
echo "Checking ports for $DOCKERCOMPOSEFILE"
PORTS=$($YQ_DIR/yq e '.services[].ports[]?' "$DOCKERCOMPOSEFILE" | sed 's/"//g' | sed -r 's#([0-9]+):[0-9]+(/udp|/tcp)?#\1\2#' | sed s/-/:/ | sed -r 's#([0-9]+)-[0-9]+(/udp|/tcp)?#\1\2#')
if [ ! -z "$PORTS" ]; then
for PORT in $PORTS; do
if [[ "$PORT" == *"/udp" ]]; then
PSANSU=$(echo "$PORT" | sed 's#/udp##')
echo "Opening UDP port $PSANSU for $DOCKERCOMPOSEFILE"
${iptablesPath}/bin/iptables -A INPUT -p udp --dport $PSANSU -j ACCEPT || echo "Failed to open UDP port $PSANSU for $DOCKERCOMPOSEFILE" >> "$error_file"
else
PSANST=$(echo "$PORT" | sed 's#/tcp##')
echo "Opening TCP port $PSANST for $DOCKERCOMPOSEFILE"
${iptablesPath}/bin/iptables -A INPUT -p tcp --dport $PSANST -j ACCEPT || echo "Failed to open TCP port $PSANST for $DOCKERCOMPOSEFILE" >> "$error_file"
fi
done
fi
if ${dockerPath}/bin/docker compose -f "$DOCKERCOMPOSEFILE" up -d; then
echo "Successfully started $DOCKERCOMPOSEFILE"
else
echo "Error starting $DOCKERCOMPOSEFILE"
error_=1
echo "Failed to start $DOCKERCOMPOSEFILE" >> "$error_file"
fi
done
if [ -s "$error_file" ]; then
echo "Errors encountered:"
cat "$error_file"
rm -f "$error_file"
exit 1
fi
'';
in
{
systemd.services.runDockerComposes = {
description = "Launch docker compose files";
after = [ "network.target" "docker.service" ]; # If docker compose files are on zfs storage, add "zfs.target" here
requires = [ "docker.service" ]; # and here
wants = [ "docker.service" ];
wantedBy = [ "multi-user.target" ];
script = "${runDockerComposeScript}";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "root";
# ExecStartPre = "-/run/current-system/sw/bin/sleep 10"; # Uncomment/adjust if you need to give time for other services to finish starting before this service
TimeoutStartSec = "8min"; # How long you want to give the service to start all docker compose files
};
};
}
Notes:
- Configurable variables:
- Compose_Directory
- yq_Directory
- ExecStartPre
- TimeoutStartSec
- yq binary downloaded and used rather than NixOS native package due to old version, differing syntax.
- Intended compose file naming scheme:
- [SERVICE NAME].docker-compose.yml (should work with just "docker-compose.yml")
To-do:
- checksum verification for yq binary?