<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Udo Müller</title><description>Build logs, notes and project write-ups on infrastructure automation, Kubernetes, GitOps and the homelab they run on.</description><link>https://cs-ol.de/</link><item><title>I couldn&apos;t run Talos in CI, so I tested what I could</title><link>https://cs-ol.de/blog/testing-a-talos-collection-without-a-cluster/</link><guid isPermaLink="true">https://cs-ol.de/blog/testing-a-talos-collection-without-a-cluster/</guid><description>I wanted real-cluster tests for an Ansible collection that drives Talos. On my machine neither Docker nor QEMU would give me a node in maintenance mode, so I tested the rendering instead: fixtures, talosctl validate, determinism and negative cases. What it caught, and the five CI runs it took to go green.</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><category>testing</category><category>ci</category><category>gitea</category></item><item><title>My Pi-hole answered every query and logged none of them</title><link>https://cs-ol.de/blog/pihole-answered-every-query-and-logged-none/</link><guid isPermaLink="true">https://cs-ol.de/blog/pihole-answered-every-query-and-logged-none/</guid><description>DNS lookups to the Pi-hole address succeeded, but its query log stayed empty. The answers came from the router, whose ad-block feature redirects every port-53 packet on the LAN to its own resolver. How I found out, and the one-line test that would have saved me the afternoon.</description><pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate><category>dns</category><category>pi-hole</category><category>wsl</category><category>metallb</category></item><item><title>One corrupt WAL segment, and Prometheus quietly deleted its own history</title><link>https://cs-ol.de/blog/one-corrupt-wal-segment-ate-prometheus-history/</link><guid isPermaLink="true">https://cs-ol.de/blog/one-corrupt-wal-segment-ate-prometheus-history/</guid><description>A Prometheus volume looked almost full while retentionSize was set. The real story: a single corrupt WAL segment had stopped every checkpoint for weeks, the WAL grew to 48.7 GiB, and the size cap spent the difference by deleting old blocks.</description><pubDate>Sat, 26 Sep 2026 00:00:00 GMT</pubDate><category>longhorn</category><category>storage</category><category>monitoring</category></item><item><title>After the NVMe: what the fixes bought, and what the rolling restart cost</title><link>https://cs-ol.de/blog/after-the-nvme-what-the-fixes-bought/</link><guid isPermaLink="true">https://cs-ol.de/blog/after-the-nvme-what-the-fixes-bought/</guid><description>A follow-up on the cluster whose etcd shared one consumer drive with every storage replica. Flush latency fell from 23 ms to 3 ms, a filtered copy shrank a 9.9 GB SQLite database to 3.8 GB in ten minutes, and turning on discard cost more in replica rebuilds than in reboots.</description><pubDate>Fri, 25 Sep 2026 00:00:00 GMT</pubDate><category>etcd</category><category>storage</category><category>longhorn</category><category>proxmox</category><category>sqlite</category></item><item><title>Three etcd members, one consumer NVMe</title><link>https://cs-ol.de/blog/three-etcd-members-one-consumer-nvme/</link><guid isPermaLink="true">https://cs-ol.de/blog/three-etcd-members-one-consumer-nvme/</guid><description>A scale-down that never happened led me to leader elections lost on every node, and from there to a single drive holding every etcd member and every storage replica. Three replicas on one disk mostly just triple the writes.</description><pubDate>Wed, 23 Sep 2026 00:00:00 GMT</pubDate><category>etcd</category><category>storage</category><category>longhorn</category><category>proxmox</category></item><item><title>Active/passive failover on a single VIP, which I already had</title><link>https://cs-ol.de/blog/active-passive-failover-one-vip/</link><guid isPermaLink="true">https://cs-ol.de/blog/active-passive-failover-one-vip/</guid><description>Kubernetes has no notion of an active/passive Service. MetalLB in L2 mode with externalTrafficPolicy: Local is one, and I had been running it with a single replica the whole time.</description><pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate><category>metallb</category><category>dns</category><category>high-availability</category></item><item><title>A backup job that exits 0 and backs up nothing</title><link>https://cs-ol.de/blog/backup-job-that-backs-up-nothing/</link><guid isPermaLink="true">https://cs-ol.de/blog/backup-job-that-backs-up-nothing/</guid><description>The backup command I was asked to schedule did nothing on the current version: it printed usage text, exited 0 and wrote no file. So the job now checks the file it produces and refuses to upload anything that is not a real archive.</description><pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate><category>backups</category><category>pi-hole</category><category>ssh</category></item><item><title>Reranking made my search slower and worse</title><link>https://cs-ol.de/blog/reranking-made-search-worse/</link><guid isPermaLink="true">https://cs-ol.de/blog/reranking-made-search-worse/</guid><description>I added local semantic search to a small knowledge base, and the default LLM reranker took 33 seconds to put the wrong document first. Every cheaper path got it right, which I only found out by asking questions I already knew the answers to.</description><pubDate>Sun, 13 Sep 2026 00:00:00 GMT</pubDate><category>search</category><category>llm</category><category>wsl</category></item><item><title>Replacing WordPress with a static site I can deploy from my own CI</title><link>https://cs-ol.de/blog/static-site-on-shared-hosting/</link><guid isPermaLink="true">https://cs-ol.de/blog/static-site-on-shared-hosting/</guid><description>Retiring an unpatched WordPress on PHP 5.6 by replacing it with an Astro build pushed from a self-hosted Gitea runner to the same shared hosting.</description><pubDate>Sat, 12 Sep 2026 00:00:00 GMT</pubDate><category>astro</category><category>gitea</category><category>ci</category><category>hosting</category></item></channel></rss>