Archived
Fix nix-cache retry storms and lxc creation in create-proxmox-resource.sh
Two independent problems found while actually running the script: 1. nix build/nix-shell retry each unreachable substituter/builder up to 5x with backoff, per store path -- with nix-cache down this compounds into minutes of noise. scripts/env.sh gains nix_extra_opts(), which probes http://nix-cache and nixremote@nix-cache:22 once via plain curl/TCP (bypassing Nix's own retry logic entirely -- confirmed nix store ping still retries 5x even with a short connect-timeout) and exports the decision so create-proxmox-resource.sh and the sync-host-keys.sh subprocess it shells out to both reuse it instead of probing independently. 2. The actual failure: "archive contains no configuration file". pct restore expects a vzdump backup archive with embedded config; config.system.build.tarball is a plain CT template tarball -- wrong Proxmox mechanism entirely. Fixed to pct create against it as a vztmpl template instead, uploaded to /var/lib/vz/template/cache/ rather than /var/lib/vz/dump/. This same wrong claim had propagated into docs/auto-installer.md, README.md, and CLAUDE.md from when the script was first written -- corrected everywhere. Also: checks for an already-uploaded image on the node (fixed <flake_target>.tar.xz/.raw naming) before building, skipping build+upload entirely if found (--force-rebuild to always rebuild). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
This commit is contained in:
@@ -34,7 +34,7 @@ Usage: $0 --type lxc|vm --host <name> [options] (create)
|
||||
$0 --modify --vmid <n> [options] (reconfigure)
|
||||
|
||||
Create mode (default):
|
||||
--type lxc|vm lxc = container, built as a pct-restorable tarball.
|
||||
--type lxc|vm lxc = container, built as a CT template tarball.
|
||||
vm = VM, built as a Disko .raw disk image (UEFI/OVMF).
|
||||
--host <name> Which host identity to deploy -- matches
|
||||
config.networking.hostName (server, docker,
|
||||
@@ -45,10 +45,13 @@ Create mode (default):
|
||||
--vmid <n> Numeric VMID (default: next free, via
|
||||
\`pvesh get /cluster/nextid\` on the node).
|
||||
Refuses to run if this ID already exists.
|
||||
--disk-size <GB> lxc only, at create time: overrides the
|
||||
restored rootfs's absolute size.
|
||||
--disk-size <GB> lxc only: rootfs size for \`pct create\`
|
||||
(default: \$PROXMOX_DEFAULT_LXC_DISK_GB, ${PROXMOX_DEFAULT_LXC_DISK_GB}).
|
||||
--image <path> Use this local image/tarball instead of
|
||||
building one from the flake.
|
||||
checking the node / building one from the flake.
|
||||
--force-rebuild Skip the "does the node already have this
|
||||
image" check -- always build fresh and
|
||||
overwrite what's there.
|
||||
--allow-duplicate-host Required if --host already has a real
|
||||
deployment elsewhere (variables.nix's
|
||||
deployedTargets) -- otherwise refused, since
|
||||
@@ -101,6 +104,7 @@ bridge="$PROXMOX_BRIDGE"
|
||||
node="$PROXMOX_HOST"
|
||||
do_list=0
|
||||
allow_duplicate_host=0
|
||||
force_rebuild=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
@@ -119,6 +123,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--node) node="$2"; shift 2 ;;
|
||||
--list) do_list=1; shift ;;
|
||||
--allow-duplicate-host) allow_duplicate_host=1; shift ;;
|
||||
--force-rebuild) force_rebuild=1; shift ;;
|
||||
--modify) modify=1; shift ;;
|
||||
--dry-run) dry_run=1; shift ;;
|
||||
-h | --help) usage; exit 0 ;;
|
||||
@@ -288,6 +293,13 @@ fi
|
||||
|
||||
echo "Target: ${flake_target} (host=${host}, type=${type}) -> Proxmox resource '${name}'"
|
||||
|
||||
# Decide on nix-cache once, here -- this is the earliest point that needs
|
||||
# it (sync-host-keys.sh below needs nix-shell packages regardless of
|
||||
# whether an image ends up getting built later), and the decision is
|
||||
# exported so that subprocess -- and this script's own later build step,
|
||||
# if it gets there -- both reuse it instead of probing again.
|
||||
nix_extra_opts
|
||||
|
||||
# --- make sure this target has a registered host key --------------------
|
||||
echo
|
||||
echo "==> Ensuring host key exists and is registered..."
|
||||
@@ -325,61 +337,93 @@ if [[ "$dry_run" -eq 0 ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- build (or reuse) the image ------------------------------------------
|
||||
# --- resolve the remote path -- fixed naming (not the nix store's own
|
||||
# derivation-hash-based filename), so a later run can check for it by name.
|
||||
# lxc uploads as a CT *template* (Proxmox's "vztmpl" content type, under
|
||||
# iso_storage) -- config.system.build.tarball is a plain rootfs tarball,
|
||||
# not a vzdump backup archive, so it's created with `pct create ... vztmpl`,
|
||||
# not restored with `pct restore` (that expects backup-archive metadata
|
||||
# this tarball doesn't have, and fails with "archive contains no
|
||||
# configuration file").
|
||||
remote_dir="/var/lib/vz/import"
|
||||
remote_filename="${flake_target}.raw"
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
remote_dir="/var/lib/vz/template/cache"
|
||||
remote_filename="${flake_target}.tar.xz"
|
||||
fi
|
||||
remote_path="${remote_dir}/${remote_filename}"
|
||||
|
||||
# --- build (or reuse an image already on the node) ------------------------
|
||||
echo
|
||||
local_image=""
|
||||
remote_dir=""
|
||||
image_already_remote=0
|
||||
|
||||
if [[ -n "$image" ]]; then
|
||||
[[ -f "$image" ]] || { echo "ERROR: --image '${image}' not found." >&2; exit 1; }
|
||||
local_image="$image"
|
||||
echo "Using provided image: ${local_image}"
|
||||
elif [[ "$type" == "lxc" ]]; then
|
||||
remote_dir="/var/lib/vz/dump"
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would build: NIXOS_HOST_KEYS_DIR=${repo_root}/host-keys nix build --impure \\"
|
||||
echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.tarball"
|
||||
local_image="<built-tarball>"
|
||||
else
|
||||
echo "==> Building LXC tarball for ${flake_target}..."
|
||||
NIXOS_HOST_KEYS_DIR="${repo_root}/host-keys" nix build --impure \
|
||||
--no-use-registries --no-accept-flake-config \
|
||||
".#nixosConfigurations.${flake_target}.config.system.build.tarball" \
|
||||
--out-link "${repo_root}/result-${flake_target}"
|
||||
local_image="$(find "${repo_root}/result-${flake_target}/tarball" -maxdepth 1 -type f | head -1)"
|
||||
echo "Built: ${local_image}"
|
||||
fi
|
||||
elif [[ "$force_rebuild" -eq 1 ]]; then
|
||||
echo "--force-rebuild: skipping the existing-image check on ${node}."
|
||||
else
|
||||
remote_dir="/var/lib/vz/import"
|
||||
echo "==> Checking whether ${node} already has ${remote_path}..."
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would build: nix build .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript"
|
||||
echo "[dry-run] would run: sudo ./result-${flake_target} \\"
|
||||
echo "[dry-run] --pre-format-files host-keys/${flake_target}_ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key \\"
|
||||
echo "[dry-run] --pre-format-files host-keys/${flake_target}_ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub \\"
|
||||
echo "[dry-run] --build-memory 2048"
|
||||
local_image="<built-image>.raw"
|
||||
echo "[dry-run] would check: ssh ${ssh_target} -- test -f ${remote_path}"
|
||||
elif ssh "$ssh_target" "test -f '${remote_path}'" 2>/dev/null; then
|
||||
echo "Found it -- reusing, skipping build and upload (use --force-rebuild to override)."
|
||||
image_already_remote=1
|
||||
else
|
||||
echo "==> Building Disko image script for ${flake_target}..."
|
||||
nix build --no-use-registries --no-accept-flake-config \
|
||||
".#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript" \
|
||||
--out-link "${repo_root}/result-${flake_target}"
|
||||
echo "==> Running it (builds the .raw image in a temporary QEMU VM, needs sudo)..."
|
||||
( cd "$repo_root" && sudo "./result-${flake_target}" \
|
||||
--pre-format-files "host-keys/${flake_target}_ssh_host_ed25519_key" /etc/ssh/ssh_host_ed25519_key \
|
||||
--pre-format-files "host-keys/${flake_target}_ssh_host_ed25519_key.pub" /etc/ssh/ssh_host_ed25519_key.pub \
|
||||
--build-memory 2048 )
|
||||
local_image="$(find "$repo_root" -maxdepth 1 -name "*.raw" -newer "${repo_root}/result-${flake_target}" | head -1)"
|
||||
if [[ -z "$local_image" ]]; then
|
||||
echo "ERROR: expected a .raw image after the build but didn't find one in ${repo_root}." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Built: ${local_image}"
|
||||
echo "Not found -- will build."
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- upload ---------------------------------------------------------------
|
||||
if [[ "$image_already_remote" -eq 0 && -z "$local_image" ]]; then
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would build: NIXOS_HOST_KEYS_DIR=${repo_root}/host-keys nix build --impure \\"
|
||||
echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.tarball"
|
||||
local_image="<built-tarball>"
|
||||
else
|
||||
echo "==> Building LXC tarball for ${flake_target}..."
|
||||
NIXOS_HOST_KEYS_DIR="${repo_root}/host-keys" nix build --impure \
|
||||
--no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \
|
||||
".#nixosConfigurations.${flake_target}.config.system.build.tarball" \
|
||||
--out-link "${repo_root}/result-${flake_target}"
|
||||
local_image="$(find "${repo_root}/result-${flake_target}/tarball" -maxdepth 1 -type f | head -1)"
|
||||
echo "Built: ${local_image}"
|
||||
fi
|
||||
else
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would build: nix build .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript"
|
||||
echo "[dry-run] would run: sudo ./result-${flake_target} \\"
|
||||
echo "[dry-run] --pre-format-files host-keys/${flake_target}_ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key \\"
|
||||
echo "[dry-run] --pre-format-files host-keys/${flake_target}_ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub \\"
|
||||
echo "[dry-run] --build-memory 2048"
|
||||
local_image="<built-image>.raw"
|
||||
else
|
||||
echo "==> Building Disko image script for ${flake_target}..."
|
||||
nix build --no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \
|
||||
".#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript" \
|
||||
--out-link "${repo_root}/result-${flake_target}"
|
||||
echo "==> Running it (builds the .raw image in a temporary QEMU VM, needs sudo)..."
|
||||
( cd "$repo_root" && sudo "./result-${flake_target}" \
|
||||
--pre-format-files "host-keys/${flake_target}_ssh_host_ed25519_key" /etc/ssh/ssh_host_ed25519_key \
|
||||
--pre-format-files "host-keys/${flake_target}_ssh_host_ed25519_key.pub" /etc/ssh/ssh_host_ed25519_key.pub \
|
||||
--build-memory 2048 )
|
||||
local_image="$(find "$repo_root" -maxdepth 1 -name "*.raw" -newer "${repo_root}/result-${flake_target}" | head -1)"
|
||||
if [[ -z "$local_image" ]]; then
|
||||
echo "ERROR: expected a .raw image after the build but didn't find one in ${repo_root}." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Built: ${local_image}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- upload (skip entirely if reusing an image already on the node) ------
|
||||
echo
|
||||
remote_path="${remote_dir}/$(basename "${local_image}")"
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
if [[ "$image_already_remote" -eq 1 ]]; then
|
||||
: # nothing to upload
|
||||
elif [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would upload: scp ${local_image} ${ssh_target}:${remote_path}"
|
||||
else
|
||||
echo "==> Uploading to ${node}:${remote_path}..."
|
||||
@@ -391,9 +435,9 @@ fi
|
||||
echo
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
echo "==> Creating LXC container ${vmid} (${name})..."
|
||||
restore_cmd="pct restore ${vmid} ${remote_path} --storage ${storage} --hostname ${name} --cores ${cores} --memory ${memory} --net0 name=eth0,bridge=${bridge},ip=dhcp"
|
||||
[[ -n "$disk_size" ]] && restore_cmd="${restore_cmd} --rootfs ${storage}:${disk_size}"
|
||||
remote "$restore_cmd"
|
||||
local_disk_size="${disk_size:-$PROXMOX_DEFAULT_LXC_DISK_GB}"
|
||||
create_cmd="pct create ${vmid} ${iso_storage}:vztmpl/${remote_filename} --rootfs ${storage}:${local_disk_size} --hostname ${name} --cores ${cores} --memory ${memory} --net0 name=eth0,bridge=${bridge},ip=dhcp"
|
||||
remote "$create_cmd"
|
||||
remote "pct start ${vmid}"
|
||||
else
|
||||
echo "==> Creating VM ${vmid} (${name})..."
|
||||
|
||||
Reference in New Issue
Block a user