Files
nixos-at-home/modules/common/docker-host.nix

123 lines
3.4 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.docker-host-incus;
containerName = "docker-host";
in
{
options.services.docker-host-incus = {
enable = mkEnableOption "Incus Docker host container";
image = mkOption {
type = types.str;
default = "images:ubuntu/24.04";
description = "Incus image to use for the Docker host container";
};
network = mkOption {
type = types.str;
default = "internalbr0";
description = "Incus bridge network to attach the container to";
};
ip = mkOption {
type = types.str;
default = "10.0.0.2";
description = "Static IP for the Docker host container";
};
gateway = mkOption {
type = types.str;
default = "10.0.0.1";
description = "Gateway for the container network";
};
dns = mkOption {
type = types.str;
default = "1.1.1.1";
description = "DNS server for the container";
};
exposeDockerTcp = mkOption {
type = types.bool;
default = false;
description = "Expose Docker TCP API on port 2375 (internal bridge only)";
};
};
config = mkIf cfg.enable {
# Ensure incus + jq are available on the host
environment.systemPackages = with pkgs; [ incus-lts jq ];
# ── Systemd oneshot that creates/starts the Docker host container ──
systemd.services.incus-docker-host = {
description = "Incus Docker Host Container (${containerName})";
after = [ "incus.service" ];
requires = [ "incus.service" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ incus-lts jq ];
script = ''
set -euo pipefail
create_container() {
echo "Creating ${containerName} from ${cfg.image}..."
incus init "${cfg.image}" "${containerName}" \
--config security.privileged=true \
--config boot.autostart=true
# Attach to the internal bridge
incus config device add "${containerName}" eth0 nic \
nictype=bridged \
parent="${cfg.network}"
# Inject cloud-init to install Docker
incus config set "${containerName}" user.user-data - <<'CLOUD_EOF'
#cloud-config
package_update: true
packages:
- docker.io
- docker-compose-v2
- curl
runcmd:
- systemctl enable docker.service
- systemctl start docker.service
- sleep 2
- docker info || echo "Docker not ready yet, will start on boot"
CLOUD_EOF
# Set a static lease
${optionalString (cfg.ip != "auto") ''
incus config device set "${containerName}" eth0 ipv4.address "${cfg.ip}"
''}
incus start "${containerName}"
echo " ${containerName} created and started"
}
# Main logic
if ! incus list --format json 2>/dev/null | jq -e '.[] | select(.name == "${containerName}")' >/dev/null 2>&1; then
create_container
else
STATUS=$(incus list --format json 2>/dev/null | jq -r '.[] | select(.name == "${containerName}") | .status')
if [ "$STATUS" != "Running" ]; then
echo "${containerName} is $STATUS starting..."
incus start "${containerName}"
else
echo "${containerName} already running"
fi
fi
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
};
};
}