{ pkgs, ... }: let sshKeyPath = "/home/lebowski/.ssh/id_ed25519"; sshKeyName = "V3"; serverName = "builder"; serverType = "ccx43"; snapshotType = "ccx13"; snapshotDesc = "nixos-builder"; location = "nbg1"; flakeDir = "/home/lebowski/NixOS"; sshCmd = "ssh -o StrictHostKeyChecking=no -o BatchMode=yes -i ${sshKeyPath}"; # Boot a builder from the NixOS snapshot builder-up = pkgs.writeShellApplication { name = "builder-up"; runtimeInputs = with pkgs; [ hcloud openssh ]; text = '' if hcloud --context builder server describe "${serverName}" &>/dev/null; then IP=$(hcloud --context builder server ip "${serverName}") echo "Builder already running at $IP" echo " builder-ssh" exit 0 fi SNAPSHOT_ID=$(hcloud --context builder image list --type snapshot -o noheader -o columns=id,description | grep "${snapshotDesc}" | awk '{print $1}' | head -1) if [ -z "$SNAPSHOT_ID" ]; then echo "ERROR: No snapshot '${snapshotDesc}' found." echo " Create one with: builder-snapshot" exit 1 fi echo "==> Creating ${serverType} in ${location}..." hcloud --context builder server create \ --name "${serverName}" \ --type "${serverType}" \ --image "$SNAPSHOT_ID" \ --location "${location}" \ --ssh-key "${sshKeyName}" IP=$(hcloud --context builder server ip "${serverName}") echo "==> Waiting for SSH..." for i in $(seq 1 30); do if ${sshCmd} "root@''${IP}" true 2>/dev/null; then break fi if [ "$i" -eq 30 ]; then echo "ERROR: SSH did not become available after 150s" exit 1 fi sleep 5 done ssh-keygen -R "$IP" 2>/dev/null || true ssh-keyscan -H "$IP" >> ~/.ssh/known_hosts 2>/dev/null echo "" echo "=== Builder ready ===" IP=$(hcloud --context builder server ip "${serverName}") ssh -i "${sshKeyPath}" -t "root@$IP" "tmux new-session -A -s build" ''; }; # SSH into the builder builder-ssh = pkgs.writeShellApplication { name = "builder-ssh"; runtimeInputs = with pkgs; [ hcloud openssh ]; text = '' if ! hcloud --context builder server describe "${serverName}" &>/dev/null; then echo "No builder found. Run builder-up first." exit 1 fi IP=$(hcloud --context builder server ip "${serverName}") ssh -i "${sshKeyPath}" -t "root@$IP" "tmux new-session -A -s build" ''; }; # Tear down the builder builder-down = pkgs.writeShellApplication { name = "builder-down"; runtimeInputs = with pkgs; [ hcloud openssh ]; text = '' if hcloud --context builder server describe "${serverName}" &>/dev/null; then IP=$(hcloud --context builder server ip "${serverName}") echo "==> Deleting '${serverName}' ($IP)..." hcloud --context builder server delete "${serverName}" ssh-keygen -R "$IP" 2>/dev/null || true echo " Done." else echo "No builder found." fi ''; }; # Create or update the NixOS builder snapshot # 1. Spins up a cheap server # 2. Installs NixOS via nixos-anywhere # 3. Rebuilds with latest config # 4. Sets up credentials (attic, git) # 5. Cleans up, snapshots, deletes server builder-snapshot = pkgs.writeShellApplication { name = "builder-snapshot"; runtimeInputs = with pkgs; [ hcloud openssh nixos-anywhere ]; text = '' TMP_SERVER="snapshot-tmp" MISSING="" [ -z "''${ATTIC_TOKEN:-}" ] && MISSING="$MISSING ATTIC_TOKEN" [ -z "''${FORGEJO_TOKEN:-}" ] && MISSING="$MISSING FORGEJO_TOKEN" [ -z "''${CACHIX_TOKEN:-}" ] && MISSING="$MISSING CACHIX_TOKEN" if [ -n "$MISSING" ]; then echo "ERROR: Missing environment variables:$MISSING" echo "" echo " export ATTIC_TOKEN=" echo " export FORGEJO_TOKEN=" echo " export CACHIX_TOKEN=" exit 1 fi echo "==> Creating ${snapshotType} for snapshot..." hcloud --context builder server create \ --name "$TMP_SERVER" \ --type "${snapshotType}" \ --image ubuntu-24.04 \ --location "${location}" \ --ssh-key "${sshKeyName}" IP=$(hcloud --context builder server ip "$TMP_SERVER") echo "==> Waiting for SSH..." for i in $(seq 1 30); do if ${sshCmd} "root@''${IP}" true 2>/dev/null; then break fi if [ "$i" -eq 30 ]; then echo "ERROR: SSH did not become available" hcloud --context builder server delete "$TMP_SERVER" exit 1 fi sleep 5 done ssh-keygen -R "$IP" 2>/dev/null || true ssh-keyscan -H "$IP" >> ~/.ssh/known_hosts 2>/dev/null echo "==> Installing NixOS via nixos-anywhere..." nixos-anywhere --flake "${flakeDir}#builder" "root@$IP" echo "==> Waiting for NixOS to boot..." sleep 15 ssh-keygen -R "$IP" 2>/dev/null || true for i in $(seq 1 30); do if ${sshCmd} "root@''${IP}" true 2>/dev/null; then break fi sleep 5 done ssh-keyscan -H "$IP" >> ~/.ssh/known_hosts 2>/dev/null echo "==> Rebuilding with latest config..." nixos-rebuild switch --flake "${flakeDir}#builder" --target-host "root@$IP" echo "==> Setting up credentials..." ${sshCmd} "root@''${IP}" "attic login axiomania https://cache.axiomania.org ''${ATTIC_TOKEN}" ${sshCmd} "root@''${IP}" bash -s -- "''${FORGEJO_TOKEN}" <<'SETUP' TOKEN="$1" git config --global credential.https://git.axiomania.org.helper store echo "https://lebowski:$TOKEN@git.axiomania.org" > /root/.git-credentials chmod 600 /root/.git-credentials SETUP ${sshCmd} "root@''${IP}" bash -s -- "''${CACHIX_TOKEN}" <<'CACHIX' echo "$1" | cachix authtoken --stdin CACHIX echo "==> Garbage collecting..." ${sshCmd} "root@''${IP}" "nix-collect-garbage -d && nix store optimise" # ── Delete old snapshot(s) ── OLD=$(hcloud --context builder image list --type snapshot -o noheader -o columns=id,description | grep "${snapshotDesc}" | awk '{print $1}') if [ -n "$OLD" ]; then echo "==> Deleting old snapshot(s)..." echo "$OLD" | while read -r sid; do hcloud --context builder image delete "$sid" done fi echo "==> Shutting down for clean snapshot..." hcloud --context builder server shutdown "$TMP_SERVER" sleep 10 echo "==> Creating snapshot..." hcloud --context builder server create-image --type snapshot --description "${snapshotDesc}" "$TMP_SERVER" echo "==> Deleting temporary server..." hcloud --context builder server delete "$TMP_SERVER" ssh-keygen -R "$IP" 2>/dev/null || true echo "" echo "=== Snapshot '${snapshotDesc}' created ===" echo " Use 'builder-up' to spin up a builder." ''; }; in { environment.systemPackages = [ builder-up builder-ssh builder-down builder-snapshot pkgs.hcloud ]; }