The second file in my homelab notes repo is the hardware note for the machine the whole lab runs on. One box: a refurbished Lenovo ThinkCentre M720q Tiny, bought from a refurbisher on an online marketplace in July 2026 for $172.94, with a one-year seller warranty and a short return window.
That is the entire lab. Not a node in a cluster. The single point of failure is written into my own notes as a single point of failure, because pretending otherwise would not make it less true.
Everything below is from those notes as they stood on 2026-08-23 — a historic account, not an inventory of what is running right now.
Check the machine, not the listing
The date code puts manufacture in 2019, so the unit was about seven years old when it arrived. On arrival I recorded the cosmetic condition as Fair — light corner scuffs, missing paint — confirmed every expected port was present, and confirmed it was the standard I/O variant rather than the conferencing variant. That last check is not paranoia: the two are easy to confuse on a used listing, and if you skip it you find out after the return window.
Then I checked the specs on the machine rather than believing the ad:
- CPU: Intel Core i5-8400T, 6 cores and 6 threads, 1.7 GHz base, up to 3.3 GHz turbo. Core count confirmed on the box.
- RAM: 16 GB DDR4-2666, two 8 GB sticks, dual channel, both slots full. Confirmed with
dmidecode. About 15 GiB is usable; the rest is reserved by the system, which is normal. - Storage: a 256 GB NVMe SSD, M.2 2280, PCIe 3.0 x4. The 2.5-inch SATA bay is empty, so there is room for a second drive later.
Both slots being full is the fact that matters most, and it is the one a listing photo will not tell you. Going to 32 GB is not adding a stick, it is replacing both. That is the gating upgrade for everything else in the lab, and I only knew it because dmidecode said so.
What it carries, and what that costs
The host runs two self-hosted agent systems, one per VM, both migrated off a public cloud provider. One of the two gateways went live on 2026-08-21.
Sixteen gigabytes does not stretch far across two VMs. On the same day, I cut one VM from 4 vCPU and 8 GB down to 3 vCPU and 6 GB, specifically to free memory for the second one. The second VM then measured about 2.4 GB live with a 2.6 GB peak — higher than I had estimated beforehand. My notes say so rather than quietly correcting the estimate, and that measurement is why a third workload cannot move onto this host until the RAM swap happens.
The planned network and edge services are not stood up, and the heavier security-lab work is deferred to a machine I have not bought. Neither is built, and I am not going to write them as though they were.
A week lost to a cable that was fine
This is the part worth reading.
The onboard Intel NIC hits a known e1000e driver bug. The kernel logs Detected Hardware Unit Hang, the transmit queue stalls, and the link stays electrically up the whole time. It behaves like a bad cable, and I treated it as one for about a week.
The persistent fix in my notes is to disable the TSO, GSO and GRO offloads on the interface with a post-up ethtool line under the bridge stanza in the network interfaces file — one feature per line. A live hang clears if you bounce the link, and I check for recurrence by grepping the timestamped kernel ring buffer for hang.
The fix is easy to look up. The reasoning is what actually cost me the week, and it is two ideas:
- Carrier detected is not traffic passing.
LOWER_UPmeans the physical link negotiated. It says nothing about whether packets are moving. - "Reseating the cable fixed it" does not prove a physical fault. A link reset also clears a driver stall. Identical symptom, identical apparent cure, completely different cause — and that false confirmation is exactly what hid the bug from me.
The general habit I took from it: for anything hardware-adjacent, read the timestamped kernel ring buffer first, before you start swapping parts.
The wall jack is dead, so the network is interim
The apartment's ethernet wall jack does not work. I reproduced NO-CARRIER with two cables on two machines before concluding it was the jack and not my gear.
The workaround is a small travel router in repeater / WiFi-as-WAN mode, bridging to the managed building WiFi and serving a private LAN behind it. That has a useful side effect: it gets around client isolation on the shared network, so my devices can actually see each other. The host is wired into that router. The wireless leg is the weak link and I know it; the whole arrangement is labelled interim in my notes and it deserves the label.
Two lessons came out of it. On managed or shared WiFi you do not control, bring your own router. And a device with a static address will not appear in a router's DHCP client list — "not in the list" is not "not connected", so test the address directly.
The upgrade I decided not to do
On 2026-08-23 I looked at memory ballooning and deliberately left it unconfigured.
Neither VM had a balloon setting, which means each one reserves its full allocated RAM at boot regardless of what the guest is actually touching. Both were measured well under their allocation, so there was no real pressure, and setting a floor added complexity for a problem I did not have. Revisit if the numbers climb.
The generalisation is the useful bit, and it is not obvious: the hypervisor reserves a VM's full configured RAM at boot, so a host reporting tight free memory does not by itself prove real memory pressure. Check inside each guest before you buy RAM.
Hypervisor install, and the small things that waste an evening
Proxmox VE 9.2 (Debian 13.5 base, kernel 7.0 generation), installed 2026-07-15 from the official ISO written to USB. Gotchas, in the order they bit:
- The USB flash has to complete fully, privilege prompt included, or the installer boots garbage. Pull the USB after install or you get PXE boot loops.
- A fresh install points at the paid enterprise repositories, which return 401s without a subscription. Switching to the no-subscription repository is the fix, and the "no valid subscription" popup is normal — the product is free and fully functional.
- The archive keyring filename is singular:
proxmox-archive-keyring.gpg. - Heredoc pastes into the browser shell get mangled. Print the file back and read it before you run the package update.
- After a large upgrade the first reboot is slow, because networking comes up last. Give it two or three minutes before you decide it failed.
The storage layout is the default ext4 plus LVM-thin on the single NVMe, not ZFS. The lab standard is ZFS, but on one disk there is no redundancy either way, so LVM is fine here. Honest caveat: I inferred that layout from the presence of the thin pool rather than verifying it directly, and it is still an open item in my notes.
Backups, including the leg I have not tested
Nightly host-level VM dumps at 02:00 to local storage, snapshot mode, zstd compression, keep-last-3. Nightly at 03:30, a guarded script syncs one object offsite to a cloud object-storage bucket, deliberately sized to stay inside the provider's 10 GB free tier. The guard exists so a failed upload cannot empty the bucket — cheap to write, and it removes the failure mode where your backup process is the thing that destroys the backup.
A weekly TRIM timer runs for a reason that surprised me: on a VM, deleting files does not shrink the dump. TRIM is what releases the freed blocks, with discard enabled on the guest disks. If a backup balloons right after a big delete, run TRIM. One VM also has a second virtual disk excluded from the dump, because it holds that application's own backups and there is no point backing up backups.
And the honest gap: I have tested a local restore end to end — restore, boot, delete. I have not tested restoring from the offsite copy. Until I do, I do not have a verified disaster-recovery path, only a backup that appears to be there.
A $29.98 switch instead of a UPS
On 2026-08-23 I bought a used Cisco SG300-10 managed switch for $29.98. The interesting part is not the switch, it is what it beat in a deliberate priority review.
More compute: both VMs were sitting well under allocation, so there was no case for it. More storage: nothing was under real pressure, and SSD prices were running well above normal. A UPS: genuinely useful insurance, but nothing had actually failed yet. The switch won because it is the one purchase that directly serves the learning track I am on — Network+ and SOC analyst work — through hands-on VLAN configuration.
Status as of these notes: purchased, not deployed. VLAN segmentation is the next infrastructure phase, with a firewall appliance after it. Neither has happened yet.
Still open
From the same notes: deploy the switch and start VLAN segmentation, drop a temporary SSH override in favour of key-only access and then harden keys and add fail2ban on the host, do the 32 GB RAM swap, test an offsite restore, add a drive in the empty SATA bay, stand up the edge services, and decommission the cloud account only once everything is verified — keeping it as a fallback until then.
One habit runs through that list: verify by function, not by status. "The service is running" is not evidence. Doing the thing the service exists to do and getting the expected reply back is evidence. For anything running as a per-user service, a reboot is the real durability test.
What this post is not
No power draw, no wattage, no electricity cost, no benchmarks, no temperatures, no uptime figures. My notes do not contain any of those, so I am not going to produce them here — which is a little awkward, since I opened this series talking about cost control. The purchase prices are real; a cost-per-month figure would be invented.
No addresses, hostnames, account names or hardware identifiers. No claim that the offload fix has been stable for any particular length of time, because the notes record the fix and the recurrence check, not a duration. And no claim that any of this still describes my lab today — it is a snapshot dated 2026-08-23.
Next post: the next file in the repo.
— Rich