Projects / recap
Projects / Homelab
active
1 session logged

Homelab

From two T440s and a manual VM workflow to a 3-node IaC platform with a self-service portal.

Production. Hosts the IaC control plane, secrets store, source-of-truth git server, CI/CD pipelines, and the Aros application estate — 13 VMs across three environments with HA Postgres.

Fleet
4 servers · 120 cores · ~1TB RAM

PowerEdge R740XD (primary PVE workhorse, 768GB) · 2× T440 (secondary PVE + game/dev) · R540 (Unraid NAS).

Cluster
3-node PVE

pve1 · pve2 · pve3 — quorum, live migration, HA-capable.

Storage
~80TB usable

Unraid array (R540) for bulk media + backup target. ZFS vmfast pool on pve2 (Micron 9300 NVMe) for hot VM disks.

IaC
Terraform + Ansible + Vault

Control node iaccn01, KVv2 secrets, bpg/proxmox provider, role-based playbooks. VMs provisioned in ~60s.

Source
Forgejo + Woodpecker

Self-hosted git + CI. Replaced Gitea this sprint. Aros across 4 repos, stacks deployed via webhook from repo to Portainer.

Edge
*.ryanit.com

Traefik on mvm + Cloudflare DDNS + wildcard TLS via cloudflare resolver.

The arc

How the homelab became a platform

In rough chronology, the inflection points that took this from 'a few VMs' to something that genuinely behaves like a small enterprise estate.

The headline version: I started with the same pattern as everyone else — manual VMs, manual installs, manual fixes, manual everything. Every new service was a fresh chunk of work. The way out wasn’t a single decision; it was a sequence of “this is getting ridiculous” moments that accumulated into an actual architecture.

The catalyst was the moment I realised I was about to spin up the same Ubuntu + Docker + Portainer pattern for the n-th time. That’s when the Proxmox templates went in. Templates led to Terraform. Terraform led to Ansible. Ansible led to Vault — because once you’re provisioning declaratively you can’t hardcode credentials anywhere visible. Vault led to AppRole. AppRole led to a control node that does nothing but orchestrate the others.

Then came the deployment freeze. The Spacebar evaluation made me look at my own flat network — iDRAC, Proxmox web UIs, Vaultwarden, Gitea, internet-facing containers, all on the same broadcast domain — and I called a stop on new exposures until VLANs were properly segmented. That freeze is still partially in force; the IaC work has continued behind it but the network audit is tracked debt.

Storage went through a similar evolution. ARR stack started on Proxmox, with media on a Samba LXC, until the lack of hardlinks between filesystems made every Radarr import double-disk. The fix was the cleanest architecture I’ve built so far: Unraid as dumb storage, ARR stack on a dedicated Proxmox VM, NFS mounts so everything shares one filesystem and hardlinks work cleanly. Plex initially ran on Unraid until its Docker container OOM’d silently and went 15 days without indexing new files — that became the forcing function for moving Plex itself to a dedicated PVE VM with NFS back to Unraid.

The most recent pivot was source control. Gitea had served well but the migration to Forgejo + Woodpecker gives me a proper Actions-style pipeline with self-hosted runners and the OCI registry built in. That’s what Aros now runs on, and what this very site now runs on.

Timeline

