Infrastructure configuration needed to host the evolved-npcs application
  • Shell 39.8%
  • HCL 28.5%
  • Python 14%
  • Nix 11.1%
  • XSLT 5.8%
  • Other 0.8%
Find a file
tepichord 3ee1531777 Determine incremental deployment methodology (#28)
Closes #27

Automated PR created by forgejo-done adapter.

Reviewed-on: #28
2026-08-22 04:37:17 -07:00
control-plane Determine incremental deployment methodology (#28) 2026-08-22 04:37:17 -07:00
hc-vault Specify use of NIX for dependencies (#10) 2026-08-18 12:15:24 -07:00
tests Determine testing methodology (#14) 2026-08-19 08:56:13 -07:00
woodpecker Rename repo (#9) 2026-08-18 10:15:59 -07:00
worker Determine testing methodology (#14) 2026-08-19 08:56:13 -07:00
.gitignore Determine testing methodology (#14) 2026-08-19 08:56:13 -07:00
AGENTS.md Determine testing methodology (#14) 2026-08-19 08:56:13 -07:00
ARCHITECTURE.md Determine incremental deployment methodology (#28) 2026-08-22 04:37:17 -07:00
flake.lock Specify use of NIX for dependencies (#10) 2026-08-18 12:15:24 -07:00
flake.nix Determine testing methodology (#14) 2026-08-19 08:56:13 -07:00
PLAN.md Plan out system architecture (#13) 2026-08-18 23:48:05 -07:00
pyproject.toml Determine testing methodology (#14) 2026-08-19 08:56:13 -07:00
README.md Determine incremental deployment methodology (#28) 2026-08-22 04:37:17 -07:00

Milner

General-purpose Talos Linux cluster definition on libvirt/QEMU. Hosts multiple pods:

Getting Started

Install Nix:

sh <(curl -L https://nixos.org/nix/install)

Enter the dev shell (provides terraform, qemu, podman, and all other dependencies):

nix develop

How Changes Ship

Infrastructure changes ship as Terraform deltas. A single reviewed terraform planterraform apply reconciles exactly the declared delta against the running environment; it never recreates what did not change. There are two primitives — (a) in-place delta (always the default) and (b) -replace of a disposable VM (used only at the VM-level base infrastructure) — and one approach that is always rejected: per-change migration scripts (c).

The layer boundary

Different infrastructure layers support in-place deltas to different degrees. The table below is the authoritative boundary: use the default column for normal reconciliation and the recovery column only when a disposable node must be swapped.

Layer Real in-place delta? Default Recovery
VM, disk, libvirt network/pool No Terraform (a) Replace only the disposable VM (b); keep volume/network/pool separate and protected
Talos machine configuration Yes Terraform delta (a) Stage the changed config (staged_if_needing_reboot), never reset the node
Pods / Helm / Compose Yes Reconcile the release/chart (a) Roll back or reconcile, never recreate workers

(a) Terraform delta — always the default

Past the VM boundary, (a) is the default for every change, unconditionally. All changes run inside nix develop. A change is one value in one release/chart/config; applying it reconciles only that declaration:

terraform plan && terraform apply          # only the declared delta reconciles
helm upgrade --install woodpecker . --values values.yaml   # only that release
podman-compose -f hc-vault/docker-compose.yml up -d        # preserves ./vault_data

Always:

  • Run terraform plan and review the diff before any terraform apply. Never apply without explicit user confirmation.
  • Never commit Terraform state or secrets.
  • Prefer -replace over destroy+recreate when a resource genuinely cannot be diffed in place.
  • Never hand-run virsh/qemu-img for infrastructure lifecycle — all infra changes go through Terraform plan/apply.

(b) -replace only for a disposable VM

(b) is used only at the VM-level base infrastructure, and only for a disposable node. A VM created from an ISO is not a stateful resource Terraform can diff in place, so the honest delta for the VM itself is recreation. Replace the libvirt_domain only — never the disk, network, or pool:

terraform plan  -replace='libvirt_domain.control_plane'
terraform apply -replace='libvirt_domain.control_plane'

For (b) to be safe, the domain and its persistent volume must be separate resources so the volume is never a replacement target:

resource "libvirt_volume" "control_plane_disk" {
  lifecycle { prevent_destroy = true }
}

Always:

  • Keep the persistent volume/network/pool separate from the domain, with the volume protected via prevent_destroy.
  • Review every -replace plan before applying; confirm exactly which resource is the replacement target.
  • Never hand-run virsh/qemu-img on the volume or node.

(c) per-change migration scripts — always rejected

(c) is never used. Per-change migration scripts are rejected outright because they become a second source of truth that must be kept in sync with reality. The only acceptable script-adjacent pattern is a thin adoption/recovery helper that imports an existing VM once — reference-only, never a migration script per change.

cleanup.sh — full cluster teardown only

control-plane/cleanup.sh has exactly one purpose: full cluster teardown. It is the confirmation-gated tool for removing the entire control-plane environment (managed resources, kubeconfig/talos client contexts, cluster files). It is never a partial-recovery tool — recovery is -replace. Its destructive actions (stopping QEMU, removing managed cluster state) are reserved exclusively for that one full-teardown purpose and must never be pointed at an individual node or at persistent data — those are preserved and recovered via -replace (b). If the environment only needs a disposable node swapped, use (b), not cleanup.sh.

Clean install vs incremental adopt — one graph

There are two pathways but one Terraform graph, not two divergent instruction sets:

  • Clean installterraform apply from an empty state.
  • Incremental adopt / recover — import the existing VM's UUID → review plan → apply, using the same main.tf.

Provider-version caveat: the repo pins dmacvicar/libvirt ~>0.7; domain-UUID import via the provider is confirmed only on libvirt 0.9.6+. Documentation that references import/-replace adoption must not assume a provider version the repo is not yet on.

Origin Repos