# My Homelab: One Proxmox Box, ZFS Pools, and a Properly Segmented Network

> The lab started as services sprawled across whatever machine was awake — until one bad upgrade took them all down at once. This is what that failure taught me: one Proxmox host, ZFS snapshots as the undo button, and a network segmented by trust.

URL: https://harshith.in/blog/homelab-proxmox-zfs-lxc/
Author: Harshith Sunku (Staff Engineer) — https://harshith.in/about/
Tags: linux, systems, containers
Series: [The Homelab](https://harshith.in/series/homelab/) (part 1 of 5)

The lab didn't start as a lab. It started as services accreting onto
whatever machine happened to be on — a media server here, a script there,
everything sharing one filesystem and one fate. The failure that changed
it was boring and total: one bad upgrade, and everything went down
together, with no isolation between the thing I broke and the things I
cared about, and no undo. I rebuilt from scratch, twice.

That's the problem this lab solves. Not "host services" — *break things
without breaking everything, and always have a way back*. The solution
became **one Proxmox VE host carrying about twenty LXC containers and a
handful of VMs**, behind a UniFi network that does the segmentation work.
Every post on this blog is now written, built, and pushed from a container
in that lab. No seven-node cluster blinking in a rack — the interesting
lessons are in how much one box can carry, and where the isolation
boundaries actually earn their cost.

*[Diagram: One Proxmox host, tiered ZFS pools, and a VLAN-segmented network]*

## The host, and the undo button

The box is built around an Intel Core Ultra 9 285K — 24 threads on one
socket — with 96 GB of RAM, running Proxmox VE 9.2 on kernel
`7.0.14-4-pve`. Proxmox gives me three primitives behind one API: full
VMs (QEMU/KVM), system containers (LXC), and ZFS storage. At a typical
moment the host sits under 1% CPU with ~18% of memory committed — the
first surprise the lab taught me is that the constraint on a homelab
server is almost never compute. It's RAM and storage layout.

Storage is where the two rebuilds paid off. Everything is ZFS, split by
what the disks are good at: a fast NVMe pool for container roots and VM
disks, a big spinning pool (~12 TiB raw between them) for bulk data, and
a separate pool that exists only to receive backups. Each container's
root is its own dataset (the blog's workspace lives on a `subvol` under
the fast pool), which buys the three things I actually use daily:

- **Snapshots before anything risky.** Instantaneous, and rollback is
  one command. The "no undo" failure that started this lab can't happen
  anymore — and it has saved me twice since.
- **Per-dataset quotas** instead of pre-carved disk images — the pool is
  shared and containers take what they're allotted.
- **Checksums on every block.** ZFS detects the bit-rot that a plain
  RAID mirror silently propagates — the distinction I unpacked in
  [RAID: what it does and doesn't](https://harshith.in/blog/raid-what-it-does-and-doesnt/).

## The VM/container line is a sizing lesson

I wrote about [what a container really is](https://harshith.in/blog/what-is-a-container/) —
namespaces plus cgroups, no guest kernel. What the lab taught me is that
this definition becomes a *budget*. My first instinct was VMs for
everything, because VMs feel like "proper" isolation. Then I did the
arithmetic: a VM pins its allocation — give it 8 GB and that memory is
spoken for, plus a whole guest kernel. Twenty services as VMs would have
blown through 96 GB before doing any work. An LXC container shares the
host kernel; its "8 GB" is a cgroup ceiling, and unused memory stays in
the host page cache where it does something useful.

So almost everything is LXC: a reverse proxy, the Cloudflare Tunnel
connector, a Tailscale node, a photo library, a vector database, an LLM
chat UI, a code-server, a Docker host for compose-shaped things, some
monitoring, and the container that runs the coding agent maintaining
this site — 4 vCPUs and 8 GB, strange enough to deserve
[its own post](https://harshith.in/blog/an-ai-coworker-in-an-lxc/).

The VMs are the exceptions that prove the rule, each needing its own
kernel: a Windows 11 guest, an identity/auth appliance I want strongly
isolated, and an emulated ARM machine for cross-architecture testing.
The tell from inside a container: `systemd-detect-virt` says `lxc`, and
`uname -r` shows the *host's* `-pve` kernel. One kernel, many userspaces.

## The network does the isolation

The second lesson came from the network side, and it arrived the way
these lessons do — as a "wait, *that* can see *this*?" moment. A flat
network means the cheapest, least-trustworthy device on it (a smart
plug, a guest's phone) sits one hop from the hypervisor's management
interface. Compute isolation is worthless if the network hands out
adjacency for free.

So the other half of the lab is a UniFi stack — a UCG Ultra gateway, a
PoE switch, and a WiFi 6 access point — with the flat-network problem
solved the way [the homelab networking
post](https://harshith.in/blog/self-hosting-networking-stack/) prescribes: **segmentation
by trust**. The management network hangs off `192.168.0.0/24`, and
everything else lives in its own VLAN and `10.10.x.0/24` — IoT gadgets,
guest WiFi, office gear, public-facing servers, NAS traffic, VPN
clients, an experiment sandbox for things I don't trust yet, and an
identity/auth segment. A dozen VLANs sounds like enterprise cosplay
until the first compromised smart plug can't see your hypervisor. Then
it sounds like the minimum.

SSIDs map onto the same segments — the IoT network is 2.4 GHz-only,
which is where small embedded things like
[my wake-on-LAN gateway](https://harshith.in/blog/waking-the-lab-esp32-mqtt-wol/) live.

Nothing is port-forwarded. Remote access is Tailscale (mesh VPN in,
zero inbound holes), and the few deliberately public services go out
through a Cloudflare Tunnel — an outbound connection from a container,
so the LAN never accepts unsolicited traffic. The blog itself doesn't
touch any of this: it's [static files on Cloudflare's
edge](https://harshith.in/blog/how-this-blog-runs-for-0/), because a residential connection
is exactly where a public website should *not* live.

There's also one machine outside the house: an Oracle Cloud free-tier
VM running an LLM API proxy — keys stay off the LAN, and it survives
local power cuts.

## Takeaways

- The lab exists to learn on. Its real product isn't uptime — it's the
  lessons: isolation boundaries, sizing budgets, and recovery paths you
  only understand after breaking them.
- One well-specced Proxmox box covers a remarkable amount of homelab;
  RAM and storage tiering are the real constraints, not CPU.
- Prefer LXC over VMs for trusted Linux services — shared-kernel density
  is the whole game on a single host. Reserve VMs for foreign kernels
  and hard isolation boundaries.
- ZFS snapshots before every risky change; quota datasets instead of
  fixed images; a dedicated backup pool that does nothing else.
- Segment by trust: VLANs per role, IoT quarantined, management network
  unreachable from anything untrusted. VPN in, tunnel out, forward
  nothing.
- Next in this series: [the fleet of bare-metal machines around this
  box — and what a hypervisor can't
  do](https://harshith.in/blog/the-fleet-why-bare-metal-earns-its-keep/).