The moments that changed the architecture

  1. ~Sept 2025

    Inherited cluster: 2× T440 + R740XD (asleep, too loud)

    Flat network, manual VM provisioning, Portainer-per-VM pattern for separation of concerns. Worked, but every new service was a fresh chunk of toil.
  2. Late 2025

    R540 lands as the dedicated NAS

    Bought broken for £850, recovered ~768GB RAM + 4× 1.6TB SAS SSDs + X710 + H740P. R540 takes over storage; R740XD slated as the primary PVE workhorse once noise is sorted with IPMI fan control.
  3. Early 2026

    Multi-user Nextcloud + Immich on bare Docker

    First "platform-ish" deployment. Per-user stacks, Traefik routing, Cloudflare DDNS, Vaultwarden for secrets. All stacks committed to a Gitea repo — first time the homelab had source control over its own config.
  4. March 2026

    Spacebar evaluation triggers a deployment freeze

    Looking at the network during the Spacebar review revealed iDRAC, Proxmox, Vaultwarden, Gitea, and internet-facing containers all on the same flat network. Called a halt on new exposures pending VLAN segmentation.
  5. April 2026

    ARR stack + Unraid pivot

    Lifted the ARR stack off the legacy host and onto a dedicated Proxmox VM with NFS mounts to Unraid. Hardlinks now work cleanly between /data/torrents/complete and /data/media. Recyclarr TRaSH profiles, qBittorrent categories, Sonarr + Radarr + Prowlarr + Bazarr + Overseerr + Tautulli.
  6. April 2026

    The IaC awakening

    iaccn01 stood up as the control node. Terraform v1.14.8 + Ansible 2.20.4 + Vault v1.21.4. First IaC-provisioned VM (home-services, VM 104) deployed in 32s. Bin collection automation built as the first real workload running on the IaC pipeline.
  7. April 2026

    iac.ryanit.com — the self-service portal

    React frontend that drives Terraform + Ansible behind the scenes. Provisioned infraservices01 (VM 111) and a multi-service stack pattern with one click. First service deployed through it: Domain Tracker, monitoring RDAP expiry for ryanit.com and ryantaylor.uk plus Cloudflare DNS drift.
  8. April 2026

    Plex pipeline rescue

    Plex on Unraid had silently been failing to index new content for ~15 days — Docker container OOM on the image file. Migrated Plex to a dedicated Proxmox VM with NFS back to Unraid storage, then disabled Docker on Unraid entirely. Library health restored.
  9. May 2026

    Forgejo + Woodpecker migration

    Moved off Gitea to Forgejo 9 + Woodpecker v3 with the built-in OCI registry. Aros and projects-recap (this site) both build through Woodpecker now. Gitea remains for legacy stacks.
  10. May 2026

    Aros Sprint 0

    13 VMs provisioned, full dev → test → UAT pipeline validated, 3-node HA PostgreSQL cluster stood up. The homelab pattern matured enough to support a real first-party application.
  11. May 2026

    Aros Sprint 1 — first real application code

    FastAPI modular monolith (aros-backend) deployed across dev/test/UAT. HA Postgres proven at the application layer — zero dropped requests during a deliberate Patroni leader kill. Pipeline validated for a second service.
  12. May 2026

    Aros Sprint 1.5a — pipeline maturity

    Backend hardening, Vault secret layout established (per-service per-env), env-file permissions model fixed, hello-world decommissioned. Fourth repo (aros-backend) operational.
  13. Next

    VLAN segmentation lift

    The freeze is still in force. Management / Trusted / Services / IoT trust zones designed, blocked on a methodical network audit + cutover. Once lifted, oauth2-proxy goes in front of every internal service.
Inventory

What's actually running

PVE nodes
3

pve1, pve2, pve3

Aros VMs
13

Sprint 1.5a estate + hub-1

Long-running VMs
15+

iaccn01, mvm, forgejo-vm, infraservices01, ARR stack, Plex, Home Assistant, Nextcloud, Immich, …

Unraid array
~80TB

media + backup target

Domains
2

ryanit.com, ryantaylor.uk

Active monitors (Uptime Kuma)
20+
Secrets in Vault
10+

Proxmox, Cloudflare, AppRole, per-service

Backups
PBS + Unraid

daily snapshots + rsync to NAS

Stack

Everything in production today

Hypervisor
Proxmox VE 8Proxmox Backup Server
Storage
UnraidZFSNFS
IaC
TerraformAnsiblebpg/proxmox
Secrets
HashiCorp VaultVaultwarden
Source + CI
ForgejoWoodpeckerGitea
Orchestration
PortainerDocker 29
Edge
Traefik v3Cloudflare DDNSoauth2-proxy
Data
PostgreSQLMariaDBRedisSQLiteMosquitto MQTT
Apps
Home AssistantNextcloudImmichPlexSonarr / Radarr / Prowlarr / BazarrOverseerr / TautulliqBittorrent / RecyclarrDomain Tracker
Observability
Uptime KumantfyNode Exporter
Network
Unifi (network)UniFi Protect