Notes on the box in the closet, as of August 2026.

Why bother

At work, mistakes get caught by change control, peer review and someone else’s on-call rotation. The homelab has none of that. If I misconfigure DNS, the household loses the internet and I hear about it immediately.

That is the value of the thing. It is where I make architecture decisions and then live with them, which teaches differently than documentation does. The server is called Pelican and it runs Unraid.

The hardware

Pelican is a used HP EliteDesk 800 G5 SFF: an Intel Core i5-9500, six cores at 3.0 GHz, in a small-form-factor case that started life under somebody’s desk. Bought secondhand. No custom build, no rack.

That is deliberate. The whole machine idles around 50 watts and runs everything below with CPU load near zero most of the day. A four-figure homelab teaches the same lessons as a three-figure one, and working with what is in front of you is closer to the actual job than speccing an ideal build.

Where I did spend, I spent on the parts that fail or that hurt when they are slow:

  • 32 GB of DDR4, half of it free. ZFS wants memory, containers want memory, and running near the ceiling is how you find out what the OOM killer does to a database at 2am.
  • Storage, covered below. That is where the real money and the real thinking went.
  • An NVIDIA Quadro P1000 for hardware transcoding, the low-profile version because an SFF chassis takes nothing taller. Media transcoding on CPU is the one workload that would tax this box; on the GPU it is free. A cheap used workstation card is the best value-per-dollar part in the machine.
  • A UPS, the least exciting purchase and the first thing I would replace if it died. It carries about 220 watts of load at 55% capacity, which buys roughly 11 minutes. That is not for working through an outage; it is for a clean, ordered shutdown. An array that loses power mid-write during a parity operation is a bad afternoon.
  • 2.5 Gb networking, the two NICs bonded in active-backup. Overkill for a single-homed box on one switch, but it cost nothing to configure and it is the pattern I would use somewhere it mattered.

The constraints of an ex-office desktop shaped the build more than any preference did: one low-profile PCIe slot, four DIMM slots capped at 64 GB, and a power supply sized for an office PC instead of a drive array.

Storage: three tiers, on purpose

The array is two 10 TB drives, one data and one parity. Around 6 TB in use.

Parity is not a backup. It is the most common thing people get wrong about this kind of build. Parity survives a disk failure. It does not survive me deleting the wrong directory, or ransomware, or the house burning down. It buys uptime, not recovery.

In front of the array sits a ZFS mirror of two 1 TB SSDs as the write cache. New writes land on SSD and move to the array on a schedule, so the spinning disks are not in the path of every small write.

The decision I think is the interesting one: Docker’s application data lives on a separate 1 TB NVMe drive, mounted outside both the array and the cache pool.

That is not the default, and it took a rebuild to get there. Container appdata is a constant stream of small reads and writes: databases checkpointing, logs rotating, services polling. On the array, the disks never spin down and every container competes with actual file transfers. On the cache pool, container I/O fights the write cache for the same devices.

A dedicated NVMe means bulk storage, write caching and application I/O do not contend with each other at all. Storage tiering is not an enterprise-only idea; it usually just gets decided for you by whoever bought the SAN.

DNS: the thing that fails hardest

I run network-wide DNS filtering, and I run two of them, AdGuard Home and Pi-hole, each with its own IP address on the LAN.

DNS is the highest-blast-radius service on a home network. When it goes down, nothing works, and every device fails at once in a way that looks like the internet is broken. It is also the service most likely to go down during maintenance, because you are updating it or the host is rebooting.

Two independent resolvers on two addresses means I can take one down, update it, break it, rebuild it, and nothing in the house notices.

Both run on macvlan networking instead of bridge with port mapping, so each gets a real address on the LAN and behaves like a network appliance. Clients point at an IP, not at a host-and-port on the server. DNS keeps working the way clients expect even if I move the container, and DHCP can hand out those addresses directly.

What runs

  • Home Assistant, on host networking so it can do local device discovery
  • Immich with its own PostgreSQL instance, a self-hosted photo library and my exit from Google Photos
  • Duplicacy for backups, pointed at the storage share
  • Uptime Kuma, monitoring for everything above and the longest-running service on the box
  • DuckDNS, so a residential IP that changes does not break remote access
  • MeTube for local media archiving

The box also serves Time Machine targets for the Macs in the house, holds NVR camera recordings, and terminates a WireGuard tunnel for remote access.

That last one is the security decision I would defend hardest. None of the services above is reachable from the internet. No reverse proxy, no published web UIs, no forwarded ports for individual apps. The only thing listening from outside is WireGuard’s single UDP port, and getting past it means holding a key.

Publishing services behind a reverse proxy would make every one of them an internet-facing attack surface with its own auth, its own CVEs and its own update cadence. One tunnel makes remote access an authentication problem I solve once instead of a hardening problem I solve per service, forever.

No virtual machines. Everything is containerized, because for this workload VMs would be overhead with no benefit.

Backups, monitoring, integrity

Three habits carried over from work:

Backups are separate from redundancy. Duplicacy backs up the storage share independently of parity. Parity is for disk failure; backups are for everything else.

Monitoring is not optional because it is a home network. Uptime Kuma has run for ten months and is the reason I find out a container died from a dashboard instead of from someone in the house.

Integrity gets verified on a schedule, not assumed. Parity checks run on a schedule. The last full check took 15 hours and 40 minutes at an average of 177 MB/s and returned zero errors. A backup or a parity array you have never verified is a belief, not a control.

What it taught me

The lessons that transferred to the day job were not about Unraid.

Separation of concerns is a performance decision as much as a tidiness one. I understood tiered storage in the abstract. I understood it properly after watching container I/O keep an array awake all night.

Redundancy belongs on the thing with the widest blast radius, not on everything equally. One data disk with parity is fine. One DNS server is not. That asymmetry is a judgment about consequences, not a rule you can look up.

Monitoring you built yourself is monitoring you read. Every alert on that dashboard is one I decided mattered, which is why it still gets looked at ten months later.

And running the whole stack alone means there is nobody to escalate to. Every outage is mine to diagnose from symptom to root cause with no runbook. That is a different skill from following an established process, and it is the one I lean on most when something at work breaks in a way nobody has documented.