# Central YAML Source of Truth for Nathan's Lab (2026) # Edit and commit this file; Ansible playbooks should read this as canonical. lab_name: "nathan-lab-2026" canonical_source: "ansible/group_vars/all.yml" # The standard operational user created on every managed host. # Override per-host in host_vars/ if a node uses a different login. lab_ansible_user: "chester" # Omada Open API credentials are sourced from the encrypted vault file. omada_client_id: "{{ vault_omada_client_id }}" omada_client_secret: "{{ vault_omada_client_secret }}" omada_id: "{{ vault_omada_id }}" omada_base_url: "{{ vault_omada_base_url }}" networks: main: vlan: 1 cidr: "10.0.0.0/24" dhcp_pool: "10.0.0.100-10.0.0.240" gateway: "10.0.0.1" purpose: "Family / wired / main SSID" infra: vlan: 10 cidr: "10.0.10.0/24" reserved: "10.0.10.2-10.0.10.50" purpose: "Management / Proxmox / NAS / Heimdall mgmt" iot: vlan: 50 cidr: "10.0.50.0/24" dhcp_pool: "10.0.50.100-10.0.50.199" purpose: "IoT devices (Omada)" guest: vlan: 30 cidr: "10.0.30.0/24" dhcp_pool: "10.0.30.100-10.0.30.200" purpose: "Guest WiFi (isolated)" compute: vlan: 200 cidr: "10.0.200.0/24" purpose: "Swarm / AI grid / ephemeral compute" lab_hosts: er7212pc: role: gateway current_ip: "10.0.0.2" desired_ip: "10.0.0.2" note: "DHCP + Omada controller" pve01: physical_backing_host: "pve04" role: proxmox current_ip: "10.0.0.201" desired_ip: "10.0.10.11" pve02: role: proxmox current_ip: "10.0.0.202" desired_ip: "10.0.10.12" pve03: role: proxmox current_ip: "10.0.0.203" desired_ip: "10.0.10.13" pve04: replacement_status: "retired-identity-now-backing-pve01" role: retired_physical_alias current_ip: "10.0.0.204" desired_ip: "10.0.10.14" swarm-manager-1: current_ip: "10.0.0.211" desired_ip: "10.0.200.11" swarm-manager-2: current_ip: "10.0.0.212" desired_ip: "10.0.200.12" swarm-manager-3: current_ip: "10.0.0.213" desired_ip: "10.0.200.13" statler: role: standalone_vm current_ip: "10.0.0.210" desired_ip: "10.0.0.210" hypervisor_host: "pve02" note: "Standalone Ubuntu 24.04 VM planned on pve02 with 2 vCPU, 10 GB RAM, and 32 GB disk." swarm-worker-1: current_ip: "10.0.0.221" desired_ip: "10.0.200.21" swarm-worker-2: current_ip: "10.0.0.222" desired_ip: "10.0.200.22" swarm-worker-3: current_ip: "10.0.0.223" desired_ip: "10.0.200.23" ai-lenovo: current_ip: "10.0.0.220" desired_ip: "10.0.200.20" onboarding_status: "tbd-needs-onboarding-like-heimdall" ansible_managed: false note: "Pending onboarding workflow before inclusion in active automation and monitoring groups." synology: current_ip: "10.0.0.249" desired_ip: "10.0.10.40" terramaster: current_ip: "10.0.0.250" desired_ip: "10.0.10.41" waldorf: current_ip: "10.0.0.251" desired_ip: "10.0.200.30" lifecycle_status: "retired-shutdown" ansible_managed: false monitoring_enabled: false note: "Retired host; excluded from active monitoring and deployment inventories." watchtower: current_ip: "10.0.0.200" desired_ip: "10.0.10.200" heimdall: role: beelink current_ip: null desired_ip: mgmt: "10.0.10.2" lan: "10.0.0.50" # === MONITORING INFRASTRUCTURE === # Environment-specific configuration for monitoring stack monitoring: stack_user: "chester" heimdall_redis: "10.0.0.151:6379" watchtower_ip: "10.0.0.200" grafana_domain: "grafana.castaldifamily.com" uptime_domain: "status.castaldifamily.com" dozzle_domain: "logs.castaldifamily.com" authentik_host: "https://sso.castaldifamily.com" # grafana_admin_password: DEFINE IN VAULT # === EDGE ROUTING TOPOLOGY === # Canonical ingress model: Traefik runs on a dedicated edge host outside Swarm. # Swarm and standalone hosts publish routes through traefik-kop agents. edge_routing: ingress_mode: "external-traefik" edge_host: name: "heimdall" ip: "10.0.0.151" ssh_port: 22 http_port: 80 https_port: 443 integration: # Watchtower-hosted traefik-kop instance (publishes Watchtower container routes) agent_image: "ghcr.io/jittering/traefik-kop:latest" redis_addr: "10.0.0.151:6379" bind_ip: "10.0.0.200" # Watchtower IP — correct for routes originating on Watchtower swarm: # Swarm-hosted traefik-kop instance (publishes Swarm service routes) # bind_ip MUST be a Swarm node IP — the Swarm routing mesh makes published # ports available on ALL nodes, so Traefik routes inbound requests here. bind_ip: "10.0.0.212" # swarm-manager-2 (current Leader; was swarm-manager-1 before it went down) proxy_network: "proxy-net" # Swarm overlay network; separate from heimdall's bridge of same name stack_deploy_target: "swarm-manager-2" migration_rules: deploy_traefik_in_swarm: false use_external_proxy_network: true notes: - "Services should attach to swarm overlay proxy-net for east-west traffic." - "Ingress is terminated by external Traefik at 10.0.0.151 via traefik-kop updates." # Per-stack placement node overrides. # Update when the deploy target node changes (e.g., after node replacement). gitea_placement_node: "swarm-manager-2" authentik_placement_node: "swarm-manager-2" # === SERVICE SECRETS (set via: ansible-vault encrypt_string) === vault_gitea_db_password: !vault | $ANSIBLE_VAULT;1.1;AES256 34623365623337336535656164623637656633356661373162356438646637333932663765323134 6261626565646166353966393366666434356434333263330a333666393765646233303663363738 65616665393235323132623462373435373637363262363539626163373061643930393730346633 3232373866663034310a343661306634313766313765623439626339353635626232663662323365 6666 vault_authentik_secret_key: !vault | $ANSIBLE_VAULT;1.1;AES256 61373834613362356638303166376135613133616139613963333632613430636136623062373161 6335636331386565386139376234663362396361653463660a613834313263653039376363396264 62383166346563326630323734643462326438643436626565656633636234323835333033353130 3535306539626339320a323431666164353038323166633663656265613266366535623130323165 38353833393934393764376331333464663337616432623033303830393464303966643036656538 34396337363163663566383063396130616530633363636461343531636438303963653733343830 66636165656563653164383364643032373135666263316137623761656332316130313235623232 33623462343639366566 vault_authentik_postgres_password: !vault | $ANSIBLE_VAULT;1.1;AES256 37356530373764353038343038663662333535323436336663613239333234363036626462656130 3138313535353838306563663565663230646561313234390a313166623232383364623766383961 30363065373065353365616239663562333833313139636137616561616465656462613238323932 3630333538366430370a616263633263336436303662373530323161316534313737366633643535 30326636383131353265613463363431666536313966366364666564623637343737 vlan_defaults: dns_domain: "home.lab" ntp_servers: - "10.0.10.2" # Plex bootstrap claim token — used only on first server claim. vault_plex_claim: !vault | $ANSIBLE_VAULT;1.1;AES256 31373365323534353264373735363937623566646633653434613038396463303164396138306661 3130323134656463383835366130663632323561326265350a653162643064643563383738373637 36363135613735663037303036613637313431336139343430313963393930303532666366336365 3734386639393336310a323964386233346134616164656663393731376632643037313734323830 65366334356531623339643066373237306263323063383963363330346665316435 # Authentik outpost tokens for standalone arr services on statler. vault_authentik_token_sonarr: !vault | $ANSIBLE_VAULT;1.1;AES256 39303463306665356436626265653339663163613464366237663234376135306366303739343266 3762646230666263393330373833393037613165373337380a336663646161613534353232663761 65376666663063643066323831366265633337653630666235636234393130646361383032383032 3433393235633762390a376561303866373739613663333461643938353931626134336665383164 34346538376436313438313733393963303735646632323739313137626466356138636266396434 61363737636139386665616438646439366139303739646530316566373563306565623637363661 343938653662646132373565303836353030 vault_authentik_token_radarr: !vault | $ANSIBLE_VAULT;1.1;AES256 32363735353663623031356362323765616232326234333564323839626236653634626263313765 6335653537656531396431366662616163366166633462390a346363633364363866373732373939 61666261616266333465393837383337313565613539303732396530333833666563653139353238 6537383336613933370a333662323339396463353134363635383430353133646331376533303861 30303765373566353633643261376430363837386239363261396235333033636563366231323564 35643564663866653831663633333436653330363130656631356166363731356639643238656530 643062636137396333383438623534346636 vault_authentik_token_sabnzbd: !vault | $ANSIBLE_VAULT;1.1;AES256 30373635366337343236353866623234383665386461356637353534666461613466373463616531 3837646263643864636331343364663563666531333861660a626335393762353862663564656465 61373430336336373062623563633832383261333035353432666265313435363132316561383130 3236643962313765630a386634313331643639363035623663616166313532623932643162633762 64353335393764653031633033323862643732326434613564363935336166386239613932653765 32323335306634326133613334386262316464613166373031376362653266653937303131653165 376436643431366561323866383231343362 # Usage notes: # - Treat this file as the single source of truth for IPs and VLANs. # - Ansible playbooks should read `networks` and `lab_hosts` to render configs, # update `inventory/hosts.ini`, and generate DHCP reservation templates. # # Discussion queue (2026-03-13): # - Decide NAS + Ansible + Watchtower reporting model (agentless scrape, exporter sidecar, or API/blackbox only). # - Decide Omada onboarding scope and what should be automated via Ansible versus documented/manual operations.