Four nodes, five old GPUs, and an AI with root.
This is a small home lab: one custom-built server and three retired office desktops running Proxmox, a storage server full of secondhand disks, a local AI stack on 2016-vintage workstation graphics cards, and an AI assistant that does most of the day-to-day administration over SSH. Nothing here is enterprise gear. Everything here is real, measured, and still running.
Details are kept general on purpose: no addresses, hostnames, account names, or anything that would help someone poke at the network. No links out, either.
# the cluster, as of this writing root@node1:~# pvecm status | grep -E 'Nodes|Quorate' Nodes: 4 Quorate: Yes root@node1:~# pveversion pve-manager/9.2 (running kernel: 7.0-pve) root@node1:~# nvidia-smi --query-gpu=name,memory.total --format=csv,noheader Quadro M2000, 4096 MiB root@node1:~# uptime 19:35:57 up 7 days, 23:46, load average: 1.31, 1.31, 1.29
The room: no rack, no enterprise gear
The heart of the cluster is a custom-built tower: a 16-core Ryzen 9 with 64 GB of RAM and a SAS host bus adapter feeding six spinning disks to the storage server. Around it sit three retired office desktops that were headed for recycling. Each one became a Proxmox node; two of them carry a pair of workstation GPUs each.
Proxmox is free software that lets one physical computer host many independent virtual machines and lightweight containers. If one node needs maintenance, the others keep going, and every guest can be snapshotted before a change and rolled back if the change goes wrong.
Everything is managed from one laptop: a ThinkPad T14 (8-core Ryzen 7 PRO, 14 GB) running Omarchy, an opinionated Arch Linux desktop built on the Hyprland tiling window manager. It drives a 3 × 2 wall of six screens through two DisplayLink docks. Proxmox, dashboards, and the wiki live on the big monitors; terminals and the AI assistant sit on the laptop panel in the middle. The laptop is swapped daily with a work machine, so the docks, not the laptop, own the monitors.
Also on the wall
- Two spare iPads run kiosk dashboards: cluster health on one, guest-network activity on the other. The older iPad can't run a modern browser, so a container renders the dashboards to PNG and serves a plain auto-refresh page instead.
- One battery backup (a 600 W consumer UPS) carries all four nodes, the router, and the satellite dish. Monitors and docks go on the surge-only side so they don't eat runtime.
- Workstation GPUs, five of them. The Quadro M2000 is a 2016 Maxwell card: 4 GB of VRAM, 75 W, single slot, no power connector. Cheap, quiet, and still enough for an 8-billion-parameter language model or CUDA speech-to-text. All five run the same NVIDIA 580-series driver through DKMS, with persistence mode on so an idle card draws about 48 W instead of 110 W.
The nodes
One flat gigabit LAN, no shared storage, nightly backups to the NAS. The custom build does the heavy lifting; the desktops carry the GPUs and the lighter services.
| Node | CPU | RAM | Boot / VM storage | Extras |
|---|---|---|---|---|
| Node 1 custom build | AMD Ryzen 9 5950X 16 cores / 32 threads | 64 GB | 500 GB NVMe, LVM-thin, plus a dedicated 12 GB volume for video transcodes | 1 × M2000 SAS HBA with six spinning disks passed through to the NAS VM |
| Node 2 office desktop | Intel Core i5-8400 6 cores | 16 GB | 240 GB SATA SSD | Wiki, Git, automation, push notifications, analytics, media automation |
| Node 3 office desktop | Intel Core i5-6500 4 cores | 32 GB | 120 GB SATA SSD | 2 × M2000 local language model; the CMS behind the public sites |
| Node 4 office desktop | Intel Core i5-6500 4 cores | 16 GB | 120 GB SATA SSD | 2 × M2000 speech-to-text, tunnel connector, honeypot, price API |
Storage: five disks from 2012 and one from 2023
The NAS is a TrueNAS virtual machine on node 1. Its disks are passed through individually from the host, so the VM owns them but the host still reads their SMART health. The main pool is RAIDZ1 across five 2 TB enterprise SAS drives, all manufactured in early 2012, now between 45,000 and 59,000 power-on hours. That is five to seven years of continuous spinning, each.
They are watched closely. The first-ever long SMART self-tests on the array ran in October 2026 and came back clean, but one drive had been growing defects and was the weakest member. A spare 4 TB NetApp-branded SAS drive from 2023 was reformatted from 520-byte to 512-byte sectors, hot-plugged into the VM, and swapped in for the failing drive with the pool live. The resilver moved 6.5 TB in about six hours at roughly 200 MB/s with zero errors. The pool stays at 2 TB per member until all five are 4 TB, so the capacity gain comes later.
A second single-disk pool holds a 1 TB desktop drive for scratch. Nothing is offsite yet, which is the honest weak spot and the top of the roadmap.
What lives on the pool
- Nightly backups of every container and VM at 02:00 (vzdump, zstd), kept 7 daily / 4 weekly / 3 monthly. Restore has been proven end to end.
- A 1.25 TB archive of open-weight AI models: 16 model repositories, checksum-verified and pinned to exact commits, downloaded over a satellite link that tops out around 17 MB/s. More on that in stop six.
- The media library the household streams from: a few hundred films and about 1,750 TV episodes, slowly being right-sized to 1080p one title at a time.
- A nightly one-way mirror of a cloud drive, with replaced files kept in dated history folders for 180 days.
Lesson learned the hard way. The very first vzdump --all tried to back up the NAS VM's six passthrough disks (10 TiB) and deadlocked the guest. PCIe passthrough is excluded from backups automatically; raw disk passthrough is not. Every passthrough disk now carries backup=0, and the NAS backs up as a 64 GiB boot disk in 29 seconds.
Network, power, and the things that keep it safe
Satellite in, nothing out
Internet arrives by satellite, behind carrier-grade NAT, through a small OpenWrt-based consumer router. Midday it does about 175–215 Mbit down; evenings can sag to 15–45. Upload is the real constraint at 10–14 Mbit, so the router shapes upload with cake (diffserv8, auto-rate between 10 and 16 Mbit) and marks traffic in five tiers: laptops first, phones, then the cluster, then everything else, with the media stack at the bottom as bulk. Shaping download turned out to make things worse on this link, so download is controlled at the application layer instead: 1.5 MB/s during the day, unlimited overnight.
There are no port forwards, and under CGNAT there couldn't be anyway. The handful of things that must be reachable from outside, like an upload endpoint for a hobby site and a web-analytics collector, come in through an outbound tunnel to a CDN: a small container dials out, and traffic rides back down it. The public websites themselves are static files served from the CDN's edge, so the cluster being off never takes a public site down.
Remote access
Administration from outside the house goes over a mesh VPN with the router acting as a subnet router. Family members streaming from another house use the same mesh, but their devices are tagged so the VPN's access policy lets them reach exactly one port on one host: the media server, and nothing else.
Defence in depth
- Cluster firewall with a default-drop inbound policy; only an allow-list of admin addresses reaches the Proxmox web UI, SSH, and console ports. The rest of the LAN is explicitly dropped.
- SSH keys only. Password logins are disabled on all four nodes and the router. Every new container gets the admin key before it gets anything else.
- Real TLS certificates from Let's Encrypt on every node's web UI, issued by DNS challenge so no node needs a public address, auto-renewed.
- A honeypot: a decoy container that looks like an easy target, with fake login services nothing legitimate ever touches. Any connection raises an email alert. It is kept off the dashboards on purpose.
- Isolated guest Wi-Fi for TVs, iPads, and IoT, with per-device exceptions for the two dashboard iPads. WPA3 mixed mode since October.
- Secrets live in a password-store on the laptop, GPG-encrypted and versioned in the private Git server. The AI reads a secret by name when it needs one; the human never pastes it into chat.
Power
The UPS is small for the load, so the cluster is tuned to idle around 60% of its capacity: CPU miners off, GPU persistence on. If the mains fail, the UPS daemon waits 90 seconds and then shuts the whole cluster down in a scripted order: the three desktops in parallel, then node 1's containers, the laptop VM, the NAS, and finally node 1 itself. A thermal guard on every node pauses heavy work at 82 °C, stops the hottest guest at 90 °C, and cleanly powers the node off at 95 °C. The storage VM is on its protected list and is never auto-stopped.
What runs on it
Almost everything is an LXC container: lighter than a VM, near-native speed. Only two guests are full VMs: the storage server and a retired laptop. Sizes are what each guest is allocated, not what it uses.
| Service | What it does | Type | Size |
|---|---|---|---|
| TrueNAS | The storage server from stop three: SMB shares for backups, media, the model archive, and the cloud-drive mirror. | VM | 4 vCPU · 16 GB |
| Jellyfin | The household media server, streaming to TVs and phones at home and over the VPN. Hardware transcoding on node 1's GPU (about 20× real time), 17 live channels relayed in from free sources, intro skipping, scrub thumbnails. | LXC | 8 vCPU · 4 GB |
| Media automation | A Docker stack that handles requests, subtitles (with the speech-to-text service below), and downloads through a VPN with a kill switch, plus the dashboards and a worker that re-encodes the library to 1080p one title at a time. | LXC | 4 vCPU · 4 GB |
| Ollama 2 GPUs | A local Qwen3 8B language model spread across both of node 3's cards, so private email and meeting transcripts never leave the house. | LXC | 4 vCPU · 12 GB |
| whisper.cpp GPU | Speech-to-text with CUDA for meeting notes and subtitles, behind a small HTTP API. | LXC | 3 vCPU · 6 GB |
| n8n | Workflow automation: a daily pricing refresh for a hobby site, an AI-read inbox that turns email into structured records, chat-bot intake, and a GPU-deal watcher that pushes alerts to a phone. | LXC | 4 vCPU · 4 GB |
| Price API | A home-grown Python service that identifies collectible items and pulls market prices from four providers with pacing and quotas, for a hobby site. | LXC | 2 vCPU · 2 GB |
| WordPress | The CMS behind the public sites. Never exposed: a crawler exports it to static HTML and a deploy tool pushes that to the CDN. Also hosts an upload inbox, a chat bot, and a small ledger. | LXC | 2 vCPU · 16 GB |
| Prometheus + Grafana | Metrics and dashboards for every node: temperatures, load, GPU use, disk. Also renders the wall iPad pages. | LXC | 2 vCPU · 4 GB |
| Gitea | Self-hosted Git. Infrastructure scripts, site code, this page, and the encrypted secret store are all versioned here. | LXC | 2 vCPU · 1 GB |
| BookStack | The runbook wiki: 10 books and about 80 pages on how every piece works and how to fix it, written mostly by the AI. | LXC | 2 vCPU · 2 GB |
| Umami | Privacy-friendly web analytics for the public sites. | LXC | 2 vCPU · 6 GB |
| ntfy | Push notifications to phones from any script or workflow. | LXC | 1 vCPU · 512 MB |
| Actual Budget | Household budgeting, self-hosted. | LXC | 2 vCPU · 4 GB |
| Tunnel connector | The outbound-only connector from stop four. | LXC | 1 vCPU · 512 MB |
| Cloud-drive mirror | Nightly rclone copy of a cloud drive onto the NAS, with 180 days of history. | LXC | 2 vCPU · 1 GB |
| Honeypot | The decoy from stop four. | LXC | 1 vCPU · 512 MB |
| Second WordPress | A not-yet-public site for a local road-safety advocacy effort. | LXC | 2 vCPU · 2 GB |
| Retired laptop (P2V) | A cracked-screen laptop converted physical-to-virtual: its disk was imaged into a VM, so everything on it lives on and the hardware could be retired. | VM | 6 vCPU · 8 GB |
| CPU miners | Three containers that mined a CPU-friendly coin on idle cores, plus a wallet. Switched off since the UPS swap; the thermal guard still knows how to pause them if they ever come back. | LXC ×4 | off |
A day in the life
- BackupsEvery container and VM to the NAS; the retired-laptop VM on its own weekly job.
- Cloud-drive mirrorOne-way sync, replaced files moved into a dated history folder.
- Price refreshn8n asks the price API for fresh market data, updates every item in the CMS, re-exports the static site, deploys it, and emails a report.
- Second passA smaller category is priced from a different source.
- WatchingThermal guard every 20 s, metrics every 15 s, the honeypot always, a GPU-deal watcher on a timer, and the AI-read inbox whenever mail arrives.
- Caps liftDaytime download caps come off so bulk transfers run while nobody is on a video call.
The AI stack
Local language model
Node 3's two M2000s (8 GB VRAM combined, compute capability 5.2) run Ollama with Qwen3 8B split across both cards at about 7 tokens per second, and Qwen3 4B on a single card at about 11. Context is capped at 8k tokens; 16k runs out of memory. Maxwell is too old for most modern inference engines, so it is llama.cpp and GGUF only, no vLLM. That is enough for the jobs it has: parsing incoming email into structured data, summarising meetings, and smoke tests for workflows.
Speech-to-text
Node 4 runs whisper.cpp built from source for sm_52 with CUDA 12.8 (a 32-minute compile on a 4-core desktop) using the medium model quantised to 5 bits, at five to seven times real time. The obvious choice, faster-whisper, was a dead end: its CTranslate2 backend ships no Maxwell kernels and refuses int8 on the card. A prebuilt Docker image was 30 GB and also failed. Build it yourself, it turns out.
Meeting recorder
One hotkey on the laptop starts a 16 kHz mono recording. Pressing it again sends the audio to the cluster's whisper service (with a local fallback), chunks the transcript through the local Qwen3 model, and writes a Markdown note with a summary, decisions, actions, and open questions. A 40-minute meeting takes about 5.5 minutes to transcribe and 10 to summarise. After two runaway overnight recordings, it now stops itself at 75 minutes.
Vision in the loop
For a hobby site, photos of small collectible items taken on a phone are uploaded through the tunnel. OpenCV finds every item in the photo and matches fronts to backs by grid cell. Each one then goes to a cloud vision model to be identified, with a cheaper model first and a stronger one only when the first is unsure. Prices are fetched, draft entries are written, and a human approves the batch from a chat command. The first batch was 66 items from a handful of photos.
The weights archive
Sixteen open-weight model repositories, 1.25 TB, sit on the NAS under a 2 TiB quota: embedding models, a large speech model, and a spread of current open LLMs from 7B dense up to 120B mixture-of-experts, every file SHA-256 verified and pinned to a commit. The downloader resumed across power cuts and a satellite link that caps near 17 MB/s; the whole set took about eleven days. None of the large models fit the current GPUs; the archive exists so that the day bigger cards arrive, the models are already on the shelf and verified.
What's not here. This page only describes what is running today. The GPU node under construction, the storage expansion, and everything else planned are on the roadmap.
The AI administrator
Everything on this page was built, repaired, or rebuilt by Claude, an AI assistant from Anthropic, working through its command-line tool on the admin laptop. The human brings the ideas, the hardware, the logins, and the decisions. The AI reads the state of the cluster over SSH, proposes a plan, and once approved does the work: writes the scripts and configs, runs them, checks the result, and documents it in the wiki.
It is not unsupervised. The whole arrangement rests on a short set of written rules the assistant reads at the start of every session.
- Snapshot before any package or config surgery, and name the snapshot so it is obvious what it was for.
- Verify with evidence: read the live state back, never trust a config file to mean the service is doing it.
- Root on the servers, never sudo on the human's own laptop. That password stays with the human.
- Secrets come from the password store by name. Never ask the human to paste one into chat.
- Check the notes before re-diagnosing. Memory files carry what was learned last time.
- Document every change in the runbook wiki, and leave a one-line pointer in the task list.
- Anything outward-facing, destructive, or that costs money gets a yes from the human first.
- Report honestly: if a test failed, say so with the output. If a step was skipped, say that.
How much work is that?
AI models read and write text in small pieces called tokens, roughly three-quarters of a word each. The figures below come from the assistant's own session logs on the laptop. The read number is much larger than the written one because at every step the assistant rereads the running conversation, its notes, and the files it is working on.
| Since the first audit on 2 Sept 2026 | Read | Written |
|---|---|---|
| About 24 working sessions over 50 days, 8,500 steps of work | ≈ 3.4 billion tokens | ≈ 7.6 million tokens ≈ 5.7 million words of code, configs, docs, and replies |
Three things it actually did
- The disk swap in stop three. Read SMART from the host, found the weakest drive, reformatted the spare's sector size, hot-plugged it into the running VM, drove the replacement through the NAS API (learning that it wants a disk identifier, not a device name), watched the resilver for six hours, and detached the old disk. The human's part was approving it and, later, pulling the dead drive out of the bay.
- The firewall gap in stop four. While setting up family streaming over the VPN, it noticed that subnet-routed traffic arrives source-NATed as the router's own address, which the cluster firewall treated as an admin. It wrote the VPN access policy that pins family devices to a single port, and flagged the firewall allow-list for tightening.
- An offsite streaming test. When family tested playback from another house, it polled the media server's session API every 15 seconds for 45 minutes, correlated bitrate, GPU encoder load, router upload, and the VPN path (direct, not relayed), and wrote up that a 4.8 Mbit remux played without a stall against a 10 Mbit uplink.
Where it stops. Some things the assistant declines or hands back on principle: resetting passwords at an identity provider, entering credentials or payment details, anything that would publish fabricated activity on a real site. When a request lands there, it says so in a sentence, offers the nearest thing it can do, and moves on.
Seven weeks, in order
- The auditA read-only pass over the cluster finds no backups, a dead disk, the firewall off, and no alerts. The first real TLS certificate goes on. The laptop gets a fresh Omarchy install the same day.
- FoundationsNightly backups (and the passthrough incident), the cluster firewall, email alerts, the honeypot, the wiki, the password store, a media-automation rebuild.
- HardeningUpload shaping with cake, certificates and key-only SSH on all nodes, the private CA retired, an IP squatter (a TV) evicted and the DHCP pool shrunk.
- New guestsThe price API for a hobby site, the cracked laptop converted to a VM, the model archive planned and the downloader started.
- The linkEvening congestion and a five-minute WAN flap on the satellite service are measured and ticketed.
- Master auditA ranked review of everything, fifteen quick wins applied, the thermal guard on every node, scripts and secrets versioned in Git.
- Rolling upgradeAll four nodes to a new kernel one at a time; GPU transcoding on the media server; download shaping switched off after measurements showed it was the bottleneck.
- Brains and eyesThe two-GPU language model node, the speech-to-text node, Prometheus and Grafana.
- Archive complete1.25 TB of model weights verified on the NAS. The hotkey meeting recorder ships.
- Bad daysA cloud-drive client zero-byte-overwrites 22,000 files (restored through the provider's API); the UPS battery dies and the cluster goes dark. The nightly cloud-drive mirror exists because of this week.
- New UPS, first launchMiners off and GPU persistence on to fit the load. The first public site goes live as static files on a CDN, with a daily automatic price refresh.
- Vision pipelinePhone photos to identified, priced draft entries, approved from chat.
- Audits and ideasHardware and Wi-Fi audit, and WPA3 on the home Wi-Fi.
- More eyesWall-iPad dashboards, a push-notification server, a GPU-deal watcher, and the parts for the next GPU node ordered.
- HousekeepingWhole-cluster shutdown to add a second UPS, node 4 doubled to 16 GB, the NAS taken from 87% to 59% full.
- The disksFirst long SMART tests on the array, the spare reformatted and tested, and the failing drive replaced live with a clean resilver. Automatic cluster shutdown on battery is armed.
- TodayA browser game is built and published in a day; family streaming over the VPN is tested from another house and the access policy is tightened; this site is written.