# Static inventory — k3s node IPs ALIGNED with infra/tofu defaults (Proxmox migration IPs), # but the SSH target is the pfSense NAT (see NAT HAND-OFF below) — this file is # HAND-MAINTAINED and must NOT be regenerated from `tofu output -raw ansible_inventory`. # # k3s_primary = the --cluster-init server (embedded-etcd founder + Cilium k8sServiceHost). # k3s_secondary = the two servers that join it. All three are servers (HA embedded etcd). # # NAT HAND-OFF (LIVE): the nodes live on the Proxmox LAN at 192.168.1.50/.51/.52, and # Ansible reaches them over the pfSense NAT at 192.168.2.102, port-forwarded as # 2250 -> 192.168.1.50:22, 2251 -> 192.168.1.51:22, 2252 -> 192.168.1.52:22. # Therefore `ansible_host`/`ansible_port` below are the NAT SSH target (192.168.2.102:225x), # and each node's REAL 192.168.1.x lives in the `k3s_node_ip` var — consumed for the k3s # --node-ip, the API/tls-san endpoint (k3s_api_host), and general in-cluster parity. # This file is HAND-MAINTAINED for the NAT: do NOT blindly regenerate it from # tofu output -raw ansible_inventory # because tofu has no knowledge of the pfSense NAT and would overwrite the mapping below. [k3s_primary] k3s-dev-1 ansible_host=192.168.2.102 ansible_port=2250 k3s_node_ip=192.168.1.50 [k3s_secondary] k3s-dev-2 ansible_host=192.168.2.102 ansible_port=2251 k3s_node_ip=192.168.1.51 k3s-dev-3 ansible_host=192.168.2.102 ansible_port=2252 k3s_node_ip=192.168.1.52 [k3s_servers:children] k3s_primary k3s_secondary # The hypervisor/workstation host itself (target of host-prereqs.yml). Its become # (sudo) password comes from the vaulted group_vars/hypervisor/vault.yml. [hypervisor] localhost ansible_connection=local # VPN egress-gateway node(s) — run the node-level split-tunnel OpenVPN + kill-switch # (ADR-0015, playbooks/vpn.egress.gateway.yml). Initially just k3s-dev-3 (single gateway; # add a second host here for Cilium Egress Gateway HA). Members are already defined above # with their ansible_host under k3s_secondary; this group only tags the egress role target. [vpn_egress_gateway] k3s-dev-3 # The Proxmox HYPERVISOR host itself (NOT a k3s node) — target of playbooks/backup-s3.yml, # which stands up the off-cluster versitygw S3 backup target for Velero + CNPG. Reached DIRECTLY # over SSH as root (its own 192.168.2.102:22, the pfSense NAT to the host's 192.168.1.9), so # it overrides the cluster-wide `ansible_user: k3s` from group_vars/all.yml. Distinct from the # `hypervisor` group above (that is `localhost` for host-prereqs.yml, a different machine). [proxmox_hosts] proxmox ansible_host=192.168.2.102 ansible_port=22 ansible_user=root ansible_ssh_private_key_file=~/.ssh/home_network # ============================================================================= # PROD (k3s-prod-*) — Phase 7 PREP ONLY. See infra/PROD-PROVISIONING.md (T-7.1, # especially §5/§6) and DECISIONS-LOG.md "PHASE 7 OPENED" (2026-08-31). Node 1 # (`k3s-prod-1`, HP EliteDesk 800 G5 Mini) is on the bench and SSH-reachable; nodes 2 # and 3 have not arrived. This section is inventory/group_vars scaffolding only — # nothing has been run against node 1, and the operator has decided the CLUSTER stays # uninitialized (no k3s install, no --cluster-init) until all three nodes are present # (decision #2 in the DECISIONS-LOG entry above) — no single-server-etcd window, no # later member-add dance. # # KEY DIFFERENCE FROM DEV: prod sits DIRECTLY on the 192.168.2.x LAN — no pfSense NAT # hop in front of it. Dev's `ansible_host` above is a NAT target (192.168.2.102:225x) # that port-forwards to the node's real 192.168.1.x address, which is why dev also # carries a separate `k3s_node_ip` per host. Prod needs no such split: `ansible_host` # below IS already the node's real, routable address, so `k3s_node_ip` is set to the # SAME value (kept explicit, not omitted, only so the k3s_server role's hostvar lookup # — see the GAP note below — has something correct to find once that gap is closed). # `ansible_port` is the node's own real sshd (22). See group_vars/k3s_prod/vars.yml for # the k3s_api_host override that reflects this directly-reachable topology. # # GROUP-NAME GAP: CLOSED 2026-08-31. The roles and playbooks used to hardcode the # LITERAL dev groups (`hosts: k3s_servers` / `k3s_primary` / `k3s_secondary`, and # `groups['k3s_primary']` inside roles/k3s_server + roles/cilium), so nothing could # select the k3s_prod_* groups below. Both halves are now variables, deliberately kept # separate because Ansible resolves them at different times: # * WHICH HOSTS A PLAY RUNS ON — `k3s_hosts` (whole-cluster plays), `k3s_primary_hosts` # / `k3s_secondary_hosts` (k3s.yml's split), each defaulting to the dev group. # EXTRA VARS ONLY: a play's `hosts:` is templated before any host exists, so # group_vars cannot be read there. Prod: `-e k3s_hosts=k3s_prod` etc. # * WHICH GROUP A ROLE MEANS BY "MY PRIMARY"/"MY SERVERS" — `k3s_primary_group` / # `k3s_servers_group`, set in group_vars/all.yml (dev) and group_vars/k3s_prod/vars.yml # (prod), so roles resolve their own environment with NO per-run flag — the same # mechanism k3s_api_host already used. # Running with no extra vars is byte-for-byte the dev behaviour it always had. See # infra/ansible/README.md "Targeting prod" and PROD-PROVISIONING.md §6. # ============================================================================= [k3s_prod_primary] k3s-prod-1 ansible_host=192.168.2.10 ansible_port=22 k3s_node_ip=192.168.2.10 [k3s_prod_secondary] # Uncomment a node only once it is actually on the bench and answering SSH, so no play can # target a host that isn't there. k3s-prod-2 came online 2026-09-01 (imaged identically to # node 1: same HP EliteDesk 800 G5 Mini, same BIOS R21 02.26.00, same i7-9700T, same # partition layout) but with a SAMSUNG PM9A1 NVMe instead of node 1's 2011 SATA C400 — the # nodes are NOT hardware-symmetric, which matters for the §2 Longhorn/etcd disk decision. # node 3 remains commented until it is racked. k3s-prod-2 ansible_host=192.168.2.11 ansible_port=22 k3s_node_ip=192.168.2.11 # k3s-prod-3 ansible_host=192.168.2.12 ansible_port=22 k3s_node_ip=192.168.2.12 # Umbrella group group_vars/k3s_prod/ attaches to (mirrors dev's [k3s_servers:children] # pattern; named to match the group_vars directory so Ansible auto-loads it). [k3s_prod:children] k3s_prod_primary k3s_prod_secondary