<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Homelab &amp; Infrastructure on vo.rs</title><link>https://vo.rs/categories/homelab--infrastructure/</link><description>Recent content in Homelab &amp; Infrastructure on vo.rs</description><generator>Hugo</generator><language>en</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Wed, 01 Jul 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://vo.rs/categories/homelab--infrastructure/index.xml" rel="self" type="application/rss+xml"/><item><title>Vaultwarden: Self-Hosting a Password Manager You Actually Control</title><link>https://vo.rs/story/vaultwarden-self-hosting-a-password-manager/</link><pubDate>Fri, 19 Jun 2026 10:00:00 +0000</pubDate><guid>https://vo.rs/story/vaultwarden-self-hosting-a-password-manager/</guid><description>&lt;p&gt;A password manager is the single most important piece of software most people never think about. It quietly holds the keys to your email, your bank, your tax account, and the embarrassing forum you joined in 2009 and forgot to delete. Handing that responsibility to a cloud service is perfectly reasonable, and the big providers do a genuinely good job. But if you would rather your encrypted vault lived on a box in your own cupboard than on someone else&amp;rsquo;s servers, Vaultwarden is the project that makes self-hosting practical without demanding you become a cryptographer first. This guide walks through what it is, how to stand one up safely, and the honest trade-offs you accept when you take the keys back.&lt;/p&gt;</description></item><item><title>Tailscale: A Zero-Config Mesh VPN for People Who Hate Networking</title><link>https://vo.rs/story/tailscale-a-zero-config-mesh-vpn/</link><pubDate>Tue, 16 Jun 2026 14:00:00 +0000</pubDate><guid>https://vo.rs/story/tailscale-a-zero-config-mesh-vpn/</guid><description>&lt;p&gt;Setting up a traditional VPN is one of those tasks that looks simple in the brochure and turns into a weekend of misery in practice. You allocate subnets, open ports on a router you may not control, wrestle with NAT, distribute keys, and then discover that two clients behind the same carrier-grade NAT cannot talk to each other no matter how politely you ask. Tailscale exists because someone got tired of all that. It promises a private network where every device can reach every other device, with essentially no configuration, and for the most part it delivers.&lt;/p&gt;</description></item><item><title>Uptime Kuma: Self-Hosted Monitoring That Warns You Before Your Users Do</title><link>https://vo.rs/story/uptime-kuma-self-hosted-monitoring/</link><pubDate>Fri, 12 Jun 2026 09:30:00 +0000</pubDate><guid>https://vo.rs/story/uptime-kuma-self-hosted-monitoring/</guid><description>&lt;p&gt;There are two ways to learn that your website is down. The first is a polite alert on your phone at the first sign of trouble, giving you time to fix it quietly. The second is an angry message from a user, a customer, or your boss, after the outage has already done its damage. Uptime Kuma exists to make sure you get the first kind. It is a self-hosted monitoring tool that watches your services and shouts the moment one stops answering — and it is genuinely pleasant to use.&lt;/p&gt;</description></item><item><title>The Home Lab Upgrade Trap: When Good Enough Should Be Good Enough</title><link>https://vo.rs/story/the-home-lab-upgrade-trap-when-good-enough-should-be-good-enough/</link><pubDate>Mon, 08 Jun 2026 09:00:00 +0000</pubDate><guid>https://vo.rs/story/the-home-lab-upgrade-trap-when-good-enough-should-be-good-enough/</guid><description>&lt;p&gt;There&amp;rsquo;s a particular flavour of evening every home-labber knows. The lab is running fine. Everything you actually use is up. And yet you find yourself three tabs deep on a marketplace, comparing the second-hand price of a faster CPU, a bigger NAS, more RAM you do not need, against the box you already own that is, by every honest measure, sufficient. This is the upgrade trap, and it has cost me more money and more weekends than any actual technical failure.&lt;/p&gt;</description></item><item><title>One-Click Everything: Deploying Self-Hosted Apps with Coolify</title><link>https://vo.rs/story/one-click-everything-deploying-self-hosted-apps-with-coolify/</link><pubDate>Fri, 05 Jun 2026 08:30:00 +0000</pubDate><guid>https://vo.rs/story/one-click-everything-deploying-self-hosted-apps-with-coolify/</guid><description>&lt;p&gt;My Heroku bill was £41 a month for three toy projects that between them served maybe two hundred requests a day. None of them made money; all of them existed because I couldn&amp;rsquo;t be bothered to run a server. That is the exact psychology the managed platforms are built on: you connect a Git repository, push a commit, and moments later your application is live with a valid HTTPS certificate, a database attached, and a URL to share. Heroku pioneered the feeling, Vercel and Netlify polished it, and a generation of us grew used to never touching a server. The catch is the meter — billed by the seat, the build minute, and the gigabyte — and the numbers add up faster than the value does.&lt;/p&gt;</description></item><item><title>Build Your Own Google Drive: A Practical Nextcloud Setup on Linux</title><link>https://vo.rs/story/build-your-own-google-drive-nextcloud-on-linux/</link><pubDate>Sat, 30 May 2026 15:00:00 +0000</pubDate><guid>https://vo.rs/story/build-your-own-google-drive-nextcloud-on-linux/</guid><description>&lt;p&gt;Cloud storage is wonderfully convenient right up until you read the fine print. Your files sync everywhere, your photos back themselves up, your calendar follows you between devices, and in exchange a very large company gets a detailed map of your life and the right to change the terms whenever it likes. The number that finally pushed me was 2 terabytes: that&amp;rsquo;s what my household photo library, document archive, and assorted detritus had grown to, and renting that much storage from a big provider in perpetuity, forever, with my data as the collateral, stopped feeling like a deal. Nextcloud is the open-source answer to that bargain: a self-hosted platform that gives you file sync, calendars, contacts, and even office documents, all running on a Linux box you control. This guide gets a robust Nextcloud running with Docker, puts it behind HTTPS, connects your devices, and sets sensible expectations about how it compares to the polished giants.&lt;/p&gt;</description></item><item><title>Self-Hosting Is Not Free: Accounting for Your Own Time</title><link>https://vo.rs/story/self-hosting-is-not-free-accounting-for-your-own-time/</link><pubDate>Wed, 27 May 2026 11:00:00 +0000</pubDate><guid>https://vo.rs/story/self-hosting-is-not-free-accounting-for-your-own-time/</guid><description>&lt;p&gt;We love to tell ourselves a story about self-hosting: cancel the £10/month subscription, run the open-source equivalent at home, and pocket the difference. It&amp;rsquo;s a satisfying story. It&amp;rsquo;s also, in cold accounting terms, frequently nonsense — because the one cost we never put on the spreadsheet is the most expensive one we own. Our own time.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not writing this to talk you out of self-hosting. I host more than is sensible, and I&amp;rsquo;d do it again tomorrow. But I&amp;rsquo;ve watched too many builders justify a setup on pure financial grounds, then quietly spend forty hours a year keeping it alive while telling themselves they &amp;ldquo;saved money&amp;rdquo;. The electricity bill is easy to reckon with — I&amp;rsquo;ve done that sum in detail in &lt;a href="https://vo.rs/story/the-real-cost-of-self-hosting-electricity-time-and-what-you-actually-save/"&gt;the real cost of self-hosting&lt;/a&gt; — but the time bill is the one nobody writes down, and it&amp;rsquo;s usually the bigger number. Let&amp;rsquo;s be honest about the ledger.&lt;/p&gt;</description></item><item><title>From GitHub to Git Home: Self-Hosting Your Repositories with Gitea</title><link>https://vo.rs/story/from-github-to-git-home-self-hosting-with-gitea/</link><pubDate>Sat, 23 May 2026 13:00:00 +0000</pubDate><guid>https://vo.rs/story/from-github-to-git-home-self-hosting-with-gitea/</guid><description>&lt;p&gt;Git was designed to be distributed. Every clone is a full copy of the history, which means no single server is special and no company holds your project hostage. Yet somewhere along the way the world decided that &amp;ldquo;git&amp;rdquo; and &amp;ldquo;GitHub&amp;rdquo; were synonyms, and a vast amount of the world&amp;rsquo;s source code now lives on infrastructure owned by a single corporation. That is convenient right up until it is not. If you have ever wanted a home for your repositories that you fully control, that runs on a Raspberry Pi or a spare VPS, and that boots in milliseconds, Gitea is the answer. This guide gets you from nothing to a running, self-hosted git forge you can push to over SSH.&lt;/p&gt;</description></item><item><title>eGPU via OCuLink: Adding a Desktop GPU to a Mini PC</title><link>https://vo.rs/story/egpu-via-oculink-adding-a-desktop-gpu-to-a-mini-pc/</link><pubDate>Tue, 19 May 2026 16:00:00 +0000</pubDate><guid>https://vo.rs/story/egpu-via-oculink-adding-a-desktop-gpu-to-a-mini-pc/</guid><description>&lt;p&gt;For years the external GPU story was a sad one: Thunderbolt enclosures that cost as much as a mid-range card, ate a third of your bandwidth in overhead, and dropped the link if you breathed on the cable. OCuLink quietly changed that. It&amp;rsquo;s an external PCIe connector — no protocol translation, just raw PCIe lanes on a cable — and a growing number of mini PCs now ship with a port. I&amp;rsquo;ve been running a desktop GPU off a palm-sized machine for a few months, and it&amp;rsquo;s the first eGPU setup I&amp;rsquo;d actually recommend.&lt;/p&gt;</description></item><item><title>Borg vs Restic: Painless Encrypted Backups You'll Actually Run</title><link>https://vo.rs/story/borg-vs-restic-painless-encrypted-backups/</link><pubDate>Sat, 16 May 2026 08:00:00 +0000</pubDate><guid>https://vo.rs/story/borg-vs-restic-painless-encrypted-backups/</guid><description>&lt;p&gt;The only backup I have ever needed in a genuine emergency was the one I almost did not have. A failing SSD, a half-corrupted home directory, and the cold realisation that the &amp;ldquo;backup&amp;rdquo; I had been meaning to set up for months did not exist. I got lucky that time. The reason I had no backup was not ignorance — I knew exactly why I needed one. It was friction. A scheme that is fiddly, slow or expensive simply does not get run, and an un-run backup is worth precisely nothing the day the disk dies.&lt;/p&gt;</description></item><item><title>Power Monitoring with Home Assistant: Tracking What Your Home Lab Actually Costs</title><link>https://vo.rs/story/power-monitoring-with-home-assistant-tracking-what-your-home-lab-actually-costs/</link><pubDate>Fri, 15 May 2026 07:00:00 +0000</pubDate><guid>https://vo.rs/story/power-monitoring-with-home-assistant-tracking-what-your-home-lab-actually-costs/</guid><description>&lt;p&gt;I used to wave away questions about what my home lab cost to run with a confident &amp;ldquo;oh, not much&amp;rdquo;. Then I put a meter on it. The rack idles at 140 watts, which doesn&amp;rsquo;t sound like a lot until you do the maths: 140W is roughly 1,226 kWh a year, and at my tariff that&amp;rsquo;s about £370 just to keep the lights blinking. Measuring it didn&amp;rsquo;t make it cheaper, but it stopped me lying to myself, and it surfaced a couple of genuine surprises. The whole exercise takes an evening and one cheap smart plug, and the data it produces is the kind you can actually act on rather than the vague guilt of a quarterly bill that lumps the lab in with the kettle and the tumble dryer.&lt;/p&gt;</description></item><item><title>MQTT Essentials: The Protocol Behind Every Smart Home</title><link>https://vo.rs/story/mqtt-essentials-the-protocol-behind-every-smart-home/</link><pubDate>Mon, 11 May 2026 14:00:00 +0000</pubDate><guid>https://vo.rs/story/mqtt-essentials-the-protocol-behind-every-smart-home/</guid><description>&lt;p&gt;If you&amp;rsquo;ve spent any time poking at a smart home, you&amp;rsquo;ve bumped into MQTT whether you meant to or not. Zigbee2MQTT, ESPHome, Tasmota, Home Assistant&amp;rsquo;s own internal bus — they all lean on it. It&amp;rsquo;s worth understanding properly, because once you do, an awful lot of &amp;ldquo;magic&amp;rdquo; stops being magic and starts being a handful of topics and a tiny broker process.&lt;/p&gt;
&lt;h2 id="what-it-actually-is"&gt;What it actually is&lt;/h2&gt;
&lt;p&gt;MQTT is a publish/subscribe messaging protocol. It was designed in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper for monitoring oil pipeline telemetry over expensive, unreliable satellite links, which tells you everything about its priorities: tiny payloads, low overhead, and tolerance for flaky connections. When every byte over the satellite costs money and the connection might drop mid-sentence, you design a protocol that says as little as possible and copes gracefully when it&amp;rsquo;s cut off. Those constraints turned out to describe a battery-powered wireless sensor in a spare bedroom almost perfectly, which is why a two-decade-old industrial protocol quietly became the backbone of the consumer smart home. Devices don&amp;rsquo;t talk to each other directly. They talk to a &lt;strong&gt;broker&lt;/strong&gt; — a small server that receives messages and fans them out to whoever has subscribed.&lt;/p&gt;</description></item><item><title>Kubernetes Without the Headache: A Single-Node K3s Cluster on a Raspberry Pi</title><link>https://vo.rs/story/kubernetes-without-the-headache-k3s-on-a-raspberry-pi/</link><pubDate>Sat, 09 May 2026 11:00:00 +0000</pubDate><guid>https://vo.rs/story/kubernetes-without-the-headache-k3s-on-a-raspberry-pi/</guid><description>&lt;p&gt;The first Kubernetes cluster I ran at home cost me about forty pounds and drew less power than a night light. It was a single Raspberry Pi 4 with K3s on it, and within an evening it was serving a real web app through a real ingress controller — the same building blocks that front production clusters ten thousand times its size. That is the pitch in one sentence: you can learn the thing that runs the modern internet on hardware you would happily drop from a desk.&lt;/p&gt;</description></item><item><title>ESPHome: Building Custom Sensors for Home Assistant</title><link>https://vo.rs/story/esphome-building-custom-sensors-for-home-assistant/</link><pubDate>Tue, 21 Apr 2026 10:00:00 +0000</pubDate><guid>https://vo.rs/story/esphome-building-custom-sensors-for-home-assistant/</guid><description>&lt;p&gt;There is a particular joy in solving a problem with three quid of silicon instead of forty quid of someone else&amp;rsquo;s cloud-tethered gadget. ESPHome is the tool that makes that joy repeatable. You describe a device — its WiFi, its sensors, its outputs — in YAML, ESPHome compiles real C++ firmware, flashes it onto an ESP32 or ESP8266, and the device shows up in Home Assistant automatically with no cloud, no app, and no telemetry leaving your house. I&amp;rsquo;ve built temperature monitors, soil moisture sensors, a letterbox-open detector, and a CO₂ meter this way, and every one was easier than wiring up the equivalent off-the-shelf product.&lt;/p&gt;</description></item><item><title>Zigbee2MQTT vs ZHA: Home Assistant Zigbee Integrations Compared</title><link>https://vo.rs/story/zigbee2mqtt-vs-zha-home-assistant-zigbee-integrations-compared/</link><pubDate>Sun, 12 Apr 2026 09:00:00 +0000</pubDate><guid>https://vo.rs/story/zigbee2mqtt-vs-zha-home-assistant-zigbee-integrations-compared/</guid><description>&lt;p&gt;I once spent a Sunday evening re-pairing forty-two Zigbee devices one at a time, standing on chairs to reach ceiling sensors, because I&amp;rsquo;d decided — six months too late — that I&amp;rsquo;d picked the wrong integration. That is the whole reason this article exists. The choice between &lt;strong&gt;ZHA&lt;/strong&gt; and &lt;strong&gt;Zigbee2MQTT&lt;/strong&gt; looks like a five-minute decision when you&amp;rsquo;re setting up your first three bulbs. It isn&amp;rsquo;t. It&amp;rsquo;s a decision you live with for years, because migrating a meshed network of fifty devices from one coordinator software to another is exactly the kind of tedious evening you only want to do once, and preferably never.&lt;/p&gt;</description></item><item><title>ZFS for Mortals: Snapshots, Scrubs, and Surviving a Dead Disk</title><link>https://vo.rs/story/zfs-for-mortals-snapshots-scrubs-surviving-a-dead-disk/</link><pubDate>Fri, 10 Apr 2026 10:00:00 +0000</pubDate><guid>https://vo.rs/story/zfs-for-mortals-snapshots-scrubs-surviving-a-dead-disk/</guid><description>&lt;p&gt;Most filesystems are optimists. They write your data, assume the disk told the truth, and trust that the bytes you read back tomorrow are the bytes you wrote today. ZFS is a pessimist, and that pessimism is exactly why people who care about their data fall in love with it. It checksums everything, verifies what it reads, repairs what it can, and tells you loudly when it cannot. Born at Sun Microsystems and now thriving as OpenZFS on Linux, FreeBSD, and macOS, it folds the volume manager, the RAID layer, and the filesystem into one coherent whole. This guide walks you through the ideas and the actual commands you need to run ZFS at home without a degree in storage engineering.&lt;/p&gt;</description></item><item><title>Earthly: Containerized Build Pipelines That Combine Dockerfile and Makefile</title><link>https://vo.rs/story/earthly-containerized-build-pipelines-that-combine-dockerfile-and-makefile/</link><pubDate>Mon, 06 Apr 2026 08:00:00 +0000</pubDate><guid>https://vo.rs/story/earthly-containerized-build-pipelines-that-combine-dockerfile-and-makefile/</guid><description>&lt;p&gt;Every project I&amp;rsquo;ve maintained eventually grows a &lt;code&gt;Makefile&lt;/code&gt; full of &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;lint&lt;/code&gt;, and &lt;code&gt;release&lt;/code&gt; targets that work beautifully on my machine and nowhere else. The targets assume a particular toolchain, a particular OS, particular environment variables. Then CI does something subtly different, and you maintain two parallel descriptions of the same build forever. Earthly is the tool that finally made me delete that duplication. It&amp;rsquo;s what you get if a &lt;code&gt;Makefile&lt;/code&gt; and a &lt;code&gt;Dockerfile&lt;/code&gt; had a child who insisted everything run in a container.&lt;/p&gt;</description></item><item><title>Set It and Forget It: Automating Linux Patches with unattended-upgrades</title><link>https://vo.rs/story/set-it-and-forget-it-automating-linux-patches/</link><pubDate>Fri, 03 Apr 2026 08:45:00 +0000</pubDate><guid>https://vo.rs/story/set-it-and-forget-it-automating-linux-patches/</guid><description>&lt;p&gt;The most common way servers get compromised is not some dazzling zero-day wielded by a nation-state. It is a known vulnerability, with a published fix that has been sitting in the distribution&amp;rsquo;s repositories for weeks, on a box where nobody ran the update. I have cleaned up after exactly this more than once — a forgotten VPS, a &amp;ldquo;temporary&amp;rdquo; test host that quietly became load-bearing, a machine everyone assumed someone else was maintaining. Every time, the fix had been available for ages. Patching is unglamorous, trivially easy to defer, and quietly catastrophic when neglected. The only reliable answer is to take human procrastination out of the loop entirely — and on Debian and Ubuntu, the tool that does it is &lt;code&gt;unattended-upgrades&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Reverse Proxy Done Right: Automatic HTTPS with Caddy in Ten Minutes</title><link>https://vo.rs/story/reverse-proxy-done-right-automatic-https-with-caddy/</link><pubDate>Tue, 31 Mar 2026 13:00:00 +0000</pubDate><guid>https://vo.rs/story/reverse-proxy-done-right-automatic-https-with-caddy/</guid><description>&lt;p&gt;The last time I set up HTTPS the old-fashioned way, I spent forty minutes on the web server config and another twenty wiring Certbot into a cron job, then forgot about it — until eleven weeks later when a certificate quietly expired at 3am and I woke up to a wall of browser warnings. The renewal timer had been silently failing for a month. That is the classic Certbot failure: it works right up until the one time it doesn&amp;rsquo;t, and you only find out when a padlock turns red in production. Caddy exists to make that entire class of problem disappear. It obtains valid HTTPS certificates for you, renews them before they expire without any timer for you to forget about, and routes traffic to your applications using a configuration file short enough to read in a single breath. By the end of this guide you will have two services sitting safely behind it with proper TLS and roughly ten minutes of effort. This is what a reverse proxy is supposed to feel like.&lt;/p&gt;</description></item><item><title>Pi-hole Meets Unbound: Network-Wide Ad Blocking and Truly Private DNS</title><link>https://vo.rs/story/pi-hole-meets-unbound-network-wide-ad-blocking/</link><pubDate>Tue, 24 Mar 2026 10:00:00 +0000</pubDate><guid>https://vo.rs/story/pi-hole-meets-unbound-network-wide-ad-blocking/</guid><description>&lt;p&gt;Browser ad blockers are good, but they only protect the browser. Your smart TV, your phone&amp;rsquo;s apps, the various gadgets quietly phoning home across your network, none of them benefit from an extension you installed on a laptop. Pi-hole solves this at the source by blocking unwanted domains for every device on your network at once. Pair it with Unbound and you go a step further, resolving DNS yourself rather than trusting a third party with the record of every site you visit. This guide builds that combination, explains why each piece matters, and is candid about the gotchas. If you have ever cleared a DNS cache to fix a stubborn lookup, you already understand more of this than you think.&lt;/p&gt;</description></item><item><title>Devcontainers: Reproducible Development Environments in VS Code</title><link>https://vo.rs/story/devcontainers-reproducible-development-environments-in-vs-code/</link><pubDate>Tue, 17 Mar 2026 11:00:00 +0000</pubDate><guid>https://vo.rs/story/devcontainers-reproducible-development-environments-in-vs-code/</guid><description>&lt;p&gt;I have lost enough hours to &amp;ldquo;but it works on my machine&amp;rdquo; that I treat the phrase as a personal insult. The last time it cost me a full afternoon: a teammate cloned a repo, their Node version was a major release behind, a native dependency wouldn&amp;rsquo;t compile because they were missing a system library, and we both vanished into &lt;code&gt;apt-get&lt;/code&gt; archaeology while the actual work sat untouched. Devcontainers fix this by moving the whole development environment into a container described by a file that lives in the repo. Clone, reopen, build, work. That&amp;rsquo;s the promise, and after a couple of years of leaning on them I&amp;rsquo;ll say it mostly delivers — with some sharp edges I&amp;rsquo;ll be honest about.&lt;/p&gt;</description></item><item><title>The Tunnel Home: Reaching Your Homelab from Anywhere with WireGuard</title><link>https://vo.rs/story/the-tunnel-home-wireguard-homelab-access/</link><pubDate>Tue, 17 Mar 2026 08:00:00 +0000</pubDate><guid>https://vo.rs/story/the-tunnel-home-wireguard-homelab-access/</guid><description>&lt;p&gt;Picture the scene: you are on a train, you want to check a service running at home, and your options are to expose that service to the entire internet or to go without. For years the answer to this dilemma was a clunky VPN that took an afternoon to configure and never quite behaved on mobile. WireGuard changes the calculus completely. It is a modern virtual private network so lean that its entire configuration fits in a file you can read at a glance, fast enough that you forget it is there, and secure enough that it has been merged into the Linux kernel itself. This guide builds a WireGuard tunnel from scratch so you can reach your homelab from anywhere without exposing a single service to the open web.&lt;/p&gt;</description></item><item><title>Proxmox 101: Turn One Old PC into a Virtualization Powerhouse</title><link>https://vo.rs/story/proxmox-101-turn-one-old-pc-into-a-virtualization-powerhouse/</link><pubDate>Tue, 10 Mar 2026 09:15:00 +0000</pubDate><guid>https://vo.rs/story/proxmox-101-turn-one-old-pc-into-a-virtualization-powerhouse/</guid><description>&lt;p&gt;That old desktop in the cupboard, the one too slow for modern games but far too capable to bin, is a homelab waiting to happen. With Proxmox VE you can carve a single physical machine into a dozen virtual ones, each isolated, each snapshot-able, each ready to host a different service. Instead of one box doing one job, you get a flexible platform where you can spin up a test server in two minutes and throw it away just as fast. This guide explains what Proxmox is, what hardware it needs, how to install it, and how to plan a sensible first homelab that complements the self-hosted services covered elsewhere on this blog.&lt;/p&gt;</description></item><item><title>Pre-commit Hooks: Catching Mistakes Before They Reach the Repo</title><link>https://vo.rs/story/pre-commit-hooks-catching-mistakes-before-they-reach-the-repo/</link><pubDate>Mon, 09 Mar 2026 14:00:00 +0000</pubDate><guid>https://vo.rs/story/pre-commit-hooks-catching-mistakes-before-they-reach-the-repo/</guid><description>&lt;p&gt;There is a particular flavour of shame that comes from pushing a commit, watching CI light up red ninety seconds later, and discovering the failure was a trailing-whitespace lint error or a file you forgot to format. Worse is the commit that ships a cloud access key in a &lt;code&gt;.env&lt;/code&gt; you meant to gitignore, now etched into history forever. I have done both. The whitespace one wastes ten minutes; the leaked-key one cost me an afternoon of rotating credentials and grovelling in a channel. Pre-commit hooks catch all of it at the only moment it is cheap to catch: before the commit exists. Git has always supported hooks; the &lt;code&gt;pre-commit&lt;/code&gt; framework just makes them sane to manage.&lt;/p&gt;</description></item><item><title>Ditch Plex: Building a Bulletproof Jellyfin Media Server on Linux</title><link>https://vo.rs/story/ditch-plex-bulletproof-jellyfin-media-server-on-linux/</link><pubDate>Tue, 03 Mar 2026 10:00:00 +0000</pubDate><guid>https://vo.rs/story/ditch-plex-bulletproof-jellyfin-media-server-on-linux/</guid><description>&lt;p&gt;There was a golden age when Plex felt like magic: you pointed it at a folder of films, and suddenly your living room television displayed glossy posters, plot summaries, and trailers as if you ran a private streaming service. For a steady stream of long-time users that magic has slowly curdled into nagging account prompts, paywalled features, and the creeping sense that the software guarding your own files now answers to someone else. If you have started eyeing the monthly subscription column of your bank statement with suspicion, this guide is for you. We will build a media server that is genuinely yours, runs on a modest Linux box, transcodes video efficiently, and never once asks you to log in to a remote service to watch the films sitting on your own hard drive.&lt;/p&gt;</description></item><item><title>Minio: S3-Compatible Object Storage in Your Cluster</title><link>https://vo.rs/story/minio-s3-compatible-object-storage-in-your-cluster/</link><pubDate>Tue, 03 Mar 2026 07:00:00 +0000</pubDate><guid>https://vo.rs/story/minio-s3-compatible-object-storage-in-your-cluster/</guid><description>&lt;p&gt;Half the software I self-host now expects an S3 bucket to throw things into. Backup tools, container registries, CI artifact stores, Loki for logs, photo libraries — they&amp;rsquo;ve all standardised on the S3 API as the lingua franca of &amp;ldquo;somewhere to put blobs&amp;rdquo;. You can hand all of them an AWS account and a credit card, or you can run Minio and have a real S3-compatible endpoint inside your own cluster, on your own disks, with no egress bills and no surprise invoices. I&amp;rsquo;ve run it for years and it has quietly become load-bearing infrastructure.&lt;/p&gt;</description></item><item><title>Taskfile: A Modern Task Runner That Replaces Make Without the Pain</title><link>https://vo.rs/story/taskfile-a-modern-task-runner-that-replaces-make-without-the-pain/</link><pubDate>Sat, 28 Feb 2026 09:00:00 +0000</pubDate><guid>https://vo.rs/story/taskfile-a-modern-task-runner-that-replaces-make-without-the-pain/</guid><description>&lt;p&gt;I have a long and complicated relationship with &lt;code&gt;make&lt;/code&gt;. It is everywhere, it is reliable, and it has been quietly running the world&amp;rsquo;s builds for nearly fifty years. It also punishes you with significant whitespace, a recipe language that is really shell-inside-make-inside-shell with three layers of escaping, and the eternal indignity of &lt;code&gt;Makefile:12: *** missing separator. Stop.&lt;/code&gt; because you typed spaces where a literal tab was required. For most of what people actually use Make for these days — not building C, but running a handful of project commands — Taskfile is the replacement I wish I&amp;rsquo;d switched to years ago.&lt;/p&gt;</description></item><item><title>Snowflake and Tor: What Censorship Circumvention Looks Like in 2026</title><link>https://vo.rs/story/snowflake-and-tor-what-censorship-circumvention-looks-like-in-2026/</link><pubDate>Tue, 24 Feb 2026 09:00:00 +0000</pubDate><guid>https://vo.rs/story/snowflake-and-tor-what-censorship-circumvention-looks-like-in-2026/</guid><description>&lt;p&gt;Censorship circumvention used to be a simple arms race: you&amp;rsquo;d point your traffic at a proxy, the censor would block the proxy&amp;rsquo;s address, you&amp;rsquo;d find another. By 2026 the censor has graduated. Modern national firewalls don&amp;rsquo;t just block known addresses — they fingerprint the &lt;em&gt;shape&lt;/em&gt; of your traffic, and they&amp;rsquo;ll happily throttle or drop a connection that merely &lt;em&gt;looks&lt;/em&gt; like Tor even if they can&amp;rsquo;t read it. The interesting story now is how Tor has evolved from &amp;ldquo;hide the destination&amp;rdquo; to &amp;ldquo;don&amp;rsquo;t even look like Tor in the first place&amp;rdquo;, and Snowflake is the most elegant piece of that puzzle.&lt;/p&gt;</description></item><item><title>Docker Compose Demystified: A Full Stack in a Single File</title><link>https://vo.rs/story/docker-compose-demystified-a-full-stack-in-a-single-file/</link><pubDate>Fri, 20 Feb 2026 09:00:00 +0000</pubDate><guid>https://vo.rs/story/docker-compose-demystified-a-full-stack-in-a-single-file/</guid><description>&lt;p&gt;The fastest way to lose a weekend is to stand up a self-hosted app by hand, get
it working at 2am, and then need to rebuild it six months later from memory.
I&amp;rsquo;ve done it. The container&amp;rsquo;s exact flags — the volume path, the network name,
the three environment variables it absolutely needed — lived only in my shell
history and the increasingly unreliable part of my brain that thinks it
remembers things. When the SD card died, so did all of it.&lt;/p&gt;</description></item><item><title>Act: Running GitHub Actions Locally Before You Push</title><link>https://vo.rs/story/act-running-github-actions-locally-before-you-push/</link><pubDate>Fri, 13 Feb 2026 08:00:00 +0000</pubDate><guid>https://vo.rs/story/act-running-github-actions-locally-before-you-push/</guid><description>&lt;p&gt;We have all done the thing. You tweak a workflow file, you can&amp;rsquo;t run it locally, so you push a commit called &amp;ldquo;fix ci&amp;rdquo;, watch it fail, push &amp;ldquo;fix ci 2&amp;rdquo;, fail, &amp;ldquo;actually fix ci&amp;rdquo;, fail, and by the time it&amp;rsquo;s green your git history reads like a cry for help. The feedback loop on GitHub Actions is dreadful precisely because the only way to test it is to use the production system itself, slowly, in public. &lt;code&gt;act&lt;/code&gt; fixes that by running your workflows locally in Docker.&lt;/p&gt;</description></item><item><title>Renovate Bot: Automated Dependency Updates That Don't Break Everything</title><link>https://vo.rs/story/renovate-bot-automated-dependency-updates-that-dont-break-everything/</link><pubDate>Sat, 07 Feb 2026 10:00:00 +0000</pubDate><guid>https://vo.rs/story/renovate-bot-automated-dependency-updates-that-dont-break-everything/</guid><description>&lt;p&gt;Last spring I opened a side project I hadn&amp;rsquo;t touched in eighteen months and ran &lt;code&gt;npm install&lt;/code&gt;. The lockfile resolved to 340 packages, 61 of which had known advisories, and the top-level framework was four major versions behind. Bumping it meant chasing breaking changes through a dependency tree I&amp;rsquo;d forgotten the shape of. I lost most of a Saturday, and the whole mess existed for one reason: I&amp;rsquo;d let the updates pile up until they became a project instead of a chore.&lt;/p&gt;</description></item><item><title>Neovim in 2026: A Practical Setup for People Who Also Have Work to Do</title><link>https://vo.rs/story/neovim-in-2026-a-practical-setup-for-people-who-also-have-work-to-do/</link><pubDate>Tue, 20 Jan 2026 09:00:00 +0000</pubDate><guid>https://vo.rs/story/neovim-in-2026-a-practical-setup-for-people-who-also-have-work-to-do/</guid><description>&lt;p&gt;Neovim has a reputation, and it isn&amp;rsquo;t entirely undeserved: the editor you spend a weekend configuring instead of using. I&amp;rsquo;ve lost those weekends. I&amp;rsquo;ve stayed up until 2am chasing a plugin conflict that turned out to be a load-order problem, and I&amp;rsquo;ve rebuilt my config from scratch more times than I&amp;rsquo;ll admit in writing. I&amp;rsquo;ve also come out the other side with an editor I genuinely prefer to anything else — and the good news, the whole reason for this post, is that in 2026 you no longer have to pay that tax. The ecosystem has matured into a small set of plugins that do the heavy lifting, and a sensible config is now a few hundred lines of readable Lua rather than a haunted 3,000-line &lt;code&gt;init.vim&lt;/code&gt; inherited from a stranger&amp;rsquo;s dotfiles. This is the setup I&amp;rsquo;d hand someone who wants a capable editor and also, crucially, has actual work to do this week.&lt;/p&gt;</description></item><item><title>OCR Pipelines: Tesseract, PaddleOCR, and When to Use Which</title><link>https://vo.rs/story/ocr-pipelines-tesseract-paddleocr-and-when-to-use-which/</link><pubDate>Mon, 05 Jan 2026 11:00:00 +0000</pubDate><guid>https://vo.rs/story/ocr-pipelines-tesseract-paddleocr-and-when-to-use-which/</guid><description>&lt;p&gt;I have a filing cabinet&amp;rsquo;s worth of scanned documents — receipts, appliance manuals, the occasional important letter from an institution that still believes firmly in paper — and a stubborn refusal to feed them through a cloud OCR service that charges per page and keeps a copy of everything for its own purposes. So I run optical character recognition locally, on my own hardware, and I&amp;rsquo;ve done it long enough to have opinions. The good news is that self-hosted OCR has quietly become genuinely excellent, good enough that &amp;ldquo;just use a cloud API&amp;rdquo; is no longer the obvious answer it was five years ago. The bad news is that there are two serious contenders, they&amp;rsquo;re good at &lt;em&gt;different&lt;/em&gt; things, and the internet is full of people insisting their favourite is universally best. It isn&amp;rsquo;t, in either direction. Let me save you the afternoon I lost finding that out the hard way.&lt;/p&gt;</description></item><item><title>mTLS: Mutual TLS Between Services Without a Service Mesh</title><link>https://vo.rs/story/mtls-mutual-tls-between-services-without-a-service-mesh/</link><pubDate>Thu, 11 Dec 2025 16:00:00 +0000</pubDate><guid>https://vo.rs/story/mtls-mutual-tls-between-services-without-a-service-mesh/</guid><description>&lt;p&gt;The incident that made me care about mutual TLS was embarrassingly mundane. A metrics scraper on one of my hosts had been quietly hitting an internal API for months. When I finally audited what could actually &lt;em&gt;reach&lt;/em&gt; that API, the answer was: anything on the same network segment. The service checked no identity at all. It answered a request the way a shop assistant answers a stranger — politely, completely, and with no idea who it was talking to. Nothing had gone wrong yet. But &amp;ldquo;nothing has gone wrong yet&amp;rdquo; is not a security posture, it&amp;rsquo;s a countdown.&lt;/p&gt;</description></item><item><title>DNS Sinkholing: Blocking Malware Domains at the Network Level</title><link>https://vo.rs/story/dns-sinkholing-blocking-malware-domains-at-the-network-level/</link><pubDate>Sun, 09 Nov 2025 16:00:00 +0000</pubDate><guid>https://vo.rs/story/dns-sinkholing-blocking-malware-domains-at-the-network-level/</guid><description>&lt;p&gt;A few years ago I watched a brand-new &amp;ldquo;smart&amp;rdquo; plug on my network try to reach
roughly forty different domains in its first hour of life — telemetry endpoints,
a couple of ad networks, and one domain that a threat feed flagged as a known
command-and-control host. I hadn&amp;rsquo;t installed anything on it; I couldn&amp;rsquo;t. It&amp;rsquo;s a
sealed appliance with no SSH, no agent, no way in. The only thing standing
between that plug and whatever it was trying to phone was the resolver it asked
for an address. That resolver said &amp;ldquo;no&amp;rdquo;, and the connection died in the device&amp;rsquo;s
own network stack before a single packet left my house.&lt;/p&gt;</description></item><item><title>Sigstore and Cosign: Verifying Container Images Before They Run</title><link>https://vo.rs/story/sigstore-and-cosign-verifying-container-images-before-they-run/</link><pubDate>Fri, 17 Oct 2025 14:00:00 +0000</pubDate><guid>https://vo.rs/story/sigstore-and-cosign-verifying-container-images-before-they-run/</guid><description>&lt;p&gt;A container tag is a lie you&amp;rsquo;ve agreed to believe. &lt;code&gt;nginx:latest&lt;/code&gt; today and
&lt;code&gt;nginx:latest&lt;/code&gt; next week can be entirely different bytes, and a tag tells you
nothing about who built the image or whether it&amp;rsquo;s been swapped underneath you.
The whole modern supply-chain panic — compromised build pipelines, typosquatted
images, dependency confusion — comes down to that one weak link: we run images we
can&amp;rsquo;t actually verify. Sigstore, and its CLI &lt;code&gt;cosign&lt;/code&gt;, is the most practical fix
I&amp;rsquo;ve adopted, mostly because it finally killed the part of signing that everyone
hated: key management.&lt;/p&gt;</description></item><item><title>Kubernetes Resource Requests and Limits: The Difference Between Crashing and Thriving</title><link>https://vo.rs/story/kubernetes-resource-requests-and-limits-the-difference-between-crashing-and-thriving/</link><pubDate>Mon, 08 Sep 2025 10:00:00 +0000</pubDate><guid>https://vo.rs/story/kubernetes-resource-requests-and-limits-the-difference-between-crashing-and-thriving/</guid><description>&lt;p&gt;I once spent the better part of a Saturday chasing a pod that vanished every few
hours on my home cluster. No crash logs, no panic, nothing in the application
output — the container simply wasn&amp;rsquo;t there when I looked, and its restart count
kept ticking up. The &lt;code&gt;describe&lt;/code&gt; output eventually gave it away: &lt;code&gt;OOMKilled&lt;/code&gt;,
exit code 137. The pod had a 256Mi memory limit copied from a blog post, and a
nightly job inside it briefly allocated 400Mi. The kernel did exactly what it was
told and shot the process. Nothing was flaky. I had told Kubernetes to kill it.&lt;/p&gt;</description></item><item><title>Distroless Container Images: Smaller, Safer, and More Annoying to Debug</title><link>https://vo.rs/story/distroless-container-images-smaller-safer-and-more-annoying-to-debug/</link><pubDate>Thu, 21 Aug 2025 11:00:00 +0000</pubDate><guid>https://vo.rs/story/distroless-container-images-smaller-safer-and-more-annoying-to-debug/</guid><description>&lt;p&gt;The first time you run a vulnerability scanner against a &lt;code&gt;node:20&lt;/code&gt; image and it reports two hundred CVEs, you have a small crisis of faith. None of those flaws are in &lt;em&gt;your&lt;/em&gt; code. They&amp;rsquo;re in &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;coreutils&lt;/code&gt;, the Debian base — an entire operating system you shipped just to run one process. Distroless images are the reaction to that absurdity: a container image containing your application, its runtime dependencies, and &lt;em&gt;nothing else&lt;/em&gt;. No shell. No package manager. No &lt;code&gt;ls&lt;/code&gt;. The CVE count drops because the attack surface drops, and that&amp;rsquo;s not a coincidence — it&amp;rsquo;s the whole point.&lt;/p&gt;</description></item><item><title>ArgoCD: GitOps with a Dashboard You Might Actually Use</title><link>https://vo.rs/story/argocd-gitops-with-a-dashboard-you-might-actually-use/</link><pubDate>Mon, 11 Aug 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/argocd-gitops-with-a-dashboard-you-might-actually-use/</guid><description>&lt;p&gt;I came to GitOps grudgingly. The pitch — &amp;ldquo;Git is the single source of truth, the cluster reconciles itself to match&amp;rdquo; — sounded like the kind of slogan that survives precisely one production incident. Then I tried to debug a drifting Kubernetes cluster the old way, &lt;code&gt;kubectl apply&lt;/code&gt;-ing manifests by hand and trying to remember which of three engineers had hotfixed what, and I came round. ArgoCD is the tool that converted me, mostly because of one thing the others don&amp;rsquo;t do nearly as well: it &lt;em&gt;shows&lt;/em&gt; you the state of the world.&lt;/p&gt;</description></item><item><title>Container Networking Debugging: tcpdump, nsenter, and What Packets Are Actually Doing</title><link>https://vo.rs/story/container-networking-debugging-tcpdump-nsenter-and-what-packets-are-actually-doing/</link><pubDate>Thu, 17 Jul 2025 16:00:00 +0000</pubDate><guid>https://vo.rs/story/container-networking-debugging-tcpdump-nsenter-and-what-packets-are-actually-doing/</guid><description>&lt;p&gt;I once spent the better part of an evening convinced a node in my home lab had a broken bridge. An app container kept logging &lt;code&gt;connection refused&lt;/code&gt; against its database, the database was clearly up, and I&amp;rsquo;d restarted both more times than I&amp;rsquo;d like to admit. Three layers of NAT, a Docker bridge and a stack of iptables rules all insisted they were innocent, and I had no way to call their bluff — because the app image was a stripped-down distroless thing with no &lt;code&gt;ping&lt;/code&gt;, no &lt;code&gt;curl&lt;/code&gt;, no &lt;code&gt;tcpdump&lt;/code&gt;, nothing you&amp;rsquo;d actually use to look. So I guessed. Eventually it &amp;ldquo;fixed itself&amp;rdquo; after a redeploy, which is the worst possible outcome: the problem was gone and I still didn&amp;rsquo;t know what it had been.&lt;/p&gt;</description></item><item><title>Dagger: CI/CD Pipelines as Code That Run Anywhere</title><link>https://vo.rs/story/dagger-ci-cd-pipelines-as-code-that-run-anywhere/</link><pubDate>Mon, 07 Jul 2025 11:00:00 +0000</pubDate><guid>https://vo.rs/story/dagger-ci-cd-pipelines-as-code-that-run-anywhere/</guid><description>&lt;p&gt;The single most demoralising thing about CI is the feedback loop. You edit a YAML file, push, wait three minutes for a runner to spin up, watch it fail on a typo, fix the typo, push again. Repeat until either the pipeline goes green or you go home. I once burned an entire afternoon — eleven pushes, I counted — chasing a pipeline failure that turned out to be a misindented &lt;code&gt;with:&lt;/code&gt; block, because the &lt;em&gt;only&lt;/em&gt; way to run the pipeline was to push and pray. The pipeline existed only inside the CI provider, so the provider was the only thing that could execute it. Dagger&amp;rsquo;s whole pitch is that this is daft, and they&amp;rsquo;re right.&lt;/p&gt;</description></item><item><title>Voice Assistants Without the Cloud: Whisper, Piper, and Home Assistant</title><link>https://vo.rs/story/voice-assistants-without-the-cloud-whisper-piper-and-home-assistant/</link><pubDate>Sun, 22 Jun 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/voice-assistants-without-the-cloud-whisper-piper-and-home-assistant/</guid><description>&lt;p&gt;I have always found something faintly insulting about commercial smart speakers. You buy a microphone, put it on your kitchen counter, and then pay a subscription to a company that would dearly love to know what brand of coffee you argue about at breakfast. The convenience is real, but the bargain is rotten. So a couple of years ago I tore the cloud out of my voice setup entirely, and the technology has matured to the point where I can finally recommend it without a disclaimer the length of a mortgage agreement.&lt;/p&gt;</description></item><item><title>Beyond VPNs: Leveraging Secure Access Service Edge (SASE) for Remote Workforces</title><link>https://vo.rs/story/beyond-vpns-leveraging-secure-access-service-edge-sase-for-remote-workforces/</link><pubDate>Sat, 17 May 2025 17:25:00 +0000</pubDate><guid>https://vo.rs/story/beyond-vpns-leveraging-secure-access-service-edge-sase-for-remote-workforces/</guid><description>&lt;p&gt;The clearest sign your remote-access VPN has outgrown its design is the tromboning. A salesperson in the same city as your cloud provider opens their laptop, connects to a SaaS app that lives twenty miles away, and their traffic first flies several hundred miles to the corporate VPN concentrator, gets inspected, then flies several hundred miles back out to the cloud. Round trip: a thousand miles to reach something next door. Everyone blames &amp;ldquo;the internet being slow.&amp;rdquo; The internet is fine. The architecture is the problem.&lt;/p&gt;</description></item><item><title>Digital Sovereignty in the EU: Navigating Schrems II, GDPR, and Local Hosting Mandates</title><link>https://vo.rs/story/digital-sovereignty-in-the-eu-navigating-schrems-ii-gdpr-and-local-hosting-mandates/</link><pubDate>Thu, 08 May 2025 14:50:00 +0000</pubDate><guid>https://vo.rs/story/digital-sovereignty-in-the-eu-navigating-schrems-ii-gdpr-and-local-hosting-mandates/</guid><description>&lt;p&gt;On 16 July 2020 the Court of Justice of the European Union threw out the EU-US Privacy Shield, and an awful lot of European companies discovered overnight that the legal basis for storing their users&amp;rsquo; data in an American cloud had evaporated. The case is known as Schrems II, after the Austrian lawyer Maximilian Schrems who spent the better part of a decade dragging Facebook&amp;rsquo;s data transfers through the courts. The ruling did not say &amp;ldquo;you may never send data to the US.&amp;rdquo; It said something more awkward: that you, the data exporter, are now personally on the hook for proving that the country you ship data to protects it to an EU standard — and that for the US specifically, given what the NSA was doing, that protection was inadequate.&lt;/p&gt;</description></item><item><title>Automated Chaos: Using Fault Injection to Build Resilience Before Your Users Notice</title><link>https://vo.rs/story/automated-chaos-using-fault-injection-to-build-resilience-before-your-users-notice/</link><pubDate>Mon, 28 Apr 2025 10:40:00 +0000</pubDate><guid>https://vo.rs/story/automated-chaos-using-fault-injection-to-build-resilience-before-your-users-notice/</guid><description>&lt;p&gt;The first time a single dead Redis container took down my entire homelab dashboard stack, it was 11pm on a Sunday and I had no idea why. Grafana was throwing 502s, the reverse proxy was timing out, and the actual fault — one cache container that had OOM-killed itself — was three layers down from anything I was looking at. I spent forty minutes chasing the symptom before I found the cause. The annoying part is that I could have found that fragility on a Tuesday afternoon, in daylight, with a coffee, if I had simply killed that container on purpose and watched what happened.&lt;/p&gt;</description></item><item><title>Edge Computing vs. Cloud: Choosing the Right Architecture for Mission-Critical IoT</title><link>https://vo.rs/story/edge-computing-vs-cloud-choosing-the-right-architecture-for-mission-critical-iot/</link><pubDate>Fri, 18 Apr 2025 12:10:00 +0000</pubDate><guid>https://vo.rs/story/edge-computing-vs-cloud-choosing-the-right-architecture-for-mission-critical-iot/</guid><description>&lt;p&gt;The first time I sent every sensor reading from a home-lab project straight to a cloud endpoint, the architecture looked clean on a whiteboard and fell apart in a week. The broadband had a thirty-second wobble one evening, a dozen sensors queued their readings, the reconnect storm hammered the endpoint, and an automation that was supposed to cut power to a load when a temperature crossed a threshold simply didn&amp;rsquo;t fire — because the decision lived in the cloud, and the cloud wasn&amp;rsquo;t reachable. Nobody got hurt; it was a hobby rig. But it taught me the single most important question in IoT architecture, which has nothing to do with which cloud you like: &lt;strong&gt;where does the decision get made, and what happens to that decision when the network is gone?&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Beszel: Lightweight Server Monitoring Without the Grafana Overhead</title><link>https://vo.rs/story/beszel-lightweight-server-monitoring-without-the-grafana-overhead/</link><pubDate>Tue, 08 Apr 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/beszel-lightweight-server-monitoring-without-the-grafana-overhead/</guid><description>&lt;p&gt;I will defend the Grafana-and-Prometheus stack to anyone who&amp;rsquo;ll listen. But I have to be honest about something: it is a &lt;em&gt;lot&lt;/em&gt; of machinery to answer the question &amp;ldquo;is my little VPS okay?&amp;rdquo; For a serious homelab with a dozen nodes and real alerting needs, the weight is justified. For three boxes and a Raspberry Pi, standing up Prometheus, exporters, Grafana, and a pile of YAML to find out whether a disk is filling up feels like hiring a structural engineer to hang a picture.&lt;/p&gt;</description></item><item><title>Why Your Kubernetes Cluster Crashes at 2 a.m. and How to Stop It</title><link>https://vo.rs/story/why-your-kubernetes-cluster-crashes-at-2-am-and-how-to-stop-it/</link><pubDate>Wed, 02 Apr 2025 13:00:00 +0000</pubDate><guid>https://vo.rs/story/why-your-kubernetes-cluster-crashes-at-2-am-and-how-to-stop-it/</guid><description>&lt;p&gt;The first time my home cluster fell over at 02:14, I assumed it was a fluke. The second time, three nights later, at almost exactly the same hour, I stopped believing in coincidence and started believing in cron. Because that is what a 2 a.m. crash almost always is: not random cosmic bad luck, but a scheduled job — a backup, a log rotation, a certificate renewal, a &lt;code&gt;docker image prune&lt;/code&gt; you set up and forgot — colliding with a cluster that has no headroom to absorb it. The outage feels mysterious because you are asleep and the trigger fires on a timer you no longer remember writing.&lt;/p&gt;</description></item><item><title>Stalwart: Self-Hosting Your Own Email Server (and Why You Probably Shouldn't)</title><link>https://vo.rs/story/stalwart-self-hosting-your-own-email-server/</link><pubDate>Tue, 04 Mar 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/stalwart-self-hosting-your-own-email-server/</guid><description>&lt;p&gt;Every couple of years I get the itch to run my own mail server. I quash it, because every couple of years I remember what running a mail server is actually like: a Postfix config you don&amp;rsquo;t dare touch, a Dovecot setup nobody documented, Rspamd bolted on the side, and a Sunday afternoon vanished into SASL authentication errors. And then, the first time you email someone at a big provider, you land in spam anyway.&lt;/p&gt;</description></item><item><title>IPv6 at Home: Why It Matters Now and How to Stop Ignoring It</title><link>https://vo.rs/story/ipv6-at-home-why-it-matters-now-and-how-to-stop-ignoring-it/</link><pubDate>Tue, 11 Feb 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/ipv6-at-home-why-it-matters-now-and-how-to-stop-ignoring-it/</guid><description>&lt;p&gt;Most of us have spent two decades treating IPv6 as someone else&amp;rsquo;s problem — a thing that exists, that we&amp;rsquo;ll get round to, that the internet seems to run fine without. Meanwhile it quietly crept onto your network anyway. Your phone is probably using it right now. A large slice of traffic to Google, Netflix and the big CDNs is already IPv6 over your home connection, and you never lifted a finger. The question stopped being &amp;ldquo;should I enable IPv6&amp;rdquo; some time ago. It&amp;rsquo;s now &amp;ldquo;should I keep pretending the IPv6 that&amp;rsquo;s already running is something I understand.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Building in Public: What Running a Blog on Your Own Infrastructure Teaches You</title><link>https://vo.rs/story/building-in-public-what-running-a-blog-on-your-own-infrastructure-teaches-you/</link><pubDate>Wed, 05 Feb 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/building-in-public-what-running-a-blog-on-your-own-infrastructure-teaches-you/</guid><description>&lt;p&gt;It is one thing to read about infrastructure. It is another to be woken by an email — or worse, a stranger on the internet politely telling you your site is down — and realise that the thing that&amp;rsquo;s broken is yours, the page is yours, and the only person who&amp;rsquo;s going to fix it is also, regrettably, yours. Running a blog on your own infrastructure is the cheapest, most effective infrastructure course I know, because every lesson arrives attached to genuine embarrassment.&lt;/p&gt;</description></item><item><title>Wazuh: A Self-Hosted SIEM for the Home Lab (Is It Worth the RAM?)</title><link>https://vo.rs/story/wazuh-a-self-hosted-siem-for-the-home-lab-is-it-worth-the-ram/</link><pubDate>Tue, 28 Jan 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/wazuh-a-self-hosted-siem-for-the-home-lab-is-it-worth-the-ram/</guid><description>&lt;p&gt;A SIEM — Security Information and Event Management — is the thing big companies pay six figures a year for so that a roomful of analysts can stare at dashboards and catch the moment something goes wrong. The pitch is simple: pull logs from everything you own into one place, correlate them, and raise an alert when the pattern smells like an attack. The reality, for most of those companies, is a very expensive log bucket nobody reads.&lt;/p&gt;</description></item><item><title>Tandoor: When Your Recipe Collection Outgrows Browser Bookmarks</title><link>https://vo.rs/story/tandoor-when-your-recipe-collection-outgrows-bookmarks/</link><pubDate>Wed, 15 Jan 2025 09:00:00 +0000</pubDate><guid>https://vo.rs/story/tandoor-when-your-recipe-collection-outgrows-bookmarks/</guid><description>&lt;p&gt;My recipe &amp;ldquo;system&amp;rdquo; used to be a browser folder with 240-odd bookmarks, a Notes app full of half-typed ingredient lists, and a recurring Sunday-evening ritual of squinting at my phone in the supermarket trying to remember whether I needed one tin of chickpeas or three. It worked, in the sense that a shopping trolley with a wonky wheel works. It got me there, mostly, with some swearing.&lt;/p&gt;
&lt;p&gt;Tandoor is what I replaced all of that with, and a year in I&amp;rsquo;m not going back.&lt;/p&gt;</description></item><item><title>Kubernetes Secrets Management: SOPS, Sealed Secrets, or External Secrets</title><link>https://vo.rs/story/kubernetes-secrets-management-sops-sealed-secrets-or-external-secrets/</link><pubDate>Thu, 19 Dec 2024 10:00:00 +0000</pubDate><guid>https://vo.rs/story/kubernetes-secrets-management-sops-sealed-secrets-or-external-secrets/</guid><description>&lt;p&gt;Kubernetes Secrets are not secret. That&amp;rsquo;s the first thing nobody tells you. A &lt;code&gt;Secret&lt;/code&gt; object is base64-encoded YAML sitting in etcd, and base64 is encoding, not encryption — anyone with &lt;code&gt;get secrets&lt;/code&gt; on the namespace can read it back in plaintext with a single command. If you doubt it, &lt;code&gt;kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d&lt;/code&gt; will happily print your database password to the terminal of anyone with the right RBAC. Encryption at rest in etcd helps a little, but the real problem turns up the moment you adopt GitOps: now you want everything in a repo, and committing a base64 blob of your database password to Git is the kind of decision that ends careers. It doesn&amp;rsquo;t even need a breach — a public mirror, a fork, a laptop backup that syncs to someone&amp;rsquo;s personal cloud, and the credential is loose forever, because Git never forgets. So you reach for tooling. There are three serious contenders, and I&amp;rsquo;ve run all of them in anger on my own cluster.&lt;/p&gt;</description></item><item><title>The SaaS Trap: How Per-Seat Pricing Pushes You Toward Self-Hosting</title><link>https://vo.rs/story/the-saas-trap-how-per-seat-pricing-pushes-you-toward-self-hosting/</link><pubDate>Tue, 03 Dec 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/the-saas-trap-how-per-seat-pricing-pushes-you-toward-self-hosting/</guid><description>&lt;p&gt;There is a particular moment, familiar to anyone who has run a small team, where you log into a tool you&amp;rsquo;ve happily paid for for years, go to add a new colleague, and watch the price quietly leap by another tenner a month. Do it five times and the friendly little subscription has become a line on the budget that someone, eventually, is going to question. That moment is when otherwise sensible people start googling &amp;ldquo;self-hosted alternative to&amp;rdquo; — and per-seat pricing is the reason.&lt;/p&gt;</description></item><item><title>Linkwarden: Self-Hosted Bookmarking for the Tab Hoarder</title><link>https://vo.rs/story/linkwarden-self-hosted-bookmarking-for-the-tab-hoarder/</link><pubDate>Tue, 26 Nov 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/linkwarden-self-hosted-bookmarking-for-the-tab-hoarder/</guid><description>&lt;p&gt;I have a confession that will surprise nobody who has ever borrowed my laptop: my browser at any given moment is holding somewhere north of two hundred open tabs. Each one is a promise to myself — &amp;ldquo;I&amp;rsquo;ll read this later&amp;rdquo; — and each one is a lie. Worse, the tabs I &lt;em&gt;do&lt;/em&gt; eventually bookmark have a nasty habit of rotting. I click through six months later and find a 404, a parked domain, or a &amp;ldquo;this article has been removed&amp;rdquo; notice. The thing I wanted to keep is gone, and all my bookmark preserved was the gravestone.&lt;/p&gt;</description></item><item><title>Kaniko: Building Container Images Inside Kubernetes</title><link>https://vo.rs/story/kaniko-building-container-images-inside-kubernetes/</link><pubDate>Thu, 21 Nov 2024 14:00:00 +0000</pubDate><guid>https://vo.rs/story/kaniko-building-container-images-inside-kubernetes/</guid><description>&lt;p&gt;There is an awkward chicken-and-egg problem at the heart of running CI inside Kubernetes: you want to build container images, but the traditional way to build a container image is to run &lt;code&gt;docker build&lt;/code&gt;, and &lt;code&gt;docker build&lt;/code&gt; needs a Docker daemon, and a Docker daemon needs root and a bunch of kernel features that you really, really should not be handing to a CI pod. The old hack — mounting the host&amp;rsquo;s Docker socket into the build pod — is the security equivalent of leaving your front door open because the lock is fiddly. Anything that can talk to that socket effectively owns the node: it can launch a privileged container, mount the host filesystem, and walk straight out of your cluster&amp;rsquo;s security boundary. That&amp;rsquo;s not a theoretical risk; it&amp;rsquo;s a standard step in real cluster-escape write-ups.&lt;/p&gt;</description></item><item><title>Proxmox vs Kubernetes for the Home Lab: Pick the Right Tool for the Mess</title><link>https://vo.rs/story/proxmox-vs-kubernetes-for-the-home-lab-pick-the-right-tool-for-the-mess/</link><pubDate>Tue, 19 Nov 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/proxmox-vs-kubernetes-for-the-home-lab-pick-the-right-tool-for-the-mess/</guid><description>&lt;p&gt;&amp;ldquo;Should I run Proxmox or Kubernetes in my home lab?&amp;rdquo; is one of those questions that&amp;rsquo;s wrong in a subtle, expensive way. It sounds like a versus, and the internet loves a versus, so people pick a side and defend it with the energy of a football derby. But the two tools answer different questions. One asks &amp;ldquo;how do I carve this physical machine into several virtual ones?&amp;rdquo; The other asks &amp;ldquo;how do I run my workloads across machines and keep them running when one dies?&amp;rdquo; You can — and many of us do — run the second on top of the first.&lt;/p&gt;</description></item><item><title>Supply Chain Attacks: From npm Typosquatting to Poisoned Container Images</title><link>https://vo.rs/story/supply-chain-attacks-from-npm-typosquatting-to-poisoned-container-images/</link><pubDate>Tue, 12 Nov 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/supply-chain-attacks-from-npm-typosquatting-to-poisoned-container-images/</guid><description>&lt;p&gt;The most effective way to attack a company isn&amp;rsquo;t to break down its front door. It&amp;rsquo;s to stand inside the delivery van the company waves through the gate every morning. Supply chain attacks work because they exploit the one thing every developer does without thinking: pull in code and images they didn&amp;rsquo;t write, from people they&amp;rsquo;ve never met, and run them with full trust.&lt;/p&gt;
&lt;p&gt;You did it an hour ago. &lt;code&gt;npm install&lt;/code&gt;. &lt;code&gt;docker pull&lt;/code&gt;. &lt;code&gt;pip install&lt;/code&gt;. Each one is an act of faith that the thing at the other end is what it claims to be and hasn&amp;rsquo;t been tampered with since you last looked. Mostly that faith is rewarded. The supply chain attacker&amp;rsquo;s entire business is the times it isn&amp;rsquo;t — and those times are getting more frequent. The 2024 backdoor slipped into &lt;code&gt;xz-utils&lt;/code&gt; (CVE-2024-3094) nearly made it into every mainstream Linux distribution, planted patiently over years by an attacker who social-engineered their way into becoming a co-maintainer. That is the shape of the modern threat: not a smash-and-grab, but a slow poisoning of the things you trust by default.&lt;/p&gt;</description></item><item><title>Keel: Automated Kubernetes Deployments Without GitOps Overhead</title><link>https://vo.rs/story/keel-automated-kubernetes-deployments-without-gitops-overhead/</link><pubDate>Thu, 24 Oct 2024 10:00:00 +0000</pubDate><guid>https://vo.rs/story/keel-automated-kubernetes-deployments-without-gitops-overhead/</guid><description>&lt;p&gt;The orthodox answer to &amp;ldquo;how should my Kubernetes cluster deploy updates&amp;rdquo; is GitOps: Argo CD or Flux watching a Git repository, reconciling the cluster to match declared state, the whole pull-based pipeline. It is genuinely the right answer for a team shipping a product. For a homelab cluster running Sonarr, a couple of dashboards, and the odd hobby project, it is also a small mountain of YAML, a controller to babysit, and a Git workflow you have to actually follow. Sometimes you just want third-party images to update themselves when a new tag lands, without standing up a GitOps engine to do it.&lt;/p&gt;</description></item><item><title>Proton vs Tuta vs Self-Hosted: Email Privacy, Practically Assessed</title><link>https://vo.rs/story/proton-vs-tuta-vs-self-hosted-email-privacy-practically-assessed/</link><pubDate>Tue, 22 Oct 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/proton-vs-tuta-vs-self-hosted-email-privacy-practically-assessed/</guid><description>&lt;p&gt;&amp;ldquo;Private email&amp;rdquo; is one of those phrases that sounds like a single product and is in fact three completely different bargains. You can pay an encrypted provider to hold your mailbox in a way they can&amp;rsquo;t read. You can pay a different encrypted provider with a different idea of how that should work. Or you can host the lot yourself and own the whole problem. I&amp;rsquo;ve run all three at various points, and the right answer depends entirely on which trade-off you can live with — so let&amp;rsquo;s be specific about what each one actually costs you.&lt;/p&gt;</description></item><item><title>Let's Encrypt Rate Limits: What to Do When You Hit the Wall</title><link>https://vo.rs/story/lets-encrypt-rate-limits-what-to-do-when-you-hit-the-wall/</link><pubDate>Wed, 09 Oct 2024 08:00:00 +0000</pubDate><guid>https://vo.rs/story/lets-encrypt-rate-limits-what-to-do-when-you-hit-the-wall/</guid><description>&lt;p&gt;Let&amp;rsquo;s Encrypt is one of the genuinely great things to happen to the open web. Free, automated, universally trusted TLS certificates — the whole reason your homelab services have padlocks now instead of a wall of browser warnings. It is also a free service handling staggering volume, and it protects itself with rate limits. The trouble is you only ever learn those limits exist at the precise moment you slam into one, usually at 2am, usually while debugging something else, usually with a renewal cron job hammering away making everything worse.&lt;/p&gt;</description></item><item><title>Calico vs Cilium: Kubernetes CNI for a Home Cluster</title><link>https://vo.rs/story/calico-vs-cilium-kubernetes-cni-for-a-home-cluster/</link><pubDate>Tue, 08 Oct 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/calico-vs-cilium-kubernetes-cni-for-a-home-cluster/</guid><description>&lt;p&gt;Set up a fresh Kubernetes cluster and one of the first decisions you&amp;rsquo;re handed is also one of the least explained: which CNI? Your nodes sit there in &lt;code&gt;NotReady&lt;/code&gt;, the docs wave vaguely at a list, and you pick whatever the tutorial used. Months later you&amp;rsquo;re trying to write a NetworkPolicy or debug a packet that vanishes, and you finally wish you&amp;rsquo;d understood the choice.&lt;/p&gt;
&lt;p&gt;The Container Network Interface is the plugin that gives every pod an IP and makes pods on different nodes able to talk to each other. Without one, your cluster is a collection of isolated boxes. For self-hosters, two names dominate the conversation: &lt;strong&gt;Calico&lt;/strong&gt; and &lt;strong&gt;Cilium&lt;/strong&gt;. They reach the same destination by very different roads.&lt;/p&gt;</description></item><item><title>Authelia vs Authentik: Choosing a Self-Hosted SSO You Won't Regret</title><link>https://vo.rs/story/authelia-vs-authentik-choosing-a-self-hosted-sso/</link><pubDate>Wed, 18 Sep 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/authelia-vs-authentik-choosing-a-self-hosted-sso/</guid><description>&lt;p&gt;There comes a moment in every homelab&amp;rsquo;s life when you realise you have a dozen logins. Grafana wants a password. So does Sonarr, and Radarr, and that Nextcloud instance, and the thing you spun up at 2am and have already forgotten the credentials for. Each one is its own little island of authentication, half of them reusing the same password because you&amp;rsquo;re only human, and exactly none of them have multi-factor authentication because configuring it twelve times sounded like a Tuesday you&amp;rsquo;d rather not have.&lt;/p&gt;</description></item><item><title>eBPF: The Linux Kernel Feature That's Changing Security (and Attack Surfaces)</title><link>https://vo.rs/story/ebpf-the-linux-kernel-feature-thats-changing-security-and-attack-surfaces/</link><pubDate>Tue, 10 Sep 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/ebpf-the-linux-kernel-feature-thats-changing-security-and-attack-surfaces/</guid><description>&lt;p&gt;For most of Linux&amp;rsquo;s life there was a hard wall between code you wrote and the kernel that ran underneath it. If you wanted the kernel to do something new, you wrote a kernel module — which is to say, you wrote code with the power to crash, corrupt, or backdoor the entire machine, loaded it with your fingers crossed, and hoped. eBPF tore a window in that wall. And like any window, it lets the light in &lt;em&gt;and&lt;/em&gt; gives someone a way to look out.&lt;/p&gt;</description></item><item><title>Just and Task: Modern Alternatives to Make That Don't Make You Cry</title><link>https://vo.rs/story/just-and-task-modern-alternatives-to-make-that-dont-make-you-cry/</link><pubDate>Tue, 03 Sep 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/just-and-task-modern-alternatives-to-make-that-dont-make-you-cry/</guid><description>&lt;p&gt;Almost every project I touch accumulates a pile of little commands: build the thing, run the tests, regenerate the assets, deploy to staging. For decades the reflex answer was a &lt;code&gt;Makefile&lt;/code&gt;, and for decades a &lt;code&gt;Makefile&lt;/code&gt; has been quietly torturing everyone who isn&amp;rsquo;t compiling C. Tab-versus-space errors that print nothing useful. Recipes that fail silently because each line runs in its own shell. Variable expansion that fights you because Make has its own &lt;code&gt;$&lt;/code&gt; and the shell has another. Make is a brilliant build tool wearing a task-runner costume, and the costume doesn&amp;rsquo;t fit. This article is about two tools — &lt;code&gt;just&lt;/code&gt; and &lt;code&gt;Task&lt;/code&gt; — that fit much better.&lt;/p&gt;</description></item><item><title>Kubernetes CronJobs: Scheduled Tasks That Don't Silently Fail</title><link>https://vo.rs/story/kubernetes-cronjobs-scheduled-tasks-that-dont-silently-fail/</link><pubDate>Tue, 27 Aug 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/kubernetes-cronjobs-scheduled-tasks-that-dont-silently-fail/</guid><description>&lt;p&gt;Every cluster ends up with a graveyard of scheduled tasks. Backups, certificate renewals, cache warmers, the nightly script that reconciles a database with some upstream nonsense. On a single box you&amp;rsquo;d reach for &lt;code&gt;crontab -e&lt;/code&gt; and move on with your life. In Kubernetes you reach for a &lt;code&gt;CronJob&lt;/code&gt;, and if you&amp;rsquo;re not careful you reach for a thing that fails quietly at 3am and tells nobody.&lt;/p&gt;
&lt;p&gt;I have lost data to silently failing cron tasks. Not a CronJob specifically — an old-fashioned line in a crontab that had been emailing its errors into the void for eight months, because the mail relay it wrote to had been decommissioned and nobody noticed the bounce. The nightly backup had been failing since roughly the spring; I found out in the autumn, the hard way, when I needed to restore. The lesson stuck. A scheduled task you don&amp;rsquo;t monitor isn&amp;rsquo;t automation, it&amp;rsquo;s a slow-motion incident waiting for a calendar invite. Kubernetes gives you better tooling for this than a plain crontab ever did, but only if you opt in.&lt;/p&gt;</description></item><item><title>VictoriaMetrics: When Prometheus Gets Too Hungry for Your Hardware</title><link>https://vo.rs/story/victoriametrics-when-prometheus-gets-too-hungry-for-your-hardware/</link><pubDate>Tue, 13 Aug 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/victoriametrics-when-prometheus-gets-too-hungry-for-your-hardware/</guid><description>&lt;p&gt;I love Prometheus. I&amp;rsquo;ve said as much before. But there&amp;rsquo;s a moment in the life of a growing homelab where the love turns slightly conditional, and that moment usually arrives when you check &lt;code&gt;htop&lt;/code&gt; and find Prometheus quietly chewing through two gigabytes of RAM to remember some numbers about your fridge thermometer. It&amp;rsquo;s not that Prometheus is badly written — it&amp;rsquo;s that it was designed for ephemeral, short-retention monitoring of large fleets, and it makes memory and disk trade-offs that suit a data centre better than a fanless box in a cupboard.&lt;/p&gt;</description></item><item><title>Technical Debt in a Home Lab: When to Refactor and When to Let It Rot</title><link>https://vo.rs/story/technical-debt-in-a-home-lab-when-to-refactor-and-when-to-let-it-rot/</link><pubDate>Tue, 06 Aug 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/technical-debt-in-a-home-lab-when-to-refactor-and-when-to-let-it-rot/</guid><description>&lt;p&gt;Somewhere in my homelab there is a service running in a container I built by hand in 2021, configured by SSH-ing in and editing files directly, with no Compose file, no documentation, and a startup command that lives only in my shell history. It has not gone down once. I am terrified of it, and I have decided to leave it exactly where it is.&lt;/p&gt;
&lt;p&gt;This is the dirty secret of running infrastructure for fun: technical debt is real here too, but the economics are completely different from a workplace. At work, debt compounds across a team and a roadmap. At home, it compounds across exactly one person — you — and the only deadline is your own patience. That changes the calculus enormously, and it means the professional instinct to &amp;ldquo;do it properly&amp;rdquo; is sometimes the wrong instinct.&lt;/p&gt;</description></item><item><title>CoreDNS and Kubernetes DNS: What Actually Happens When a Pod Looks Up a Name</title><link>https://vo.rs/story/coredns-and-kubernetes-dns-what-actually-happens-when-a-pod-looks-up-a-name/</link><pubDate>Tue, 30 Jul 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/coredns-and-kubernetes-dns-what-actually-happens-when-a-pod-looks-up-a-name/</guid><description>&lt;p&gt;At 1am one Tuesday I watched a pod intermittently fail to reach a service it had been talking to happily for months. &lt;code&gt;nslookup&lt;/code&gt; returned SERVFAIL maybe one query in five. No deploy, no config change, nothing in the obvious logs. It took me two hours and a fair amount of swearing to discover that one of the two CoreDNS replicas had been quietly OOM-killed and rescheduled onto a node a NetworkPolicy was blocking on UDP 53. Every query that load-balanced onto that pod died. I had been treating cluster DNS as a magic black box for years, and the black box had finally bitten me.&lt;/p&gt;</description></item><item><title>mDNS and Avahi: Local Service Discovery That Works Until It Doesn't</title><link>https://vo.rs/story/mdns-and-avahi-local-service-discovery-that-works-until-it-doesnt/</link><pubDate>Wed, 03 Jul 2024 16:00:00 +0000</pubDate><guid>https://vo.rs/story/mdns-and-avahi-local-service-discovery-that-works-until-it-doesnt/</guid><description>&lt;p&gt;You have done this a hundred times without thinking about it. You plug a Raspberry Pi into the network, type &lt;code&gt;ssh pi@mylab.local&lt;/code&gt;, and it just works — no DNS server, no editing &lt;code&gt;/etc/hosts&lt;/code&gt;, no hunting for an IP that DHCP handed out semi-randomly ten minutes ago. That &lt;code&gt;.local&lt;/code&gt; resolution is multicast DNS (mDNS), and on Linux the daemon doing the heavy lifting is almost always &lt;strong&gt;Avahi&lt;/strong&gt;. It is genuinely brilliant when it works, and genuinely maddening on the days it doesn&amp;rsquo;t — mostly because nobody ever explains what it&amp;rsquo;s actually doing under the surface, so when it breaks you&amp;rsquo;re left poking at it blind. This is the explanation I wish I&amp;rsquo;d had the first time a Chromecast vanished the day after I segmented my network.&lt;/p&gt;</description></item><item><title>Trivy and Container Scanning: Finding Vulnerabilities Before They Find You</title><link>https://vo.rs/story/trivy-and-container-scanning-finding-vulnerabilities-before-they-find-you/</link><pubDate>Tue, 25 Jun 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/trivy-and-container-scanning-finding-vulnerabilities-before-they-find-you/</guid><description>&lt;p&gt;Every Docker image you pull is a tarball of someone else&amp;rsquo;s decisions. That base image you chose two years ago because the tutorial used it? It&amp;rsquo;s carrying an OpenSSL with a known hole, a libc with a CVE, and three system packages you&amp;rsquo;ve never heard of, one of which has a remote code execution bug filed against it. You didn&amp;rsquo;t write any of that. You&amp;rsquo;re still running it.&lt;/p&gt;
&lt;p&gt;Container scanning is the unglamorous practice of finding out what&amp;rsquo;s actually inside your images before an attacker does. And the tool I reach for first, every time, is &lt;strong&gt;Trivy&lt;/strong&gt; — partly because it&amp;rsquo;s genuinely good, and partly because it&amp;rsquo;s free, fast, and doesn&amp;rsquo;t try to drag me into a sales call.&lt;/p&gt;</description></item><item><title>Cloudflare Tunnels: Exposing Services Without Opening Ports (and the Trade-Offs)</title><link>https://vo.rs/story/cloudflare-tunnels-exposing-services-without-opening-ports-and-the-trade-offs/</link><pubDate>Tue, 11 Jun 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/cloudflare-tunnels-exposing-services-without-opening-ports-and-the-trade-offs/</guid><description>&lt;p&gt;There&amp;rsquo;s a particular dread that comes with port-forwarding something on your home router. You log into a web interface that hasn&amp;rsquo;t changed since the Blair government, you punch a hole through to a box on your LAN, and then you spend the next week wondering whether you&amp;rsquo;ve just invited the entire internet to find that one unpatched service. Carrier-grade NAT might mean you can&amp;rsquo;t forward ports at all, and a dynamic IP means you&amp;rsquo;re chasing DDNS updates on top of everything else. I have done all of this. I have also been the person discovering, via a logfile full of probes from a dozen countries, that an exposed service was rather more interesting to the internet than I&amp;rsquo;d hoped.&lt;/p&gt;</description></item><item><title>Longhorn vs OpenEBS: Persistent Storage for Kubernetes That Isn't a Nightmare</title><link>https://vo.rs/story/longhorn-vs-openebs-persistent-storage-for-kubernetes-that-isnt-a-nightmare/</link><pubDate>Tue, 04 Jun 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/longhorn-vs-openebs-persistent-storage-for-kubernetes-that-isnt-a-nightmare/</guid><description>&lt;p&gt;Kubernetes storage has a reputation, and the reputation is &amp;ldquo;here be dragons.&amp;rdquo; I earned my own scar early: a Postgres pod that rescheduled onto a different node during a routine reboot and came up staring at an empty disk, because its data had politely stayed behind on the node it left. That is the persistent-volume problem in one sentence. The moment you move past stateless web apps and want to run a database, a wiki, or anything that remembers things across a restart, you hit it. In the cloud you&amp;rsquo;d shrug and attach an EBS volume that follows the pod around. In a homelab or on-prem cluster, you have a pile of machines with local disks and nothing shared binding them together, so a pod that lands on a new node finds its data marooned on the old one.&lt;/p&gt;</description></item><item><title>Matrix and Element: Self-Hosted Messaging That Federates</title><link>https://vo.rs/story/matrix-and-element-self-hosted-messaging-that-federates/</link><pubDate>Tue, 28 May 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/matrix-and-element-self-hosted-messaging-that-federates/</guid><description>&lt;p&gt;Three years ago a group chat I&amp;rsquo;d relied on for the best part of a decade evaporated overnight when the company behind the app got acquired and shut the service down. No export, no migration, a curt email and a dead login screen. That was the afternoon I decided the next messaging platform I trusted with my family&amp;rsquo;s conversations would be one I actually controlled. The pitch for Matrix is the same one email made fifty years ago and instant messaging has spent its entire existence refusing to make: you run your server, I run mine, and we talk anyway. No walled garden, no single company holding the keys, no app that gets bought and switched off while you&amp;rsquo;re mid-conversation. After running a Matrix homeserver for my family and a handful of friends for a couple of years, I&amp;rsquo;m convinced it&amp;rsquo;s the only self-hosted messaging story that actually delivers federation rather than just promising it. It&amp;rsquo;s also fiddly in ways worth being honest about before you commit a weekend to it.&lt;/p&gt;</description></item><item><title>cert-manager: Automated TLS Certificates That Actually Renew</title><link>https://vo.rs/story/cert-manager-automated-tls-certificates-that-actually-renew/</link><pubDate>Tue, 14 May 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/cert-manager-automated-tls-certificates-that-actually-renew/</guid><description>&lt;p&gt;I once let a certificate expire on a Sunday morning. Not a test box — the box that mattered. The expiry email had arrived eleven days earlier with the subject line containing the word &amp;ldquo;expires,&amp;rdquo; and I had archived it the way you archive everything that isn&amp;rsquo;t on fire yet. By the time visitors started seeing browser interstitials, I was SSH&amp;rsquo;d into a server trying to remember which flags &lt;code&gt;certbot&lt;/code&gt; wanted while my coffee went cold and the clock ran. TLS certificates expire on a schedule precisely engineered to be longer than your memory and shorter than your attention span, and the manual-renewal model fails in exactly this way: silently, then all at once.&lt;/p&gt;</description></item><item><title>Kustomize: Kubernetes Configuration Without the Template Sprawl of Helm</title><link>https://vo.rs/story/kustomize-kubernetes-configuration-without-the-template-sprawl-of-helm/</link><pubDate>Tue, 07 May 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/kustomize-kubernetes-configuration-without-the-template-sprawl-of-helm/</guid><description>&lt;p&gt;There&amp;rsquo;s a moment, somewhere around your third environment, when a Helm chart stops being a convenience and starts being a small programming language you didn&amp;rsquo;t ask to learn. You&amp;rsquo;re staring at &lt;code&gt;{{- if .Values.ingress.enabled }}&lt;/code&gt; nested four levels deep, whitespace-sensitive, debugged by squinting at &lt;code&gt;helm template&lt;/code&gt; output. The thing you wanted was &amp;ldquo;the same manifests as production, but with a different replica count and hostname.&amp;rdquo; The thing you got was Go templating wrapped around YAML, which is a syntax wrapped around a syntax, and your editor can&amp;rsquo;t help you with either.&lt;/p&gt;</description></item><item><title>Firefly III: Self-Hosted Personal Finance Without the Bank Watching</title><link>https://vo.rs/story/firefly-iii-self-hosted-personal-finance/</link><pubDate>Tue, 23 Apr 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/firefly-iii-self-hosted-personal-finance/</guid><description>&lt;p&gt;Every budgeting app I have ever tried eventually wanted three things: my bank login, a monthly subscription, and the right to &amp;ldquo;anonymise&amp;rdquo; my spending and sell it to whoever buys that sort of thing. I gave up on all of them and put Firefly III on a box in the corner of my flat instead. It has been quietly tracking every penny I move for years now, and the only entity studying my coffee habit is me.&lt;/p&gt;</description></item><item><title>Nix: Reproducible Development Environments (Once You Survive the Learning Curve)</title><link>https://vo.rs/story/nix-reproducible-development-environments-once-you-survive-the-learning-curve/</link><pubDate>Tue, 16 Apr 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/nix-reproducible-development-environments-once-you-survive-the-learning-curve/</guid><description>&lt;p&gt;Every developer has lived the same small tragedy. A new colleague clones the repo, follows the README to the letter, and nothing works. The wrong version of Node. A missing system library. A &lt;code&gt;make&lt;/code&gt; step that needs a tool nobody documented because it&amp;rsquo;s been on the lead&amp;rsquo;s laptop since 2019 and they forgot it wasn&amp;rsquo;t standard. Half a day vanishes into &amp;ldquo;what version of what do you have installed&amp;rdquo;, and the answer, always, is &lt;em&gt;different from mine&lt;/em&gt;. Nix exists to make this entire category of misery go away — and it does, eventually, after putting you through a learning curve I will not pretend is gentle. This post is both the pitch and the warning, because I think the pitch is real and the warning is earned.&lt;/p&gt;</description></item><item><title>Keepalived and Virtual IPs: High Availability Without a Load Balancer</title><link>https://vo.rs/story/keepalived-and-virtual-ips-high-availability-without-a-load-balancer/</link><pubDate>Tue, 02 Apr 2024 10:00:00 +0000</pubDate><guid>https://vo.rs/story/keepalived-and-virtual-ips-high-availability-without-a-load-balancer/</guid><description>&lt;p&gt;There is a particular flavour of homelab anxiety that arrives the moment a single box becomes load-bearing. Your reverse proxy, your DNS resolver, your little MQTT broker that the whole house now depends on — all of it pinned to one IP address on one machine that will, one day, want a kernel update at the worst possible time. The cloud answer to this is a managed load balancer, and it is a fine answer if you enjoy paying monthly for a TCP forwarder. The self-hosted answer, and a remarkably good one, is &lt;strong&gt;keepalived&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>Ntfy: Self-Hosted Push Notifications That Replace Twelve SaaS Webhooks</title><link>https://vo.rs/story/ntfy-self-hosted-push-notifications-that-replace-twelve-saas-webhooks/</link><pubDate>Tue, 02 Apr 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/ntfy-self-hosted-push-notifications-that-replace-twelve-saas-webhooks/</guid><description>&lt;p&gt;Count the ways your homelab currently tries to get your attention. The backup script emails you, which means you now check email for alerts, which means you&amp;rsquo;ve made email worse. Prometheus pages through some webhook you configured eighteen months ago and no longer remember the shape of. One app posts to a Discord server you joined &lt;em&gt;only&lt;/em&gt; for its notifications, another insists on Telegram, a third wants a Pushover token you bought once and can&amp;rsquo;t find. Each new tool arrives with its own notification mechanism bolted on, and pretty soon your phone is being prodded by half a dozen different SaaS middlemen — several of which you&amp;rsquo;re quietly trusting with messages like &amp;ldquo;the front door sensor opened at 3am.&amp;rdquo; That is a lot of third parties in the loop for the crime of telling you a disk is full.&lt;/p&gt;</description></item><item><title>The Real Cost of Self-Hosting: Electricity, Time, and What You Actually Save</title><link>https://vo.rs/story/the-real-cost-of-self-hosting-electricity-time-and-what-you-actually-save/</link><pubDate>Thu, 28 Mar 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/the-real-cost-of-self-hosting-electricity-time-and-what-you-actually-save/</guid><description>&lt;p&gt;There is a story self-hosters tell themselves, usually at about 1am while staring at a rack of blinking lights, and it goes like this: &amp;ldquo;I&amp;rsquo;m saving so much money by not paying for all these subscriptions.&amp;rdquo; It is a lovely story. It is also, in my experience, mostly fiction — or at least a great deal more complicated than the version we like to repeat at dinner parties to justify the noise coming from the cupboard.&lt;/p&gt;</description></item><item><title>Traefik vs Nginx Proxy Manager: Reverse Proxies for the Rest of Us</title><link>https://vo.rs/story/traefik-vs-nginx-proxy-manager-reverse-proxies-for-the-rest-of-us/</link><pubDate>Tue, 19 Mar 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/traefik-vs-nginx-proxy-manager-reverse-proxies-for-the-rest-of-us/</guid><description>&lt;p&gt;Sooner or later, every homelab outgrows the colon. You start with &lt;code&gt;192.168.1.40:8989&lt;/code&gt; for Sonarr, &lt;code&gt;:3000&lt;/code&gt; for Grafana, &lt;code&gt;:8080&lt;/code&gt; for the thing you can no longer remember, and you keep a sticky note of port numbers like it&amp;rsquo;s 2003. Then you want HTTPS, because typing passwords over plain HTTP makes your skin crawl, and suddenly you need a reverse proxy: one thing on ports 80 and 443 that looks at the hostname and routes the request to the right backend, terminating TLS on the way through.&lt;/p&gt;</description></item><item><title>GitOps with Flux: Letting Git Be Your Cluster's Source of Truth</title><link>https://vo.rs/story/gitops-with-flux-letting-git-be-your-clusters-source-of-truth/</link><pubDate>Tue, 12 Mar 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/gitops-with-flux-letting-git-be-your-clusters-source-of-truth/</guid><description>&lt;p&gt;The worst hour I ever spent on my home cluster was the one where I tried to reconstruct what was actually running on it. I&amp;rsquo;d applied a manifest months earlier, hot-patched something live during an outage, edited a config map by hand, and then forgotten all three. The YAML in my repo and the reality in the cluster had quietly diverged, and I couldn&amp;rsquo;t say with confidence what was deployed — which meant I couldn&amp;rsquo;t rebuild it if the node died. That gap between &amp;ldquo;what&amp;rsquo;s in Git&amp;rdquo; and &amp;ldquo;what&amp;rsquo;s running&amp;rdquo; is configuration drift, and it&amp;rsquo;s the single failure mode GitOps exists to kill.&lt;/p&gt;</description></item><item><title>TOTP and WebAuthn: Two-Factor Authentication Without Authy</title><link>https://vo.rs/story/totp-and-webauthn-two-factor-authentication-without-authy/</link><pubDate>Tue, 05 Mar 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/totp-and-webauthn-two-factor-authentication-without-authy/</guid><description>&lt;p&gt;There was a moment, somewhere around the third time Authy decided my desktop app should no longer exist, that I realised I had handed the keys to my entire digital life to a company whose roadmap I had no say in. The desktop client was deprecated, the cloud backup was a black box, and exporting my tokens turned out to be deliberately awkward. That&amp;rsquo;s the wrong feeling to have about the thing standing between an attacker and every account I own.&lt;/p&gt;</description></item><item><title>MetalLB and Kubernetes Bare-Metal Networking: LoadBalancers Without a Cloud</title><link>https://vo.rs/story/metallb-and-kubernetes-bare-metal-networking-loadbalancers-without-a-cloud/</link><pubDate>Tue, 20 Feb 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/metallb-and-kubernetes-bare-metal-networking-loadbalancers-without-a-cloud/</guid><description>&lt;p&gt;The first thing that goes wrong when you build a Kubernetes cluster on your own hardware is the most anticlimactic possible failure. You deploy something, set its Service type to &lt;code&gt;LoadBalancer&lt;/code&gt; the way every tutorial told you to, and then&amp;hellip; nothing. &lt;code&gt;kubectl get svc&lt;/code&gt; shows &lt;code&gt;EXTERNAL-IP&lt;/code&gt; stuck on &lt;code&gt;&amp;lt;pending&amp;gt;&lt;/code&gt;, forever, with the quiet patience of a thing that is never going to happen.&lt;/p&gt;
&lt;p&gt;This is not a bug. On a cloud provider, &lt;code&gt;type: LoadBalancer&lt;/code&gt; is a request that the cloud&amp;rsquo;s controller fulfils by provisioning an actual load balancer and handing you a public IP. On bare metal there is no cloud controller listening, so the request just sits there. MetalLB is the missing piece: it implements that controller for your own network, so a homelab or on-prem cluster can finally do the thing the cloud does for free.&lt;/p&gt;</description></item><item><title>Writing CLI Tools in Go: From Zero to Useful in an Afternoon</title><link>https://vo.rs/story/writing-cli-tools-in-go-from-zero-to-useful-in-an-afternoon/</link><pubDate>Tue, 13 Feb 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/writing-cli-tools-in-go-from-zero-to-useful-in-an-afternoon/</guid><description>&lt;p&gt;I write a lot of little command-line tools. Glue that wires two APIs together, a thing that munges a CSV the way I actually need it, a daemon that watches a directory and pokes something when a file lands. For years my reflex was a Bash script that grew tentacles, or a Python file that worked fine on my machine and nowhere else. These days I reach for Go, and I keep reaching for it because the gap between &amp;ldquo;idea&amp;rdquo; and &amp;ldquo;a binary I can hand to someone&amp;rdquo; is genuinely about an afternoon. Here&amp;rsquo;s why, and how.&lt;/p&gt;</description></item><item><title>Paperless-ngx: A Paperless Office That Actually Works</title><link>https://vo.rs/story/paperless-ngx-a-paperless-office-that-actually-works/</link><pubDate>Mon, 12 Feb 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/paperless-ngx-a-paperless-office-that-actually-works/</guid><description>&lt;p&gt;I have owned three filing cabinets in my life. Each one followed the same arc: pristine and hopeful for a fortnight, then a graveyard of bank statements I will never read, slowly fossilising under a pile of takeaway menus. The promise of the &amp;ldquo;paperless office&amp;rdquo; was sold to me decades ago and never delivered, because the missing piece was never the scanner. It was knowing where anything went afterwards. A scanned PDF named &lt;code&gt;scan_0047.pdf&lt;/code&gt; sitting in a folder is not filed; it&amp;rsquo;s just lost in a tidier way.&lt;/p&gt;</description></item><item><title>Podman: Running Containers Without Docker (and Without Losing Your Mind)</title><link>https://vo.rs/story/podman-running-containers-without-docker-and-without-losing-your-mind/</link><pubDate>Tue, 06 Feb 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/podman-running-containers-without-docker-and-without-losing-your-mind/</guid><description>&lt;p&gt;For about a decade, &amp;ldquo;containers&amp;rdquo; and &amp;ldquo;Docker&amp;rdquo; were synonyms in most people&amp;rsquo;s heads, mine included. You installed Docker, you ran &lt;code&gt;docker run&lt;/code&gt;, a daemon somewhere did the work, and you didn&amp;rsquo;t think too hard about the fact that the daemon ran as root and you were talking to it through a socket that was, functionally, a key to the whole machine. Podman is what you get when someone looks at that arrangement and asks why it has to be like that. The answer, it turns out, is that it doesn&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>De-Googling Your Life: A Realistic, Step-by-Step Migration Plan</title><link>https://vo.rs/story/de-googling-your-life-a-realistic-step-by-step-migration-plan/</link><pubDate>Tue, 30 Jan 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/de-googling-your-life-a-realistic-step-by-step-migration-plan/</guid><description>&lt;p&gt;Every so often someone announces, with the zeal of the newly converted, that they have &amp;ldquo;deleted Google&amp;rdquo; — and then quietly admits three weeks later that they still use Maps, still use the Play Store, and have a Gmail address forwarding to their new account because everything they ever signed up for points at it. I&amp;rsquo;ve watched this fail enough times — and done a version of it myself — to be deeply suspicious of the all-or-nothing approach. De-Googling is not a single heroic act; it&amp;rsquo;s a migration project, and migration projects succeed when you sequence them properly and let nothing break in production.&lt;/p&gt;</description></item><item><title>Loki: Log Aggregation for People Who Can't Afford Splunk</title><link>https://vo.rs/story/loki-log-aggregation-for-people-who-cant-afford-splunk/</link><pubDate>Tue, 23 Jan 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/loki-log-aggregation-for-people-who-cant-afford-splunk/</guid><description>&lt;p&gt;There are two kinds of homelabber: the ones who SSH into each box and run &lt;code&gt;journalctl&lt;/code&gt; when something breaks, and the ones who got tired of doing that around the fourth machine. I crossed that line a while ago, and I remember the exact incident that pushed me over it — a service falling over at 11pm, and me hopping between four terminals trying to work out which of them held the log line that mattered. Once you have a handful of hosts and a stack of Docker containers, &amp;ldquo;which log, on which box, from which container?&amp;rdquo; becomes a small archaeological dig every single time, invariably conducted in a hurry while something is actively on fire.&lt;/p&gt;</description></item><item><title>K3s Multi-Node: Adding a Second Machine to Your Cluster</title><link>https://vo.rs/story/k3s-multi-node-adding-a-second-machine-to-your-cluster/</link><pubDate>Tue, 16 Jan 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/k3s-multi-node-adding-a-second-machine-to-your-cluster/</guid><description>&lt;p&gt;A single-node K3s install is a wonderful thing. It&amp;rsquo;s Kubernetes that fits in a few hundred megabytes of RAM, it boots in seconds, and it lets you learn the whole ecosystem on one spare machine. But a cluster of one is a contradiction in terms. The day a sensible person finally gives in and orders a second mini PC — or rescues an old laptop from the cupboard — is the day K3s starts being genuinely useful. Pods can be rescheduled, you can drain a node to do maintenance, and a single dead disk stops being the end of the world.&lt;/p&gt;</description></item><item><title>Wake-on-LAN Automation: Powering Servers On and Off with Home Assistant</title><link>https://vo.rs/story/wake-on-lan-automation-powering-servers-on-and-off-with-home-assistant/</link><pubDate>Fri, 05 Jan 2024 09:00:00 +0000</pubDate><guid>https://vo.rs/story/wake-on-lan-automation-powering-servers-on-and-off-with-home-assistant/</guid><description>&lt;p&gt;I have a beefy machine in the cupboard that exists to do exactly one thing: transcode media and crunch the occasional batch job. It pulls something like 90 watts at idle, which over a year is a meaningful slice of the electricity bill for a box that is genuinely busy maybe two hours a day. For ages I left it running because the alternative — getting up and pressing the power button when I wanted to watch something — was worse. Then I wired it into Home Assistant, and now it sleeps until it is needed and shuts itself down when it is idle. The savings paid for the effort in a couple of months.&lt;/p&gt;</description></item><item><title>Container Image Housekeeping: Pruning, Pinning, and Not Running latest in Production</title><link>https://vo.rs/story/container-image-housekeeping-pruning-pinning-and-not-running-latest-in-production/</link><pubDate>Tue, 19 Dec 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/container-image-housekeeping-pruning-pinning-and-not-running-latest-in-production/</guid><description>&lt;p&gt;Container hosts have a way of quietly filling up. You pull images, you rebuild, you redeploy, and every iteration leaves a sediment of old layers behind. Then one ordinary morning a deploy fails with &lt;code&gt;no space left on device&lt;/code&gt;, you go looking, and &lt;code&gt;/var/lib/docker&lt;/code&gt; has eaten thirty gigabytes of disk you didn&amp;rsquo;t know you&amp;rsquo;d given it. I have had this exact morning, on a box I thought I was looking after, and the indignity of discovering that &lt;em&gt;nothing was wrong&lt;/em&gt; — no leak, no runaway log, just months of orphaned layers nobody pruned — is a particular flavour of self-inflicted. Container image housekeeping is the least glamorous topic in this entire field, and it&amp;rsquo;s the one that&amp;rsquo;ll wake you at 3am if you ignore it.&lt;/p&gt;</description></item><item><title>QNAP NAS as a Kubernetes Storage Backend: iSCSI, NFS, or Just Don't</title><link>https://vo.rs/story/qnap-nas-as-a-kubernetes-storage-backend-iscsi-nfs-or-just-dont/</link><pubDate>Mon, 11 Dec 2023 10:00:00 +0000</pubDate><guid>https://vo.rs/story/qnap-nas-as-a-kubernetes-storage-backend-iscsi-nfs-or-just-dont/</guid><description>&lt;p&gt;Everyone who builds a homelab Kubernetes cluster hits the same wall about a week in: stateful workloads. Your pods are stateless and beautiful until you want to run Postgres, or a media server, or anything that remembers things between restarts. Then you need persistent volumes, and you look around your house, and your eyes land on the QNAP NAS humming away in the corner with several terabytes of perfectly good storage. Surely you can just point the cluster at that?&lt;/p&gt;</description></item><item><title>UniFi for the Home Lab: Where It Shines and Where It Doesn't</title><link>https://vo.rs/story/unifi-for-the-home-lab-where-it-shines-and-where-it-doesnt/</link><pubDate>Tue, 05 Dec 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/unifi-for-the-home-lab-where-it-shines-and-where-it-doesnt/</guid><description>&lt;p&gt;UniFi is the gateway drug of home networking. You buy one access point because your ISP router&amp;rsquo;s Wi-Fi is a disgrace, you marvel at the clean app and the pretty graphs, and eighteen months later you own a gateway, a couple of switches, a rack-mount UPS you didn&amp;rsquo;t strictly need, and you refer to your hallway cupboard as &amp;ldquo;the rack.&amp;rdquo; Ubiquiti has built something genuinely good here — prosumer gear with an enterprise feel at a price that doesn&amp;rsquo;t require a purchase order. But it is not the right answer for everyone, and the places it falls down are worth knowing before you spend the money.&lt;/p&gt;</description></item><item><title>Healthchecks.io (Self-Hosted): Making Sure Your Cron Jobs Actually Ran</title><link>https://vo.rs/story/healthchecksio-self-hosted-making-sure-your-cron-jobs-actually-ran/</link><pubDate>Tue, 21 Nov 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/healthchecksio-self-hosted-making-sure-your-cron-jobs-actually-ran/</guid><description>&lt;p&gt;Here is a story that has happened to almost everyone who has ever written a cron job. You set up a nightly backup. You test it once, it works, you feel responsible and adult. Eight months later you actually need that backup, and you discover it stopped running in March because of a full disk, an expired token, or a typo you made while &amp;ldquo;tidying up.&amp;rdquo; The cron job didn&amp;rsquo;t fail loudly. It failed &lt;em&gt;silently&lt;/em&gt;, which is the worst way for anything to fail, and nobody told you because there was nobody to tell.&lt;/p&gt;</description></item><item><title>SQLite: The Database You Already Have and Probably Underuse</title><link>https://vo.rs/story/sqlite-the-database-you-already-have-and-probably-underuse/</link><pubDate>Tue, 14 Nov 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/sqlite-the-database-you-already-have-and-probably-underuse/</guid><description>&lt;p&gt;There is a reflex, the moment a project needs to store anything, to stand up Postgres. A container, a connection string, a user, a password, a port, a backup strategy, a thing to keep running and patched. And for an enormous number of projects, all of that ceremony is in service of a workload a single file on disk could handle without breaking a sweat. That file is SQLite, it is almost certainly already installed on whatever you are reading this on, and it is one of the most underused tools in the business.&lt;/p&gt;</description></item><item><title>Mealie: A Self-Hosted Recipe Manager for People Who Actually Cook</title><link>https://vo.rs/story/mealie-a-self-hosted-recipe-manager-for-people-who-cook/</link><pubDate>Tue, 07 Nov 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/mealie-a-self-hosted-recipe-manager-for-people-who-cook/</guid><description>&lt;p&gt;I have a confession. For years my recipe collection was a chaotic sprawl of browser bookmarks, phone screenshots, a Google Doc nobody could ever find, and three different paper notebooks written in the handwriting of a doctor having a stroke. Every single time I wanted to cook something I&amp;rsquo;d dig up the original blog post, scroll past 1,400 words about the author&amp;rsquo;s grandmother in Tuscany, dodge a video ad that started playing on its own with the sound on, dismiss a newsletter pop-up, and &lt;em&gt;then&lt;/em&gt; — finally — reach the one line telling me how much flour to use. I once measured it: forty seconds of scrolling and ad-swatting to find a quantity I needed in two.&lt;/p&gt;</description></item><item><title>Helm Charts Demystified: What They Actually Do and When to Skip Them</title><link>https://vo.rs/story/helm-charts-demystified-what-they-actually-do-and-when-to-skip-them/</link><pubDate>Tue, 31 Oct 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/helm-charts-demystified-what-they-actually-do-and-when-to-skip-them/</guid><description>&lt;p&gt;Everybody installs things on Kubernetes the same way. You find the project, you scroll to the README, and there it is: &lt;code&gt;helm repo add&lt;/code&gt;, &lt;code&gt;helm install&lt;/code&gt;, done. Three commands and a workload appears, fully wired with services, config maps, secrets and a horizontal pod autoscaler you didn&amp;rsquo;t ask for. Helm has become the default, and like most defaults it&amp;rsquo;s reached for without much thought about what it actually is or whether you need it.&lt;/p&gt;</description></item><item><title>Grafana and Prometheus: A Monitoring Stack That Scales Down</title><link>https://vo.rs/story/grafana-and-prometheus-a-monitoring-stack-that-scales-down/</link><pubDate>Tue, 17 Oct 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/grafana-and-prometheus-a-monitoring-stack-that-scales-down/</guid><description>&lt;p&gt;There is a particular flavour of homelab anxiety that strikes at 3am: is the NAS still alive? Did the disk fill up while I slept? Is that container restarting in a loop, quietly burning through SD-card writes? You can answer these questions by SSHing in and squinting at &lt;code&gt;df&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt;, or you can answer them with a graph. I am firmly in the graph camp, and for the better part of a decade the graph has come from Grafana fed by Prometheus.&lt;/p&gt;</description></item><item><title>Why Every Side Project Should Have a Backup Plan (And How to Build One)</title><link>https://vo.rs/story/why-every-side-project-should-have-a-backup-plan-and-how-to-build-one/</link><pubDate>Tue, 10 Oct 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/why-every-side-project-should-have-a-backup-plan-and-how-to-build-one/</guid><description>&lt;p&gt;Every side project starts the same way: a burst of enthusiasm, a database, and absolutely no backups. This is fine, right up until the moment it is catastrophically not fine — the disk fills, the migration goes sideways, you &lt;code&gt;rm -rf&lt;/code&gt; the wrong directory at midnight, or the VPS provider has a bad week and your droplet evaporates with it.&lt;/p&gt;
&lt;p&gt;I have lost data this way. Not recently, because losing data once is an education you only need to pay for the once. The lesson was not &amp;ldquo;back things up more&amp;rdquo; — everyone knows that. The lesson was that a vague intention to back things up is worth precisely nothing, and only an automated, tested, restorable backup counts. So let&amp;rsquo;s build one that actually works for a small project, without enterprise budgets or enterprise faff.&lt;/p&gt;</description></item><item><title>VLAN Segmentation at Home: Keeping Your Smart Toaster Away from Your NAS</title><link>https://vo.rs/story/vlan-segmentation-at-home-keeping-your-smart-toaster-away-from-your-nas/</link><pubDate>Tue, 26 Sep 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/vlan-segmentation-at-home-keeping-your-smart-toaster-away-from-your-nas/</guid><description>&lt;p&gt;Here is an uncomfortable fact about most home networks: every device on them can talk to every other device. Your laptop, your phone, your NAS full of irreplaceable photos, and that £25 smart plug running firmware last updated when it left the factory in Shenzhen — they&amp;rsquo;re all sitting on the same flat subnet, able to reach each other freely. The smart plug has a hardcoded telnet password and a cloud connection you can&amp;rsquo;t see into. The NAS has everything you care about. On a flat network, the first is one hop from the second, and nothing in between is checking whether that hop should be allowed.&lt;/p&gt;</description></item><item><title>BGP at Home: What Happens When You Peer with Your ISP</title><link>https://vo.rs/story/bgp-at-home-what-happens-when-you-peer-with-your-isp/</link><pubDate>Fri, 15 Sep 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/bgp-at-home-what-happens-when-you-peer-with-your-isp/</guid><description>&lt;p&gt;I have a /48 of IPv6 announced from a box in my house. That is roughly 1.2 billion billion addresses, more than the entire IPv4 internet several times over, sitting behind a single second-hand mini PC that cost less than a decent monitor. It is gloriously absurd, and getting there taught me more about how the internet actually works than a decade of reading RFCs. This is the story of how a hobbyist peers with an upstream, announces their own address space, and joins the roughly hundred thousand independent networks that route the global internet between them.&lt;/p&gt;</description></item><item><title>DNS Over HTTPS at Home: Running Your Own DoH Resolver</title><link>https://vo.rs/story/dns-over-https-at-home-running-your-own-doh-resolver/</link><pubDate>Sat, 12 Aug 2023 11:00:00 +0000</pubDate><guid>https://vo.rs/story/dns-over-https-at-home-running-your-own-doh-resolver/</guid><description>&lt;p&gt;Plain DNS is the last great open postcard of the internet. You&amp;rsquo;ve encrypted your web traffic with TLS, you&amp;rsquo;ve got a VPN you trust, you&amp;rsquo;ve hardened SSH to within an inch of its life — and then your machine cheerfully shouts every single domain name you look up, in clear text, over UDP port 53, to whatever resolver your ISP handed you at DHCP time. Anyone on the path can read it, and plenty of middleboxes do.&lt;/p&gt;</description></item><item><title>Rallly: Self-Hosted Scheduling Without Doodle's Data Harvesting</title><link>https://vo.rs/story/rallly-self-hosted-scheduling-without-doodle/</link><pubDate>Wed, 09 Aug 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/rallly-self-hosted-scheduling-without-doodle/</guid><description>&lt;p&gt;There is a special kind of dread that comes from trying to pin six people down to a single evening. Reply-all threads spiral, someone proposes a date three others have already vetoed, and eventually somebody mutters the word &amp;ldquo;Doodle&amp;rdquo;. And Doodle does work. It also works very hard at watching everyone who clicks your link, sprinkling ad-tech all over a page whose entire job is to collect your friends&amp;rsquo; availability and, increasingly, their attention. I host my own instead. The tool is called Rallly, and it is one of the few self-hosted things I actually recommend without a long list of caveats.&lt;/p&gt;</description></item><item><title>Tailscale vs Netbird: Self-Hosted Mesh VPNs Compared</title><link>https://vo.rs/story/tailscale-vs-netbird-self-hosted-mesh-vpns-compared/</link><pubDate>Mon, 03 Jul 2023 09:00:00 +0000</pubDate><guid>https://vo.rs/story/tailscale-vs-netbird-self-hosted-mesh-vpns-compared/</guid><description>&lt;p&gt;I have lost count of the number of times I have recommended Tailscale to people. It is, genuinely, the easiest way to get a flat, encrypted network across machines that have no business being on the same subnet — your laptop in a café, a VPS in Frankfurt, the NAS in the cupboard under the stairs. You install it, you log in, and suddenly everything can ping everything else over WireGuard with no port forwarding and no faff. It feels like magic. If you want the gentle introduction to that magic before this comparison, I wrote one up in &lt;a href="https://vo.rs/story/tailscale-a-zero-config-mesh-vpn/"&gt;Tailscale: a zero-config mesh VPN&lt;/a&gt;; this piece assumes you already know what a tailnet is and want to know whether to own the control plane yourself.&lt;/p&gt;</description></item><item><title>Javascript, the good, bad and ugly.</title><link>https://vo.rs/story/javascript-the-good-bad-and-ugly/</link><pubDate>Thu, 22 Dec 2022 17:01:57 +0000</pubDate><guid>https://vo.rs/story/javascript-the-good-bad-and-ugly/</guid><description>&lt;p&gt;Brendan Eich wrote the first version of JavaScript in ten days in May 1995, under pressure to ship something for the Netscape Navigator 2.0 beta. He had wanted to put Scheme in the browser; management wanted something that looked like Java, because Java was the hot thing that year. So he took the functional core of Scheme, the prototype-based objects of Self, and bolted on a curly-brace syntax that resembled Java without behaving anything like it. The result was called Mocha, then LiveScript, and finally — in a marketing deal with Sun — JavaScript. Three names and a deadline measured in days. Three decades later that ten-day prototype runs in every browser on earth, on servers, on embedded boards, and increasingly inside my home automation. I have shipped a lot of JavaScript and I have sworn at a lot of JavaScript, often in the same afternoon. This is the honest tour: where it earns its place, where it irritates, and where it genuinely gets ugly.&lt;/p&gt;</description></item><item><title>The Internet Is a Series of Tubes</title><link>https://vo.rs/story/the-internet-is-a-series-of-tubes/</link><pubDate>Fri, 13 May 2022 17:47:19 +0000</pubDate><guid>https://vo.rs/story/the-internet-is-a-series-of-tubes/</guid><description>&lt;p&gt;On 28 June 2006, a US senator named Ted Stevens stood up during a committee debate on net neutrality and delivered a sentence that would outlive most of his legislative career: &amp;ldquo;It&amp;rsquo;s not a big truck. It&amp;rsquo;s a series of tubes.&amp;rdquo; He went on to explain that the tubes could get &amp;ldquo;filled&amp;rdquo;, that an email he had been sent took days to arrive because the tubes were congested, and that we should therefore be cautious about letting anyone dump &amp;ldquo;enormous amounts of material&amp;rdquo; into them. The internet, predictably, did not respond with kindness.&lt;/p&gt;</description></item><item><title>Clearing your DNS cache</title><link>https://vo.rs/story/clearing-your-dns-cache/</link><pubDate>Sat, 25 Dec 2021 17:28:00 +0000</pubDate><guid>https://vo.rs/story/clearing-your-dns-cache/</guid><description>&lt;p&gt;I migrated a website to a new server once, updated the DNS A record, watched the new box light up with traffic from everywhere — except my own laptop, which stubbornly kept loading the old server for the better part of an hour. Nothing was broken. The record had changed. The internet at large had noticed. My machine simply hadn&amp;rsquo;t, because it had cached the old answer and intended to keep using it until its timer ran out. That gap — between &amp;ldquo;the record changed&amp;rdquo; and &amp;ldquo;your computer agrees&amp;rdquo; — is what flushing the DNS cache fixes, and it&amp;rsquo;s the single most common reason a site works for everyone except the one person standing in front of you.&lt;/p&gt;</description></item><item><title>Video Authoring with Google Photos</title><link>https://vo.rs/story/video-authoring-google-photos/</link><pubDate>Tue, 16 Apr 2019 16:45:00 +0000</pubDate><guid>https://vo.rs/story/video-authoring-google-photos/</guid><description>&lt;p&gt;I built my first slideshow-video for a family birthday in about four minutes on my phone, sitting in a car park, and it looked genuinely good. That&amp;rsquo;s the pitch for Google Photos&amp;rsquo; video tool in one sentence: it is the fastest way to turn a pile of stills into something with music and motion that you&amp;rsquo;d actually send to relatives. It is also, quietly, a lock-in machine — the free unlimited storage that once made it a no-brainer ended in June 2021, and the &amp;ldquo;movie&amp;rdquo; you assemble lives inside Google&amp;rsquo;s walls until you export it. So this is two posts in one: how to make the video, and how to keep the source photos somewhere you control.&lt;/p&gt;</description></item></channel></rss>