NELSON HOME OPS CONSOLE
GitHub ↗

About Nelson Home

Nelson Home

Infrastructure as Code for a self-hosted homelab. This repo is the single source of truth for provisioning, configuring, and deploying all services across a Proxmox VE host (with two LXC nodes: nelson-manager and nelson-edge), and a legacy Ubuntu monolith being gradually decommissioned.


Current Architecture

┌──────────────────────────────────────────────────────────────────┐
│  Proxmox VE — Beelink S13 Pro (N100, 16 GB RAM, 4-core)          │
│  nelson-pve · 192.168.1.10                                       │
│                                                                  │
│  ┌──────────────────────────────┐  ┌──────────────────────────┐  │
│  │ nelson-manager               │  │ nelson-edge              │  │
│  │ LXC 300 · 192.168.1.30       │  │ LXC 301 · 192.168.1.2    │  │
│  │                              │  │                          │  │
│  │ · Semaphore + Postgres       │  │ · AdGuard Home (DNS)     │  │
│  │ · Vaultwarden                │  │ · Tailscale (mesh VPN)   │  │
│  │ · Homepage dashboard         │  │ · NPM (→ Caddy WIP)      │  │
│  │ · Uptime Kuma                │  │ · DuckDNS                │  │
│  │ · UniFi Network App          │  └──────────────────────────┘  │
│  │ · Grafana + Prometheus       │                                │
│  │ · Pulse, Glances             │                                │
│  └──────────────────────────────┘                                │
│                                                                  │
│  ┌──────────────────────────────┐  ┌──────────────────────────┐  │
│  │ ubuntu-server                │  │ nelson-apps              │  │
│  │ VM 100 · 192.168.1.11        │  │ VM (planned) · .40       │  │
│  │ (legacy monolith, draining)  │  │                          │  │
│  │                              │  │ · Nextcloud              │  │
│  │ · Home Assistant             │  │ · Plex / Jellyfin        │  │
│  │ · Plex, Nextcloud, n8n       │  │ · Immich, Paperless      │  │
│  │ · Pulse, Glances             │  │ · n8n                    │  │
│  └──────────────────────────────┘  └──────────────────────────┘  │
│                                                                  │
│  ┌──────────────────────────────┐                                │
│  │ bolt-claw                    │                                │
│  │ VM 105 · role TBD            │                                │
│  │ (audit pending)              │                                │
│  └──────────────────────────────┘                                │
│                                                                  │
│  5TB WD Elements · /mnt/nelson-backups                           │
└──────────────────────────────────────────────────────────────────┘

┌────────────────────────────────┐
│ ender3 · 192.168.1.123         │
│ Raspberry Pi (physically       │
│ remote from homelab)           │
│ · 3D printer controller        │
└────────────────────────────────┘

Node Roles & Design Rationale

nelson-manager (LXC · 192.168.1.30)

Role: IaC control plane + observability

All infrastructure orchestration lives here. Semaphore is the execution engine for every Ansible playbook — it can never be on the machine it deploys to (self-termination risk), so it needs its own stable node. Vaultwarden sits alongside it so secrets are always available to the control plane. Monitoring (Grafana/Prometheus, Uptime Kuma, Pulse, Glances) lives here to observe all other nodes from a neutral vantage point. UniFi Network Application is here because it needs to be reachable by all LAN devices, independent of the monolith.

nelson-edge (LXC 301 · 192.168.1.2)

Role: Network edge — DNS, VPN, reverse proxy, and SSL

nelson-edge is a Proxmox LXC on nelson-pve. It owns everything at the network boundary before traffic reaches an application node. AdGuard handles all DNS queries for *.nelson.home. Tailscale provides the mesh VPN fabric. NPM handles reverse proxying now, and will be replaced by Caddy for automatic HTTPS via Let's Encrypt (DNS-01 challenge through DuckDNS) — both staying on nelson-edge since SSL termination is inherently a network-edge concern. Concentrating all network services here means this layer survives independently of application nodes and reboots cleanly.

nelson-apps (VM · planned)

Role: Heavy compute workloads

Nextcloud, Plex/Jellyfin, Immich, and Paperless-ngx are compute and memory-intensive. Isolating them into a VM with 10–12 GB RAM keeps them from competing with control-plane and network services. Direct access to the 5TB backup drive via Samba (NAS) targets this node.

