<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Advanced on Tom Meurs</title>
    <link>https://tommeurs.nl/en/level/advanced/</link>
    <description>Recent content in Advanced on Tom Meurs</description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>© Tom Meurs</copyright>
    <lastBuildDate>Tue, 29 Sep 2026 09:00:00 +0200</lastBuildDate>
    <atom:link href="https://tommeurs.nl/en/level/advanced/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Supply Chain Security in GitLab CI: SBOM, cosign, and the Policy That Blocks</title>
      <link>https://tommeurs.nl/en/posts/supply-chain-security-gitlab-ci/</link>
      <pubDate>Tue, 29 Sep 2026 09:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/supply-chain-security-gitlab-ci/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; A pipeline that scans images and writes an SBOM, with nothing checking either at admission, had been green for a year and had stopped nothing. Four steps below: an SBOM Trivy can read back, a cosign signature on the digest with the key in Vault, the SBOM attached as an attestation, and a Kyverno policy that refuses the rest. Then a table of which SLSA Build L3 requirements a self-hosted GitLab runner meets, and the two it doesn&amp;rsquo;t.&lt;/p&gt;</description>
    </item>
    <item>
      <title>NIS2 for Platform Teams: Which Control Satisfies Which Article</title>
      <link>https://tommeurs.nl/en/posts/nis2-for-platform-teams/</link>
      <pubDate>Sun, 20 Sep 2026 21:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/nis2-for-platform-teams/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; The Cyberbeveiligingswet has applied since 15 August 2026. Below, the ten measures of &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Cbw art. 21: the duty of care. Appropriate and proportionate measures, of which paragraph 3 names ten.&#34;&gt;article 21&lt;/span&gt;&#xA; mapped onto controls that actually run, a Kyverno policy that proves one of them, the reporting deadlines as three Prometheus alerting rules, and the four duties no YAML covers. The first run of that policy found seven pods in my own cluster I&amp;rsquo;d have sworn were mirrored.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Building an Internal Developer Platform: Where to Start</title>
      <link>https://tommeurs.nl/en/posts/internal-developer-platform/</link>
      <pubDate>Wed, 06 May 2026 19:30:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/internal-developer-platform/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; An Internal Developer Platform is a thin layer between what a developer wants and what the cluster demands. Mine is a Helm base chart, ArgoCD and a handful of Kyverno policies, about 500 lines of my own code. Start with those three, and add observability and a portal only when the tickets tell you to.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Should you build an Internal Developer Platform? Usually, yes. It&amp;rsquo;s the how that wrecks teams.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Chaos Engineering: Breaking Your Cluster to Make It Stronger</title>
      <link>https://tommeurs.nl/en/posts/chaos-engineering-kubernetes/</link>
      <pubDate>Tue, 28 Apr 2026 19:45:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/chaos-engineering-kubernetes/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Chaos engineering is breaking your own cluster on purpose, with the hypothesis written down first and a rollback ready. I run it on the homelab with Litmus: a weekly pod-delete, a monthly node drain, a game day every quarter. So far it has found four things the dashboard would never have shown me.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;My dashboard is a wall of green. Pods running, replicas matched, CPU comfortable, no alerts firing. I look at it and get that small hit of &amp;ldquo;everything&amp;rsquo;s fine.&amp;rdquo; And mostly it is. The cluster&amp;rsquo;s been up for weeks and nothing has fallen over.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Longhorn vs Rook-Ceph: Kubernetes Storage Compared</title>
      <link>https://tommeurs.nl/en/posts/longhorn-vs-rook-ceph/</link>
      <pubDate>Mon, 20 Apr 2026 19:38:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/longhorn-vs-rook-ceph/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Longhorn and Rook-Ceph both give you replicated block storage that survives a node dying. Longhorn is one Helm install and a UI that shows you what a volume is doing. Rook-Ceph is Ceph with a Kubernetes operator around it, so you get block, object and filesystem storage, and the operations that come with them. I run Longhorn on three nodes. Below is why, and when I&amp;rsquo;d switch.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Run your first stateful workload on a self-hosted cluster and you find out fast that nobody hands out PersistentVolumes for free. There&amp;rsquo;s no cloud provider storage class to lean on. You&amp;rsquo;ve got your nodes, their disks, and a Postgres pod that won&amp;rsquo;t schedule. An hour of reading later you&amp;rsquo;re left with two names that keep coming up. &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Block storage for Kubernetes that replicates a volume across nodes. Simple to run and slower than Ceph, which is the trade it makes on purpose.&#34;&gt;Longhorn&lt;/span&gt;&#xA; and Rook-&lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;A distributed storage system that serves block, file and object from one cluster. Powerful, and heavy enough that running it is a job rather than a setting.&#34;&gt;Ceph&lt;/span&gt;&#xA;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Quantum-safe GPG identity with multiple aliases</title>
      <link>https://tommeurs.nl/en/posts/quantum-safe-gpg-identity/</link>
      <pubDate>Sat, 18 Apr 2026 10:47:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/quantum-safe-gpg-identity/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; One GPG masterkey that only certifies, kept offline. Three aliases bound to it, so anyone can check they&amp;rsquo;re the same person. Ed25519 subkeys for signing and SSH, an ML-KEM plus X448 subkey for encryption so recorded ciphertext stays closed after a quantum computer shows up, and three backups that each assume the other two are gone. Needs GnuPG 2.5.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A cryptographic signature still means exactly what it says. If the key is yours and the signature verifies, the content came from you. No vendor handed you that identity, no CA can pull it, no platform can suspend it. It exists because you generated the key, and it&amp;rsquo;s yours for as long as you hold the private half. Most of what gets called &amp;ldquo;online identity&amp;rdquo; is borrowed. A handle someone can ban, an email address the domain owner can take back the day they feel like it. A GPG signature sits outside all of that.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Alerting That Works: From Alert Fatigue to Actionable Notifications</title>
      <link>https://tommeurs.nl/en/posts/alerting-that-works/</link>
      <pubDate>Thu, 16 Apr 2026 20:15:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/alerting-that-works/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Most default alert rules page on a number with no user behind it, and after a week of that you stop reading pages. I alert on error rate, P99 latency, a disk that fills within 24 hours and a certificate under seven days, about ten rules in total, each with a runbook. Alertmanager sends critical to the pager, warning to Slack during office hours, info nowhere. Everything that doesn&amp;rsquo;t need a human gets deleted.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Cilium: eBPF Networking for Kubernetes</title>
      <link>https://tommeurs.nl/en/posts/cilium-ebpf-networking/</link>
      <pubDate>Wed, 08 Apr 2026 20:23:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/cilium-ebpf-networking/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Cilium is the CNI I run in every cluster. It replaces kube-proxy and its iptables chains with eBPF programs in the kernel, enforces network policies on identity and on HTTP paths, and Hubble shows me every flow and every drop. WireGuard between the pods, BGP to the switch, no sidecars. My production values are at the bottom.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The first time a service stopped resolving in one of my clusters, I spent an evening reading iptables chains. Hundreds of rules, generated by kube-proxy, evaluated top to bottom. I never found the problem. I restarted the node and it went away.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Thanos Remote Write: Push-Based Metrics for Edge and Multi-Cluster</title>
      <link>https://tommeurs.nl/en/posts/thanos-remote-write-push-architecture/</link>
      <pubDate>Fri, 27 Mar 2026 20:38:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/thanos-remote-write-push-architecture/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Thanos Receive turns the metrics flow around. Each edge Prometheus pushes samples over &lt;code&gt;remote_write&lt;/code&gt; to one central endpoint, buffers in its WAL when the link drops, and never sees an object storage credential. Below is the Receive StatefulSet, the hashring, the edge config, two ways to lock the endpoint down, and the hybrid I run with sidecars on the core clusters.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;In my &lt;a href=&#34;https://tommeurs.nl/en/posts/prometheus-thanos-metrics/&#34;&gt;previous post on Prometheus and Thanos&lt;/a&gt;, I set up the sidecar architecture. &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Gives Prometheus long-term storage in object storage and one query view across several Prometheus servers, without changing how Prometheus scrapes.&#34;&gt;Thanos&lt;/span&gt;&#xA; Sidecar runs next to Prometheus, uploads TSDB blocks to object storage, and exposes the data to the Querier over gRPC. For clusters in the same datacenter with a fat, stable link to the central infrastructure, I&amp;rsquo;ve got no complaints. Everything pulls, and everything can reach everything else.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Declarative Infrastructure as Compliance Documentation: Talos, NixOS, and Audit-Ready Systems</title>
      <link>https://tommeurs.nl/en/posts/declarative-infrastructure-compliance-certification/</link>
      <pubDate>Mon, 23 Mar 2026 19:28:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/declarative-infrastructure-compliance-certification/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Talos and NixOS boot from a file that says what the machine is. That file is what an ISO 27001, SOC 2 or NIS2 auditor asks for, so the evidence collection shrinks to a git log, a diff with no output and an inventory dump. Below is the mapping from config to control, the scripts, and the part that stays hard work.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;An ISO 27001 audit starts weeks before the auditor shows up, with someone collecting screenshots. Configuration panels, firewall rules, a dashboard that shows which patches went out. Then the Word documents that describe what the systems are supposed to do. Then the change tickets, dug out of the ticketing system, each one pointing at a &amp;ldquo;server maintenance&amp;rdquo; nobody can reconstruct six months later. Everyone treats this as the price of being in business. I did too, for years.&lt;/p&gt;</description>
    </item>
    <item>
      <title>NixOS vs Talos for Kubernetes Nodes: Two Flavors of Immutable Infrastructure</title>
      <link>https://tommeurs.nl/en/posts/nixos-vs-talos-kubernetes-nodes/</link>
      <pubDate>Sun, 15 Mar 2026 20:37:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/nixos-vs-talos-kubernetes-nodes/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Talos and NixOS are both declarative and both can be immutable, and that&amp;rsquo;s about where the resemblance stops. Talos runs Kubernetes and nothing else, with no shell to drift in. NixOS is a full Linux where the whole machine is one Nix config. I run Talos on my cluster nodes and NixOS on the machines around them, and this post is how I got to that split.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve written about &lt;a href=&#34;https://tommeurs.nl/en/posts/talos-linux-immutable-kubernetes/&#34;&gt;Talos Linux&lt;/a&gt; as the immutable Kubernetes OS, and I&amp;rsquo;ve compared &lt;a href=&#34;https://tommeurs.nl/en/posts/arch-vs-nixos-workstation/&#34;&gt;Arch vs NixOS for workstations&lt;/a&gt;. After both, the same question kept arriving in my inbox. Why not NixOS on the Kubernetes nodes too?&lt;/p&gt;</description>
    </item>
    <item>
      <title>Talos Linux: The Immutable Kubernetes OS That Changed How I Think About Nodes</title>
      <link>https://tommeurs.nl/en/posts/talos-linux-immutable-kubernetes/</link>
      <pubDate>Wed, 11 Mar 2026 21:15:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/talos-linux-immutable-kubernetes/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Talos Linux is a Linux that runs Kubernetes and nothing else. No SSH, no shell, no package manager. Every change goes through an API and a YAML file in Git. I&amp;rsquo;ve run my nodes on it for three years, and this post is how I stopped missing the shell.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The first time I tried to SSH into a &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;A Linux distribution with no shell, no SSH and no package manager, managed entirely through an API. The node is configuration rather than a machine you log into.&#34;&gt;Talos&lt;/span&gt;&#xA; node, I got nothing. No shell, no connection, not even a login prompt. I sat there for a minute wondering how I was supposed to debug a machine I couldn&amp;rsquo;t get into.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Data-Driven Kubernetes Migration: Why You Need Metrics Before You Move</title>
      <link>https://tommeurs.nl/en/posts/kubernetes-resource-sizing/</link>
      <pubDate>Sun, 08 Feb 2026 14:00:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/kubernetes-resource-sizing/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; An e-commerce client moved a PHP monolith to EKS in October, two weeks before Black Friday, with resource limits guessed from the old server specs. Mid-morning the pods hit their memory limit, died in a loop for four hours, and the estimate was €240,000 in lost revenue. The second half is how to get those numbers from load tests and VPA recommendations instead of guessing them.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&amp;ldquo;We want to migrate to Kubernetes by November.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>K8sGPT with a Local 70B Model on Apple Silicon</title>
      <link>https://tommeurs.nl/en/posts/k8sgpt-local-llm-cluster-management/</link>
      <pubDate>Thu, 05 Feb 2026 14:00:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/k8sgpt-local-llm-cluster-management/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; I pointed K8sGPT at a Llama 3.3 70B model running on a Mac Studio in my own office, so no cluster metadata leaves the network, and fed it three broken workloads. It found all three and wrote fixes I&amp;rsquo;d accept from a colleague, except for the resource numbers, which it made up. Assisted diagnosis works, at 25 seconds a query. Autonomous cluster management is still marketing, and the security section is why I wouldn&amp;rsquo;t let a model change anything on its own.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Runtime Security with Falco: Detect Suspicious Behavior in Your Cluster</title>
      <link>https://tommeurs.nl/en/posts/falco-runtime-security/</link>
      <pubDate>Thu, 07 Aug 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/falco-runtime-security/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Trivy and Kyverno check an image before it runs. Falco watches what the container does after that, syscall by syscall, and alerts when the behaviour doesn&amp;rsquo;t match a rule. This post goes from the one thing Falco does to the config I run, with the noise problem in between.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I scanned my images with &lt;a href=&#34;https://tommeurs.nl/en/posts/trivy-container-scanning/&#34;&gt;Trivy&lt;/a&gt;. I enforced policies with &lt;a href=&#34;https://tommeurs.nl/en/posts/kyverno-policies-governance/&#34;&gt;Kyverno&lt;/a&gt;. My workloads got cryptographic identity through &lt;a href=&#34;https://tommeurs.nl/en/posts/spiffe-spire-zero-trust/&#34;&gt;SPIFFE&lt;/a&gt;. Three layers of prevention, all green, and for a while that felt like enough.&lt;/p&gt;</description>
    </item>
    <item>
      <title>SPIFFE and SPIRE: Zero Trust Service Identity</title>
      <link>https://tommeurs.nl/en/posts/spiffe-spire-zero-trust/</link>
      <pubDate>Sat, 26 Jul 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/spiffe-spire-zero-trust/</guid>
      <description>&lt;p&gt;How does Service A know that Service B is actually Service B?&lt;/p&gt;&#xA;&lt;p&gt;I keep coming back to that question because the usual answer is uncomfortable. For years we trusted network location. Traffic from the right IP was legitimate, end of story. Zero trust took that assumption out back and shot it. Now every service has to prove who it is, every single request, no matter where it sits on the network.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kyverno Policies: Governance as Code for Kubernetes</title>
      <link>https://tommeurs.nl/en/posts/kyverno-policies-governance/</link>
      <pubDate>Mon, 14 Jul 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/kyverno-policies-governance/</guid>
      <description>&lt;p&gt;I used to keep a wiki page titled &amp;ldquo;Cluster conventions&amp;rdquo;. Resource limits on everything. No &lt;code&gt;:latest&lt;/code&gt; tags. No deploys in the &lt;code&gt;default&lt;/code&gt; namespace. It was a good page. Nobody read it. Six months in, half the cluster broke those rules and I only found out when something fell over.&lt;/p&gt;&#xA;&lt;p&gt;A rule that lives in a doc is a suggestion. A rule the API server refuses to accept is governance. That gap is the whole reason this post exists.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Progressive Delivery with Argo Rollouts: Canary and Blue-Green Deployments</title>
      <link>https://tommeurs.nl/en/posts/argo-rollouts-progressive-delivery/</link>
      <pubDate>Fri, 20 Jun 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/argo-rollouts-progressive-delivery/</guid>
      <description>&lt;p&gt;A standard Kubernetes Deployment had served me well for a long time. Push a new image tag, watch the pods roll, done. It was simple, it was declarative, and most of the time nothing went wrong. The rolling update even gave me a warm feeling of safety: old pods only get torn down once new ones are ready.&lt;/p&gt;&#xA;&lt;p&gt;That feeling is a lie. A rolling update protects you from pods that fail to start. It does nothing to protect you from pods that start perfectly and then serve broken responses. The container is healthy, the readiness probe is green, and your new code is quietly returning 500s to every single user. Within seconds, 100% of your traffic is hitting code that nobody validated under real load.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Drift Detection with ArgoCD: How to Know If Your Cluster Is Still in Sync</title>
      <link>https://tommeurs.nl/en/posts/argocd-drift-detection/</link>
      <pubDate>Sat, 03 May 2025 19:27:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/argocd-drift-detection/</guid>
      <description>&lt;p&gt;The whole pitch of GitOps is that Git is the source of truth. That promise holds right up until someone runs &lt;code&gt;kubectl edit&lt;/code&gt; on a deployment at 2am to stop an incident, a mutating webhook quietly rewrites a resource, or a half-finished sync leaves your cluster somewhere between what Git wanted and what it got. Now Git says one thing and the cluster does another, and nobody told you.&lt;/p&gt;&#xA;&lt;p&gt;That gap is configuration drift, and it is the part of GitOps people forget to defend. The good news: &lt;a href=&#34;https://argo-cd.readthedocs.io/&#34;&gt;ArgoCD&lt;/a&gt; already watches for it. The catch is that the defaults don&amp;rsquo;t do what you probably assume, and a few of them will bite you. This post walks from the simplest possible drift check up to the setup I actually run, one layer at a time. Stop wherever you have enough.&lt;/p&gt;</description>
    </item>
    <item>
      <title>App-of-Apps Pattern in ArgoCD: Scalable GitOps Architecture</title>
      <link>https://tommeurs.nl/en/posts/argocd-app-of-apps/</link>
      <pubDate>Mon, 21 Apr 2025 20:18:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/argocd-app-of-apps/</guid>
      <description>&lt;p&gt;My homelab started with one ArgoCD Application. Then a handful. The day I caught myself running &lt;code&gt;kubectl apply -f&lt;/code&gt; for the fifteenth time to register yet another Application, I knew I&amp;rsquo;d built the exact thing I was trying to avoid: manual steps I had to remember, in an order I had to remember, with no record of what should exist.&lt;/p&gt;&#xA;&lt;p&gt;The App-of-Apps pattern fixes that with one idea. You create a single root Application by hand, and it creates everything else. After a cluster wipe I can rebuild the whole thing with one &lt;code&gt;kubectl apply&lt;/code&gt;. That property is the entire reason I run it, and it&amp;rsquo;s the same reason I self-host in the first place: I want the repository to be the truth, not my memory.&lt;/p&gt;</description>
    </item>
    <item>
      <title>GitOps Disaster Recovery: Rebuilding Your Cluster from Git</title>
      <link>https://tommeurs.nl/en/posts/gitops-disaster-recovery/</link>
      <pubDate>Wed, 09 Apr 2025 19:52:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/gitops-disaster-recovery/</guid>
      <description>&lt;p&gt;For a couple of years my homelab cluster ran without much drama. Nodes came and went, workloads shifted around, and the worst I ever had to deal with was the occasional pod stuck in &lt;code&gt;CrashLoopBackOff&lt;/code&gt;. The kind of stable where you stop thinking about what happens if it all goes away.&lt;/p&gt;&#xA;&lt;p&gt;Then one evening I ran a &lt;code&gt;terraform destroy&lt;/code&gt; against the wrong workspace. By the time I noticed, the control plane was gone. Not degraded. Gone.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Running Kubernetes Offline: Edge Computing Without Internet</title>
      <link>https://tommeurs.nl/en/posts/kubernetes-offline-edge/</link>
      <pubDate>Tue, 04 Mar 2025 20:47:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/kubernetes-offline-edge/</guid>
      <description>&lt;p&gt;What happens when your Kubernetes cluster can&amp;rsquo;t reach the internet? I don&amp;rsquo;t mean a slow connection. I mean no connection at all. Ships at sea. Remote mining sites. Factory floors with air-gapped networks. Military deployments.&lt;/p&gt;&#xA;&lt;p&gt;For a lot of people that sounds exotic, like a problem someone else has. I treat it as a baseline design requirement, and I&amp;rsquo;ll explain why it makes my homelab better even though I almost never actually pull the cable.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Graceful Degradation in Kubernetes: What Happens When Components Fail</title>
      <link>https://tommeurs.nl/en/posts/kubernetes-graceful-degradation/</link>
      <pubDate>Thu, 20 Feb 2025 14:22:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/kubernetes-graceful-degradation/</guid>
      <description>&lt;p&gt;Everyone repeats the line that Kubernetes is self-healing. Pods die, they come back. Nodes drop, workloads reschedule. The system reconciles itself toward the state you declared, and most days you never have to think about it.&lt;/p&gt;&#xA;&lt;p&gt;Then one day the thing doing the healing is the thing that broke. The API server is down. etcd won&amp;rsquo;t respond. The scheduler is wedged. Now what?&lt;/p&gt;&#xA;&lt;p&gt;This is the question I actually care about, because &amp;ldquo;self-healing&amp;rdquo; is only useful if I understand its edges. I want to know what degrades gracefully and what takes the whole cluster with it. So I&amp;rsquo;ve run my clusters through a lot of failures on purpose: planned, unplanned, and a few &amp;ldquo;hold my beer&amp;rdquo; experiments on hardware I didn&amp;rsquo;t mind losing. Here is what actually happens when each piece breaks, and why most of it matters less than people fear.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How etcd Actually Works: The Heart of Your Kubernetes Cluster</title>
      <link>https://tommeurs.nl/en/posts/etcd-deep-dive/</link>
      <pubDate>Mon, 27 Jan 2025 21:47:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/etcd-deep-dive/</guid>
      <description>&lt;p&gt;When something goes wrong in Kubernetes, the trail usually leads back to etcd. API server timing out? Check etcd. Pods stuck in pending? Might be etcd. Cluster feels sluggish? Probably etcd.&lt;/p&gt;&#xA;&lt;p&gt;For a long time I treated etcd the way most operators do: as a black box that hums along next to the control plane. &amp;ldquo;The database.&amp;rdquo; You back it up and otherwise leave it alone. But black boxes feel like splinters to me, and the first time an etcd cluster fell over at 2am I realised I had no idea what I was actually looking at. So I learned. And it turns out the whole thing is built on a handful of ideas that, once they click, make most etcd problems diagnosable instead of terrifying.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes High Availability: Stacked vs External etcd Explained</title>
      <link>https://tommeurs.nl/en/posts/kubernetes-high-availability-etcd/</link>
      <pubDate>Wed, 15 Jan 2025 20:23:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/kubernetes-high-availability-etcd/</guid>
      <description>&lt;p&gt;The first &amp;ldquo;production&amp;rdquo; Kubernetes cluster I ran had a single control plane node. It hummed along happily for weeks, right up until a disk failed and took the whole thing with it. Every pod, every service, gone. That outage taught me what &amp;ldquo;single point of failure&amp;rdquo; actually feels like, and it pushed me toward a question that I keep seeing trip people up: when you build a cluster that survives node loss, do you run etcd on your control plane nodes, or on dedicated nodes of its own?&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
