Built to be rebuilt.

Three Proxmox hosts, one Kubernetes cluster, and nothing that answers the internet. All of it comes back from a git repository and two secrets in about thirty-five minutes — which is the only reason it is safe to keep changing.

The office corner: a standing desk running monitoring dashboards, and the open rack beside it — R730xd, mini PCs, NAS and UPS
hosts
3 × Proxmox standalone, joined by PDM
kubernetes
Talos + Flux 3 nodes, 3 machines
pools
~6 TB ZFS — NVMe, SSD, SAS
draw
~165 W three hosts, one UPS
inbound ports
0 tunnel + tailnet only
backup copies
3 one off-site, one offline

Network

No way in.

Not one port is forwarded. Nothing in the rack answers the internet directly, and the two paths that do exist both dial outward: a mesh VPN, and a tunnel that opens itself.

pfSense
Firewall and gateway on an XCY X44. Physical segmentation — one NIC per segment, not VLAN tags.
Tailscale
A subnet router puts the LAN on the tailnet, so the rack and the cloud VPS share one flat mesh. This is the way in.
Cloudflare Tunnel
Outbound-only, three routes. A router that does not exist on a port cannot be reached through it.
Unbound
Recursive resolution, no upstream forwarder, with overrides for the internal names.

Compute

Three machines.
Three jobs.

Standalone hosts, joined through Proxmox Datacenter Manager and never corosync. One Kubernetes node each — so the three etcd votes sit in three different chassis, and losing a machine costs a workload rather than the cluster.

The GPU

Beelink GTi13 Ultra

pve-1

CPU
Core i9-13900HK · 20 threads
Memory
64 GB DDR5
Node
kubernetes-1 · 14 cores / 32 GB
Why
Iris Xe passed through — every transcode happens here

The disks

Dell PowerEdge R730xd

pve-2

CPU
Xeon E5-2630 v4 · 10C/20T
Memory
251 GB DDR4 ECC
Node
kubernetes-2 · NFS, Garage S3, Nextcloud
Why
Twelve SAS bays, and the backup hub everything drains into

The vote

Dell OptiPlex 3050

pve-3

CPU
Core i5-6500T · 4 cores
Memory
32 GB DDR4
Node
kubernetes-3 · one Longhorn replica
Why
960 GB D3-S4510 — power-loss protection, the best etcd disk here

Storage

Six terabytes,
mostly asleep.

Twelve SAS disks hold the library. They park whenever nothing is reading them — forty to fifty watts that would otherwise be spent spinning platters for nobody, in a room somebody sleeps in.

media

pve-2

12 × SAS in two raidz2 vdevs

1.31 TB of ~4.2 TB

rpool

pve-2

SSD mirror — everything touched daily

159 GB of 861 GB

cluster-storage

pve-1

Single NVMe, one Longhorn replica

105 GB of 899 GB

Waking them used to stall etcd's write-ahead log on the shared HBA. The fix was not more hardware — it was raising the election timeout from one second to five, and parking the pool outside the backup window.

Kubernetes

git push is the deploy.

Talos has no shell and no package manager — it is configured by API and by nothing else. Flux watches the repository and makes the cluster match it. There is no third way to change anything, which is what makes the rebuild time believable.

kubernetes/apps/
├── default/         jellyfin, immich, the *arr stack, n8n
├── kube-system/     Cilium, CoreDNS, NFS CSI, Intel GPU plugin
├── network/         gateway, Cloudflare tunnel, netboot.xyz
├── observability/   Prometheus, Grafana, Loki, Alloy
└── storage/         Longhorn

Services

What it actually runs.

Disk-bound things in the cluster, identity and remote access on the VPS. Everything with data behind it is declared in the repo.

Media

  • Jellyfin 4K, hardware transcoded
  • Jellyseerr requests — the one route that faces out
  • Radarr · Sonarr film and TV automation
  • Prowlarr · qBittorrent indexing and download, VPN-routed

Files & photos

  • Immich photo library, own Postgres
  • Nextcloud files and office, AIO on its own VM
  • Collabora documents edited in the browser
  • Joplin notes sync

Platform

  • Talos + Flux the cluster and what reconciles it
  • Longhorn block storage, three replicas
  • Cilium eBPF CNI, Gateway API, L2
  • cert-manager certificates, renewed without asking

Edge & identity

  • Traefik reverse proxy on the VPS
  • Authentik single sign-on
  • Guacamole remote desktop in a tab
  • Pi-hole + Unbound filtering DNS, own resolver

Watching it

  • Prometheus metrics, hosts and cluster
  • Grafana dashboards
  • Loki + Alloy logs
  • Alertmanager straight to Telegram

Odds

  • n8n one workflow — the daily news digest
  • Headlamp Kubernetes, when a UI is faster
  • Portainer the VPS containers
  • netboot.xyz installers over the network

Alerting

Silence is the alarm.

Alertmanager, Flux and Proxmox go straight to Telegram — critical and warning only, tagged by layer. Every scheduled job also pings healthchecks.io on its way out, so a job that stops running raises its own hand. Nobody has to notice.

Backup

Three copies.
It can delete none of them.

The machine holding the data can add to both off-site targets and remove from neither. Oracle runs an append-only restic server. The NAS pulls with a read-only key and leaves no credential behind it. Retention runs on each target, not on the source.

Longhorn (cluster)  ──nightly──▶  Garage S3 on the R730xd
Oracle VPS          ──nightly──▶  R730xd
pfSense config      ──────────▶  R730xd
                                     │
                     ┌───────────────┴───────────────┐
                     │ weekly, pulled                │ nightly, append-only
                     ▼                               ▼
               Synology                        Oracle Cloud
               asleep between                  cannot be pruned
               wake windows                    from home

Losing any single copy is a non-event. That is the whole point of the arrangement, and it is checked by restoring a five-percent sample every week rather than by trusting it.

Recovery

71min

Cluster restored onto different hardware. Drilled on 29 August, then done for real on 1 September — from git and object storage, in seventy-one minutes. The VPS rebuilds from nothing in about fifteen.

The drill is the point. It found four tooling bugs and one library that had been quietly excluded from every restore for months — none of which a backup report would have shown.

Tech specs

All of it, in order.

Hypervisor
Proxmox VE on three standalone hosts, joined by Proxmox Datacenter Manager. No corosync, no HA — deliberate.
Kubernetes
Talos Linux, three control planes, one per physical machine. Flux reconciles from GitHub.
Networking
pfSense on an XCY X44. Physical segmentation, one NIC per segment. Cilium for CNI, Gateway API and L2 announcement.
Storage
ZFS. media = 12 × SAS in 2 × raidz2, parked when idle. rpool = SSD mirror. cluster-storage = NVMe. NFS to the cluster nodes only, ACL per client IP.
Off-site
Oracle Cloud Free Tier, Ampere A1 — 4 vCPU ARM, 24 GB, 200 GB. Traefik, Authentik, Guacamole, Joplin, Pi-hole, the public dashboard and an append-only restic server.
Cold copy
Synology DS223+. Pulls weekly over a read-only rrsync key, asleep the rest of the week.
Power
CyberPower VP700ELCD, monitored by NUT. ~165 W total, ~110 W of it the R730xd. Roughly 119 kWh a month.
Noise
Fans off iDRAC — a daemon holds a 1680 RPM floor, about 18 dB under stock.
Secrets
SOPS/age for Kubernetes, Ansible Vault for the VPS. Three keys cannot be lost; nothing else is irreplaceable.