Files
home-network-k3s/decisions/adr-0017-single-environment-bare-metal-production.md
jakman85andClaude Opus 5.5 250fb54154 docs: bring the specification in line with the accepted implementation
The operator ruled the implementation repo's main (50c8719) the latest
and accepted state. Sync the spec to it.

Accepted changes are reflected: one bare-metal environment, TrueNAS bulk
storage, R2 off-cluster backup, CloudNativePG, single-node VPN gateway
with Cilium fail-closed, inbound WireGuard, per-app auth sidecars,
Renovate, the CI gate as approver, and the pm-agent. New ADR-0017..0025;
ADR-0016 corrected and accepted; R-* IDs unchanged, eight amended.

Where the implementation has simply not met a requirement, the
requirement stands and the shortfall is recorded in docs/16-known-gaps.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-08 22:46:00 -06:00

3.7 KiB
Raw Permalink Blame History

ADR-0017 — Single environment: bare-metal production; dev retired

  • Status: Accepted
  • Date: 2026-11-08
  • Serves: R-PLAT-01, R-ENV-01/02/03, R-STATE-01/02
  • Supersedes: ADR-0001 provisioning clause only ("Nodes are provisioned by OpenTofu (libvirt, dev) / bare-metal imaging (prod) + cloud-init, and joined/configured by Ansible"). The rest of ADR-0001 stands: k3s, 3-node HA on embedded etcd, and the bundled components disabled at install.
  • Accepted by: operator ruling of 2026-11-08 that the implementation is the accepted state; effective on merge.

Context

ADR-0001 and the original specs assumed two environments: a dev/test cluster of local libvirt KVMs, provisioned by OpenTofu and cloud-init, and a prod cluster of 3 mini-PCs, kept design-only until a later phase.

Delivery did not stay on that path. The workstation host ran out of RAM and disk, so dev moved to VMs on a Proxmox host. The operator opened Phase 7 on 2026-08-31, and prod was built on 3 HP EliteDesk Mini bare-metal nodes. On 2026-09-12/13 prod took over the apex home.keithjackson.dev, and the dev VMs were stopped. The storage pools of the host those VMs ran on then moved to a standalone NAS, and the migration plan retires that host.

Keeping "dev/test" in the specification now describes something that does not exist. It also leaves R-ENV-01 and R-ENV-03 unsatisfiable as written.

Decision

The platform has one live environment: prod, 3× mini-PC bare metal.

  • Create is an imaged, checklist-driven operator step: there is no hypervisor API to call on bare metal. The checklist is infra/PROD-PROVISIONING.md in the implementation repo.
  • Baseline, cluster-join and manage are code: Ansible owns all of them, including the first-boot baseline that cloud-init used to carry. Cloud-init is not part of node provisioning.
  • OpenTofu is not part of node provisioning.
  • The Kubernetes API has one HA endpoint, a kube-vip virtual IP across the 3 servers. The address a joining node uses during a cold bootstrap is deliberately a different one, because the virtual IP does not exist until a cluster does.
  • GitOps configuration stays one base plus an environment-scoped overlay. No second environment exists, but the separation is kept, so a future environment needs overrides only.
  • With no staging surface, the safeguard is the merge gate: every change reaches prod through the PR gate and Flux. Destructive or fault-injecting tests against prod are deliberate, scheduled exercises.

Alternatives considered

  • Keep a dev cluster on Proxmox. Rejected: the migration plan retires that host, and dev does not move to the NAS, which is storage only.
  • Rebuild dev as local KVMs on the workstation. Rejected: that is the path already abandoned for lack of RAM and disk.
  • Drive bare metal through Cluster API or PXE automation. Deferred: imaging plus Ansible is sufficient and lower-complexity for 3 nodes.

Consequences

  • The spec stops gating work on a dev cluster; R-ENV-01/02/03 and R-PLAT-01 are reworded to match.
  • Conformance runs against prod. There is no separate cluster on which to absorb a bad change, so the static gate and the discipline around fault injection carry more weight.
  • Node-rebuild is exercised by hand rather than by a destroy/rebuild pipeline; a node is reimaged and rejoined through the checklist and Ansible.
  • The clusters/dev overlay, infra/tofu and dev defaults remaining in the implementation repo are inert. Removing them is implementation cleanup, and a conformance check that pins them must change with them.
  • ADR-0001's note that prod must not go below 3 servers stands, and it has no dev counterpart.