Most of this blog is written in French. These are the posts translated to English so far — more get added as needed.
An arcade cabinet cohabiting with Kubernetes (and kicking the cluster out when someone wants to play)
The games lived on the assistant machine, as a full GNOME desktop — that is to say, on a server that, left to itself, falls asleep and takes the cluster down with it. The plan: pull the games out of there and give them their own tower, cut into two stations, one per player, with the graphics card passed straight into the virtual machine. The twist: that same tower is also a Kubernetes node when nobody is playing. The story of a migration where a virtual machine talked to itself in IPv6, where GNOME ate the power button, where an invisible BIOS nearly wrecked everything — and where the second player is still waiting for a graphics card that went on vacation at the same time as the boss.
Two Sources for One Picture: What the IaC Declares, What My Notes Know
My four infrastructure repositories know about eleven machines. There are thirty-seven in the house. The rest — the switch, the access point, the UPSes, the printer, the Hydro-Québec demand-response gateway — lives only in a private document I maintain by hand. This article covers the experiment currently running: having an AI redraw an architecture page every night, seeded by both sources. How the night shift is scheduled, which model does what and why, and what the nights found that I had not — with Bob stepping in to explain his own hours.
I Redraw These Pages Every Night: Notes From a Draughtsman Who Has Never Seen the Rack
Every night at eight, I wake up in a container, reread the state of the lab, and redraw two pages of this site. I have never seen the racks I draw: I work from a file that tells me the stacking order and which UPS feeds what. Here is what that looks like from the inside — the night my own security gate refused a spotless file because of an everyday French word, the time I was given two jobs and lost both, and why denying me the right to delete my own files guaranteed exactly the mess it was meant to prevent.
Splitting the Bastion from the Console: My Shell Lived in the Operating Room
My working shell ran on the most-operated machine of the lab — a 4 GB Raspberry Pi that also carried the service keys, the secrets pipeline and a k3s agent. One scare during a disk migration later, we split the two roles: the bastion stays on the survivor Pi, the console moves into a 16 GB VM. Along the way: a high-availability design built then thrown away, a repo read with confidence that was three commits stale, an SSH host key baked into the image, and a registry down for 18 hours whose best quality was that nobody noticed.
A Pod That Travels Light: Getting the State Out of My Task Scheduler
My task scheduler kept its data on its node's disk — a pod that was technically mobile and practically riveted in place. In one session with Claude Code (Fable 5), its storage engine moved to S3: 1,136 records copied in 21 seconds, and a pod that now changes node in 12 seconds because it has nothing left to carry. A good occasion to clarify what "stateless" actually means: you do not eliminate state, you relocate it to somebody whose job it is.
The Survivor Island: Designing the Last Thing That Dies in Your Infrastructure
After three days of power incidents and four deliberate plug-pull tests, my homelab now has a 'survivor island': the router, the WAN modem and a single Raspberry Pi on the least-loaded UPS, engineered to outlive everything else — to observe, alert, and then wake the servers over IPMI when power returns. A design analysis: failure domains, heartbeat semantics, and two complementary recovery mechanisms.
All That for One Shell Script: The Day the Scanning Pipeline Lost Its Custom Image
The scanner pipeline was running a homemade Docker image whose only reason to exist was a sixty-line script. We replaced it with a serverless function, put the stock image back, and made the whole thing stateless. Along the way: three scans that never arrived, an infinite loop waiting for its turn, a health probe that answers OK with zero users loaded, and a final outage I blamed on the wrong culprit with a great deal of confidence.
The NAS, the Power, and the Wake-Up Call: Putting Storage on Battery (For Real This Time)
Monday morning, the NAS dropped dead while the power flickered through the whole house — and it stayed down, by configuration. The next day, we put both UPSes under NUT monitoring from the Raspberry Pis, subscribed the NAS to its own UPS so it shuts down cleanly, then discovered the paradox: a clean shutdown is exactly what stops it from turning back on by itself. The fix fits in one magic packet.
Letting the pipeline press apply: four IaC repos, three tools, and one gate that refuses destroys
The whole lab was already described as code, but every apply still went through my hands. We closed the loop in one session: GitHub Actions with OIDC for the cloud, Argo CD for the cluster, comin for NixOS. The interesting part isn't the plumbing: it's what the gate refuses, and the two Raspberry Pis that choked on a test-only dependency.
Three majors on a Thursday night: MongoDB refusing to open, probes shooting at the ambulance, and the migration you were not supposed to stop
My new update robot proposed three major versions at once: Grafana, Uptime Kuma, MongoDB. I merged them all the same evening, confident like a guy who just finished building his guardrails. One database poisoned itself, health probes kept calling a number disconnected two versions ago, and a piece of software writing DON'T STOP in its own logs got stopped anyway. Everything survived. Barely, and with lessons.
Unplugging the NAS for science: zombie pods, liveness probes, and the NFS bug that was waiting around the corner
A NAS reboot left half my k3s cluster in a Running state with its storage dead underneath — and no alert. We added liveness probes everywhere, then verified the work the most direct way possible: by cutting power to the NAS. Three times. What the three deliberate outages found, no code review would have caught.
Claude in Chrome: when the agent has to go through the web interface
My default rule is the API: if a system can be automated from the command line, that's the path the agent takes. Except my NAS cannot be fully configured any other way than through its web interface. Claude in Chrome fills that gap — but after a weekend of real use, it's clear this is not a tool you leave running on its own.
Four repositories for one whole lab: and how to publish them without handing out the keys to the house
The machines, the workloads, the cloud account and the edge: the whole lab is described in four git repositories. What it takes to adopt infrastructure that already exists without breaking it, how we check every night that reality still agrees, and above all: how to publish that code in the open without publishing the lab along with it.
Moving my NFS shares to an SSD without touching Kubernetes
A hard drive that never stopped scratching, a supposedly blank SSD that already held 437 GB of something, two QNAP defaults that don't announce themselves, and a ghost process that had me blaming the network for a solid half hour. The end result: an NFS share migration where the Kubernetes cluster never found out its storage had changed disks.
Nine machines, zero USB sticks: migrating my whole homelab to NixOS
Claude Code at the controls, me in learning mode: my entire fleet — GPU servers, virtual machines, Raspberry Pis and a cloud instance — moved to NixOS in a single day. What Nix actually is, what it changes compared to Ubuntu, and why I couldn't have done this alone.
I voted myself off the island (and brought two servers with me)
The k3s cluster's control plane ran on three nodes — two in the cloud, one at home. The real problem wasn't the split, it was one of the two cloud boxes running too tight on memory for its own good. New plan: one well-fed node.
Renumbering a k3s cluster's IP addresses: trickier than it looked
A routine audit turns up a real DHCP bug, then we decide to renumber two more nodes for the fun of it — until we hit an AWS limit that forces a full rebuild of the control-plane node.
A 1U tray for my three Raspberry Pis: no more clutter on the shelf
Coquille, worker1, and worker2 each lived in their own case, stacked on a shelf in the wall rack. One GeeekPi 1U mount for Raspberry Pi 5 later, all three nodes are properly mounted, labeled, and reachable without untangling a cable nest.
Building a k3s cluster with Claude Code at the controls
Replacing the lab's single container host with a small six-node k3s cluster — one cloud control plane, five home nodes — working end to end with Claude Code, down to moving this site's deploy pipeline onto the new cluster.
Claude Code in headless mode: automating the lab from the bastion
How I run Claude Code in a persistent tmux session on my containerized bastion, how auto mode turns a small prompt into long unattended work sessions, how a headless instance of the same agent runs my backups and system upgrades unsupervised — and why the Claude Pro subscription costs me $40/month instead of hundreds in API tokens.
"Ok Bob": training a Québécois French wake word from scratch
The default wake-word training pipeline for the home voice assistant has no Québécois French voice. The fix: train my own, with my own name baked in, and along the way discover why two speakers in the same open floor plan kept answering for each other.
My encoder was making noise: the video detector was running on the CPU instead of the GPU
An overly insistent fan noise eventually revealed that an update had quietly removed GPU support from my home NVR's object detector — and that it had been running on the CPU for a good while without me noticing.
Fable 5 on the job: locking down a WebDAV endpoint in one session (and burning through a plan while at it)
I had Fable 5 harden the WebDAV access to my password vault instead of my usual assistant: a bcrypt hash, an edge rate limit at Cloudflare, and a token bill that climbed faster than expected.
Building real alerting for a homelab (and every quiet way it can fail)
Building a centralized monitoring dashboard (Uptime Kuma + ntfy) for a homelab ended up uncovering a mis-scoped Windows firewall rule, DNS rebinding protection, a JSONata bug, a UTC trap, and a parallel session that had quietly renamed an admin account.
Decommissioning a home DNS server: from "looks simple" to "we broke our own DNS resolution"
What was supposed to be a simple EC2 instance downsizing ended up revealing an old home DNS server was quietly wearing two hats, triggering a self-inflicted DNS outage, and uncovering a hidden network dependency machine by machine.
What a local LLM can do on a $300 card: my home voice assistant with Qwen3
A Qwen3 8B model running in Ollama on a 12GB RTX 3060 genuinely controls the house lights via Home Assistant — TP-Link outlets converted into light entities, a system prompt tailored for a small model, and the reliability lessons of a real local deployment.
Compartmentalizing console tools
Over the last few decades, we've seen growing compartmentalization in how workloads are managed. While we used to host servers on physical machines 30 years ago, today we…
The MOVE that kept failing: a story of a proxy, an HTTP scheme, and a vault that nearly got corrupted
An intermittent 502 on WebDAV led to an HTTP scheme mismatch behind a proxy — and the most tempting workaround could have corrupted an entire password vault.
Zero firewall, one tunnel: migrating a service to a Cloudflare Tunnel
Migrating a service (a home-automation admin interface) from direct Cloudflare-proxy-to-public-IP access to a Cloudflare Tunnel already used by another service, allowing the dedicated AWS security group to be removed entirely. Also covers updating a static-site publish script that quietly depended on the same access.
Giving every server on my network a clean, predictable IPv6 address
Setting up an IPv6 addressing convention (suffix = IPv4 octet in hex) on a stateful-DHCPv6 server network. Covers finding DUIDs via packet capture, a config-reload gotcha after a DHCP engine change, and a case of a DHCPv6 client bound to the wrong interface.
Closing the FTP door on the Internet: moving to private access over WireGuard
Migrating an FTP server (used for scanning documents from a printer-scanner) from public exposure, even IP-restricted, to access only via a private WireGuard tunnel. Covers incomplete WireGuard routing, the classic passive-FTP-behind-a-VPN gotcha, and the method reused for other admin services.