PowerEdge R740XD (primary PVE workhorse, 768GB) · 2× T440 (secondary PVE + game/dev) · R540 (Unraid NAS).
pve1 · pve2 · pve3 — quorum, live migration, HA-capable.
Unraid array (R540) for bulk media + backup target. ZFS vmfast pool on pve2 (Micron 9300 NVMe) for hot VM disks.
Control node iaccn01, KVv2 secrets, bpg/proxmox provider, role-based playbooks. VMs provisioned in ~60s.
Self-hosted git + CI. Replaced Gitea this sprint. Aros across 4 repos, stacks deployed via webhook from repo to Portainer.
Traefik on mvm + Cloudflare DDNS + wildcard TLS via cloudflare resolver.
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.
The moments that changed the architecture
- ~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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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. - 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.
What's actually running
pve1, pve2, pve3
Sprint 1.5a estate + hub-1
iaccn01, mvm, forgejo-vm, infraservices01, ARR stack, Plex, Home Assistant, Nextcloud, Immich, …
media + backup target
ryanit.com, ryantaylor.uk
Proxmox, Cloudflare, AppRole, per-service
daily snapshots + rsync to NAS