bolt-claw (VM 105 · on nelson-pve)

Role: Unknown — audit pending

Existing VM consuming ~7.8 GB RAM with no documented purpose. Scheduled for audit to determine whether to keep or decommission.

ender3 (Raspberry Pi · 192.168.1.123)

Role: 3D printer controller

Physically remote from the homelab. Runs the 3D printer. Included in the network map for inventory completeness; not managed by Ansible.

ubuntu-server (VM 100 · 192.168.1.11)

Role: Legacy monolith (draining)

A Proxmox VM hosting all pre-migration services. All services here are scheduled for migration to other Proxmox nodes. It stays online until every service has a tested replacement. Nothing new is deployed here.


Service Map

Service Current Host Target Host Domain
Semaphore (IaC) nelson-manager nelson-manager semaphore.nelson.home
Vaultwarden nelson-manager nelson-manager vault.nelson.home
Homepage ubuntu-server nelson-manager homepage.nelson.home
Uptime Kuma nelson-manager uptime.nelson.home
UniFi Network App nelson-manager nelson-manager unifi.nelson.home
Grafana + Prometheus nelson-manager grafana.nelson.home
Pulse ubuntu-server nelson-manager pulse.nelson.home
Glances ubuntu-server nelson-manager glances.nelson.home
AdGuard Home (DNS) nelson-edge nelson-edge adguard.nelson.home
Tailscale nelson-edge nelson-edge
DuckDNS ubuntu-server nelson-edge
Nginx Proxy Manager nelson-edge → replaced by Caddy npm.nelson.home
Caddy (SSL) nelson-edge
Home Assistant ubuntu-server ubuntu-server ha.nelson.home
Plex ubuntu-server nelson-apps plex.nelson.home
Proxmox (Public) nelson-pve nelson-pve proxmox.tudhopenelson.duckdns.org
Jellyfin ubuntu-server nelson-apps jellyfin.nelson.home
Nextcloud ubuntu-server nelson-apps nextcloud.nelson.home
n8n ubuntu-server nelson-apps n8n.nelson.home

Networking

All internal DNS is managed by AdGuard Home on nelson-edge. Every *.nelson.home domain resolves to the node running the reverse proxy (nelson-edge), which routes by hostname to the correct upstream service.

Client → AdGuard DNS → *.nelson.home resolves to reverse proxy host
       → NPM (now) / Caddy (future) → upstream service:port

Future state: Caddy on nelson-edge replaces NPM, adding automatic HTTPS via Let's Encrypt DNS-01 (DuckDNS). DNS rewrites will continue pointing to nelson-edge — no IP change needed during the NPM → Caddy cutover.

Remote access: Tailscale mesh VPN. All nodes and the operator workstation are on the tailnet (tadpole-dory.ts.net). DNS queries on the tailnet route through AdGuard.


Roadmap

Phase 2.3: Manager Node — In Progress

  • [x] Provision nelson-manager LXC (ID 300, 192.168.1.30)
  • [x] Migrate Semaphore + Postgres to nelson-manager
  • [x] Migrate Vaultwarden to nelson-manager
  • [ ] Deploy Uptime Kuma to nelson-manager
  • [ ] Deploy Monitoring (Prometheus/Grafana) to nelson-manager
  • [ ] Migrate Pulse + Glances to nelson-manager

Phase 2.4: Networking (Edge) — Complete

  • [x] Deploy NPM to nelson-edge [Gemini, 2026-02-19]
  • [ ] Migrate DuckDNS to nelson-edge
  • [x] Update internal DNS (AdGuard rewrites for new service locations) [Gemini, 2026-02-19]
  • [ ] Replace NPM with Caddy on nelson-edge (automatic HTTPS via DuckDNS DNS-01)
  • [x] Decommission nelson-identity LXC (.20) [Gemini, 2026-02-19]

Phase 2.1: Pre-Migration Cleanup — Not Started

  • [ ] Audit bolt-claw VM 105 (keep or decommission)
  • [ ] Decommission unused VMs (ubuntu-desktop 101, netbox 104)
  • [ ] Remove 3 dead NPM rules pointing to retired 192.168.1.157
  • [ ] Template plaintext compose credentials into Ansible Vault
  • [ ] Reconcile unmanaged containers into IaC (duckdns, plex, nextcloud, glances, qdirstat, mongo-express)
  • [ ] Audit and store all lab passwords in Vaultwarden

