123 lines
3.4 KiB
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;
|
|
};
|
|
};
|
|
};
|
|
}
|