Home server

One small-form-factor PC that runs my DNS, media, file sync, camera NVR and RHEL practice VMs. Every service has its own address on my LAN and its own HTTPS name, and none of it is exposed to the internet.

Hardware
HP EliteDesk 800 G6, Core i7 vPro, 32 GB RAM
Storage
500 GB internal NVMe, 8 TB Seagate Barracuda in a USB enclosure
OS
Red Hat Enterprise Linux 10
Runtime
Docker CE with Compose, KVM and libvirt, Cockpit
Remote access
Tailscale, no open ports

What it runs

ServiceWhat it does
CaddyReverse proxy. Serves every .lan name over HTTPS with a private CA.
Pi-holeDNS for the whole LAN, ad blocking, local records for .lan names
JellyfinMedia server with Intel Quick Sync transcoding
SeafileFile sync for my laptop and phone
FrigateNVR for the studio camera, see Camera NVR
IronCardWorkout tracker I built with Claude, behind HTTPS and a login, see IronCard
PortainerContainer dashboard
WatchtowerNightly container image updates
CockpitWeb-based host management
KVM and libvirtRHEL VMs for RHCSA practice

How it's built

  • Per-service addresses. A Docker macvlan network gives each service its own LAN address, so ports never collide. A small shim interface on the host lets the server reach its own containers.
  • HTTPS on local names. Pi-hole resolves .lan names, and Caddy issues certificates from its own private CA, so every service gets HTTPS without a public domain or open ports.
  • Storage split. Compose files and app state live on the internal NVMe. Media, file sync data and VM disks live on the 8 TB drive. Backups run on a schedule to the internal disk and off-site.
  • RHEL administration. NetworkManager for static addressing, firewalld, SELinux file contexts for VM storage, and libvirt storage pools for practice VMs.
Laptop or phone
  |  DNS lookup for jellyfin.lan
  v
Pi-hole  ->  answers with the server's address
  v
Caddy :443, certificate from the private CA
  |  reverse_proxy over the macvlan shim
  v
Jellyfin   Seafile   Frigate   ...
How a request reaches a service.