# ADR-001 — Architecture overview ## Status Accepted (2026-05-30) ## Context This document describes the overall architecture of the homelab infrastructure and the boundaries of what this Ansible monorepo manages. ## Infrastructure - **Hypervisor**: Proxmox cluster (2+ nodes) - **Guest OS**: Debian 13 (all managed hosts) - **Scale**: 2–5 VMs, small fleet — treated as individuals, not cattle - **Control node**: `ubongo` — a dedicated always-on **physical** x86-64 machine **outside** the cluster. Ansible runs from here. It cannot be created by the Terraform it hosts, so it is provisioned manually (see ADR-015 and `docs/runbooks/new-host.md`). ## What this repo manages | Layer | Managed by | Notes | |--------------------|--------------------|--------------------------------------------| | VM existence | Terraform (`terraform/`) | Clones the cloud-init template; `ubongo` (control node) is a physical box outside the cluster, the one manual exception (see ADR-009/ADR-015) | | Internal DNS records | Ansible `dns` role | Internal zone rendered from inventory (see ADR-007/009) | | OS baseline | Ansible `base` role | Users, SSH, firewall, updates, audit | | Docker runtime | Ansible `docker_host` role | Engine, daemon config, log driver | | Service deployment | Ansible per-service roles | Compose rendered from templates | | Secrets | Ansible Vault | Encrypted `vault.yml` files in repo | The Terraform↔Ansible boundary and handoff are defined in ADR-009. This table describes the *intended* design — see STATUS.md for what is actually built. ## Host groups ``` all ├── control # ubongo — physical control node outside the cluster; baseline config only, runs no services ├── docker_hosts # VMs running Docker services (most hosts) ├── proxmox_hosts # Proxmox nodes themselves (limited management scope) └── offsite_hosts # askari (off-site Hetzner) — NetBird coordinator + external watchdog ``` The `control` group holds the single manually-provisioned control node; it is managed for baseline config (SSH, firewall, updates) but never runs the `docker_host` role. The `offsite_hosts` group holds `askari`, the off-site Hetzner host — also manually provisioned (ADR-016), managed for baseline config plus the `netbird_coordinator` service role. Proxmox nodes are managed only for basic baseline tasks (SSH). Proxmox configuration itself (storage, clustering, networking) is out of scope. ## Service interaction model Services run as Docker containers on one or more `docker_hosts`. Where services need to interact, they do so via: - Docker networks (same host) - Internal DNS / hostname resolution (cross-host) - Explicitly defined published ports (external access) All Compose files are rendered by Ansible from Jinja2 templates. No hand-edited Compose files exist on hosts — they are always regenerated on deploy. ## Decision This architecture prioritises: - **Simplicity**: few moving parts, no orchestration layer (no Kubernetes, no Swarm) - **Reproducibility**: any host can be rebuilt from scratch via Ansible - **Legibility**: a human reading the repo can understand what runs where ## Consequences Drawn from the boundaries this ADR already states: - The small fleet (2–5 VMs) is treated as individuals, not cattle (per Infrastructure), and forgoing an orchestration layer is the cost of the simplicity priority (per Decision). - The control node `ubongo` cannot be created by the Terraform it hosts, so it is provisioned manually — the one documented exception to Terraform-owned VM existence (per Infrastructure / Host groups; ADR-009, ADR-015). - Management scope is deliberately bounded: Proxmox configuration itself (storage, clustering, networking) is out of scope, and the `control` group never runs the `docker_host` role (per Host groups). - Compose files are always regenerated by Ansible on deploy; no hand-edited Compose files exist on hosts (per Service interaction model). - The "What this repo manages" table describes the *intended* design — STATUS.md records what is actually built (per that section).