Phase 3: Apps Node (planned)

  • [ ] Provision nelson-apps VM (10–12 GB RAM, 2–3 vCPU)
  • [ ] Migrate Plex, Jellyfin, Nextcloud, n8n
  • [ ] Deploy Immich, Paperless-ngx
  • [ ] Samba NAS from 5TB drive

Phase 4: Hardening (planned)

  • [ ] Compliance audit playbook (OS drift, SMART health, rogue processes)
  • [ ] Lynis security hardening scoring
  • [ ] Syncthing for device sync
  • [ ] Decommission ubuntu-server (final goal)

Resource Budget (Proxmox Host)

Node Type RAM vCPU Role
nelson-manager LXC 4 GB 1 IaC control plane + monitoring
bolt-claw VM ~8 GB ? TBD (audit pending)
nelson-apps VM 10–12 GB 2–3 Nextcloud, Plex, Immich, Paperless
(headroom) ~2 GB Buffer for Proxmox + expansion

Hardware ceiling: 16 GB RAM, 4-core N100 Note: bolt-claw's ~8 GB is the primary constraint on available headroom until it is audited/decommissioned.


Repository Structure

├── ansible/
│   ├── ansible.cfg             # Sets inventory path
│   ├── inventory/hosts.ini     # All hosts — source of truth for IPs
│   ├── group_vars/all/
│   │   ├── common.yml          # Shared variables (IPs, URLs, service map)
│   │   ├── secrets.yml         # Ansible Vault encrypted secrets
│   │   └── containers.yml      # Active Docker service list per node
│   ├── audit_*.yml             # System audit playbooks
│   ├── backup_*.yml            # Backup automation
│   ├── configure_*.yml         # Service config (DNS rewrites, NPM hosts)
│   ├── deploy_*.yml            # Service deployment
│   └── sync_*.yml              # Repo sync and config push
├── docker-compose/             # Compose definitions per service
├── .ops/                       # Project management workspace
│   ├── PROTOCOL.md             # Shared rules for all agents
│   ├── ROADMAP.md              # Phases and milestones
│   ├── SPRINT.md               # Active tasks with next-actions
│   ├── KNOWLEDGE.md            # Operational patterns and API details
│   ├── STANDUP.md              # Rolling 3-day daily report
│   └── archive/                # Completed sprints and older reports
├── CLAUDE.md                   # Claude Code agent instructions
├── GEMINI.md                   # Gemini CLI agent instructions
└── CHANGELOG.md                # Chronological log of all changes

How It Works

Ansible + Semaphore

All infrastructure changes go through Ansible playbooks, executed via Semaphore on nelson-manager. Playbooks are never run directly from the operator workstation against live infrastructure — Semaphore provides the audit trail, vault integration, and scheduling.

Shared variables (IPs, service locations, DNS entries) live in group_vars/all/common.yml. When a service migrates to a new node, update forward_host in common.yml and re-run configure_npm_hosts.yml and configure_adguard_dns.yml — no manual NPM or AdGuard edits needed.

Secrets (API tokens, passwords) live in group_vars/all/secrets.yml, encrypted with Ansible Vault. All Semaphore templates must have vault_key_id set or they fail on decryption.

Backups

Critical data is backed up to the 5TB WD Elements at /mnt/nelson-backups on nelson-pve:

  • backup_vaultwarden.yml — SQLite snapshots with retention
  • backup_unifi.yml — UniFi controller config with retention

Audits

audit_master.yml runs all sub-audits and writes reports to the repo root (docker_audit.md, proxmox_audit.md, etc.) for a point-in-time snapshot of system health.


AI Agent Collaboration

This repo is co-maintained by two AI coding agents alongside the human operator:

  • Claude Code — instructions: CLAUDE.md
  • Gemini CLI — instructions: GEMINI.md

Both agents follow the shared protocol in .ops/PROTOCOL.md. The .ops/ directory is the shared project workspace — agents read it at session start and update it after completing work. Both agents attribute all changes: [Agent, YYYY-MM-DD].

See .ops/STANDUP.md for the latest status, or .ops/SPRINT.md for active tasks.


Operator Workstation

SSH config for the MacBook Pro operator station:

# ~/.ssh/config
Host 192.168.1.* 100.*.*.*
    User btnelson
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes