<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Intermediate on Tom Meurs</title>
    <link>https://tommeurs.nl/en/level/intermediate/</link>
    <description>Recent content in Intermediate on Tom Meurs</description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>© Tom Meurs</copyright>
    <lastBuildDate>Tue, 06 Oct 2026 09:00:00 +0200</lastBuildDate>
    <atom:link href="https://tommeurs.nl/en/level/intermediate/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Talos Linux Vendor Lock-In: Five Questions, Run on My Own Nodes</title>
      <link>https://tommeurs.nl/en/posts/talos-linux-vendor-lock-in/</link>
      <pubDate>Tue, 06 Oct 2026 09:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/talos-linux-vendor-lock-in/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; I ran the five questions from my licence post on the OS under my own cluster. The licence holds. The &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; Linux vendor lock-in sits in two defaults that point at Sidero&amp;rsquo;s servers, the &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Business Source License 1.1: the source is published and free to use, except for the use the licensor names, usually a hosted service that competes with its own. Each release converts to an open source licence after a fixed number of years.&#34;&gt;BSL&lt;/span&gt;&#xA; code Sidero keeps adding around the &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Mozilla Public License 2.0: a file-level copyleft licence. Changes to MPL files must stay open when you distribute them, while code in other files may carry any licence.&#34;&gt;MPL&lt;/span&gt;&#xA; core, and a company Yardi has owned since 14 September.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Your Dependency&#39;s Business Model Is Part of Your Threat Model</title>
      <link>https://tommeurs.nl/en/posts/license-change-supply-chain-risk/</link>
      <pubDate>Wed, 23 Sep 2026 19:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/license-change-supply-chain-risk/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; A vendor freezing its free edition belongs in your risk register next to the expiring certificate. Thirty-six dated cases below, three kinds of event, and five questions to score your own stack. My own register had the Vault row missing for three years.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;On 11 September, Portainer&amp;rsquo;s CEO published &lt;a href=&#34;https://www.portainer.io/blog/portainer-3-0-is-coming&#34;&gt;&amp;ldquo;Portainer 3.0 is coming, and here&amp;rsquo;s what it means for you&amp;rdquo;&lt;/a&gt;. I read it the Monday after and shrugged.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s what it says. 3.0 is a Kubernetes-first codebase for enterprise customers, and &amp;ldquo;we are not cutting a separate CE build of it&amp;rdquo;. Community Edition stays on 2.x. 2.45 LTS &amp;ldquo;is the last release in the 2.x line&amp;rdquo;. A few days later they added a clarification: 2.45 keeps getting security fixes, bug fixes and back-ports of any 3.x feature that has a Docker API equivalent, &amp;ldquo;for as long as that LTS is supported&amp;rdquo;. The &lt;a href=&#34;https://docs.portainer.io/start/lifecycle&#34;&gt;lifecycle page&lt;/a&gt; puts that at May 2027. Want 3.x for free? There&amp;rsquo;s a Business Edition key called &lt;a href=&#34;https://www.portainer.io/take-3&#34;&gt;3 Nodes Free&lt;/a&gt;, one per organisation.&lt;/p&gt;</description>
    </item>
    <item>
      <title>KVM/QEMU Scripts and Aliases: Making Virtualization Actually Usable</title>
      <link>https://tommeurs.nl/en/posts/kvm-qemu-scripts-aliases/</link>
      <pubDate>Wed, 03 Jun 2026 21:17:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/kvm-qemu-scripts-aliases/</guid>
      <description>&lt;p&gt;I run VMs all day. A Kubernetes deployment I want to test, a distro I read about, Windows for that one stubborn application, an experiment I&amp;rsquo;d rather not run on my actual machine. KVM/QEMU is the right tool for all of it. It lives in the Linux kernel and runs close to native speed. Nothing in it phones home, and no vendor gets a say in what I do with my own hardware.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Infrastructure as Code for People Who Need to Understand</title>
      <link>https://tommeurs.nl/en/posts/infrastructure-as-code-control/</link>
      <pubDate>Sat, 30 May 2026 19:45:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/infrastructure-as-code-control/</guid>
      <description>&lt;p&gt;Someone opens a cloud console, clicks through a wizard, accepts a few defaults, and a resource appears. The dashboard goes green and everyone moves on. That&amp;rsquo;s still how a lot of infrastructure gets built in 2026, and I&amp;rsquo;ve built some of it that way myself.&lt;/p&gt;&#xA;&lt;p&gt;I can&amp;rsquo;t work like that for long. When I click a button and infrastructure appears, it feels borrowed. I want the configuration written down somewhere I can read it back, and I want to know why something exists, not only that it exists. The console gives me a green tick. It doesn&amp;rsquo;t give me any of that.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Backup Strategy for Your Homelab: The 3-2-1 Rule in Practice</title>
      <link>https://tommeurs.nl/en/posts/homelab-backup-strategy/</link>
      <pubDate>Mon, 18 May 2026 19:30:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/homelab-backup-strategy/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Three copies, two kinds of media, one of them offsite. Restic for files, Velero for the cluster, a Backblaze bucket for the offsite copy, and a restore test on a schedule. Mine costs about €5 a month, and the restore test is the part I still have to make myself do.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Picture the disk in your &lt;a href=&#34;https://tommeurs.nl/en/posts/k3s-cluster-setup/&#34;&gt;homelab&lt;/a&gt; dying tonight. Not corrupting, not throwing SMART warnings for a week first, just gone. The git server with every repo you care about, the password vault, fifteen years of family photos, the home automation that runs the house. All of it on a drive that&amp;rsquo;s decided it&amp;rsquo;s done.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Grafana Dashboards That Actually Get Used</title>
      <link>https://tommeurs.nl/en/posts/grafana-dashboards-that-work/</link>
      <pubDate>Sat, 02 May 2026 20:15:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/grafana-dashboards-that-work/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Each dashboard answers one question, and within five seconds I know whether I need to care. Four kinds of dashboard cover everything, variables and links tie them together, and the JSON lives in Git. My homelab runs five, and anything that goes a month unopened gets deleted.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;You have Grafana. You have &lt;a href=&#34;https://tommeurs.nl/en/posts/prometheus-thanos-metrics/&#34;&gt;Prometheus metrics&lt;/a&gt;. You have &lt;a href=&#34;https://tommeurs.nl/en/posts/loki-kubernetes-logging/&#34;&gt;logs in Loki&lt;/a&gt; and &lt;a href=&#34;https://tommeurs.nl/en/posts/tempo-distributed-tracing/&#34;&gt;traces in Tempo&lt;/a&gt;. The data&amp;rsquo;s all there.&lt;/p&gt;&#xA;&lt;p&gt;You also have 47 dashboards nobody opens.&lt;/p&gt;</description>
    </item>
    <item>
      <title>K3s Cluster Setup on Refurbished Mini-PCs</title>
      <link>https://tommeurs.nl/en/posts/k3s-cluster-setup/</link>
      <pubDate>Fri, 24 Apr 2026 21:12:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/k3s-cluster-setup/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Three refurbished mini-PCs, K3s with embedded etcd, kube-vip for one API address that survives a dead node, then MetalLB, Traefik and Longhorn on top. About €450 in hardware and an afternoon. Below are the commands, in the order I ran them.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Three mini-PCs sit on a shelf in my house. Between them they run my GitLab, my monitoring stack, home automation, file sync, a password manager, and a pile of other things I&amp;rsquo;d rather not hand to someone else&amp;rsquo;s computer. The whole setup cost less than one month of the managed Kubernetes bill for the same thing, and I know what every layer does because I put it there.&lt;/p&gt;</description>
    </item>
    <item>
      <title>cert-manager: Automatic TLS Certificates in Kubernetes</title>
      <link>https://tommeurs.nl/en/posts/cert-manager-kubernetes-tls/</link>
      <pubDate>Sun, 12 Apr 2026 21:38:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/cert-manager-kubernetes-tls/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; cert-manager watches Certificate resources, fetches the certificate from Let&amp;rsquo;s Encrypt, your own CA or Vault, stores it in a Secret and renews it 30 days before it expires. I run one DNS-01 ClusterIssuer through Cloudflare and one wildcard certificate, copy that Secret into the namespaces that need it, and let two Prometheus alerts tell me when a renewal is stuck. Nothing left to remember.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;For years my certificates renewed the way most people&amp;rsquo;s do. A calendar reminder, a manual &lt;code&gt;certbot&lt;/code&gt; run, and the hope that I&amp;rsquo;d get to it before the thing expired. It worked, right up to the morning a service started throwing cert errors at me and I had no idea why. The renewal cron had been failing for weeks. Quietly.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Distributed Tracing with Tempo and OpenTelemetry</title>
      <link>https://tommeurs.nl/en/posts/tempo-distributed-tracing/</link>
      <pubDate>Sat, 04 Apr 2026 19:47:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/tempo-distributed-tracing/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Tempo is where my traces go. The OpenTelemetry Collector receives spans from every service, tail sampling keeps every error and every slow request, and Tempo writes the rest to MinIO with three days of retention. From a span in Grafana I click through to the Loki lines with the same trace ID. My production values are at the bottom.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Your &lt;a href=&#34;https://tommeurs.nl/en/posts/prometheus-thanos-metrics/&#34;&gt;metrics&lt;/a&gt; say something is slow. Your &lt;a href=&#34;https://tommeurs.nl/en/posts/loki-kubernetes-logging/&#34;&gt;logs&lt;/a&gt; say errors happened. Fine. Which request failed, where did the latency come from, and which service in the chain ate the timeout? Neither of them can tell you.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Loki for Kubernetes Logging: The Prometheus-Like Approach</title>
      <link>https://tommeurs.nl/en/posts/loki-kubernetes-logging/</link>
      <pubDate>Tue, 31 Mar 2026 21:15:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/loki-kubernetes-logging/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Loki indexes labels and stores the log text as compressed chunks, so a month of logs fits on commodity hardware. The trade is full-text search. Below is the single-binary install I&amp;rsquo;d hand anyone first, the same chart on MinIO with three replicas, and the knobs I didn&amp;rsquo;t touch until the volume was real.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;You&amp;rsquo;ve got &lt;a href=&#34;https://tommeurs.nl/en/posts/prometheus-thanos-metrics/&#34;&gt;Prometheus for metrics&lt;/a&gt;, so you can already see what&amp;rsquo;s happening across your clusters. Metrics tell you request latency spiked at 14:32. They don&amp;rsquo;t tell you the payment service threw a null pointer because someone shipped a config change with a typo. For that you need the log line.&lt;/p&gt;</description>
    </item>
    <item>
      <title>NixOS as a Hypervisor: KVM and QEMU Can Do Everything</title>
      <link>https://tommeurs.nl/en/posts/nixos-hypervisor-kvm-qemu/</link>
      <pubDate>Thu, 19 Mar 2026 21:52:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/nixos-hypervisor-kvm-qemu/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; KVM and QEMU are the engine inside Proxmox and most of the cloud you rent. On NixOS, with libvirt on top, that engine is one block of config, and live migration, memory ballooning, GPU passthrough, SR-IOV and CPU pinning are each a command or an XML snippet away. What Proxmox adds is the dashboard and the delegation, and I&amp;rsquo;m honest below about how much that&amp;rsquo;s worth.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Ask around how to run a &amp;ldquo;real&amp;rdquo; hypervisor at home and you get the same shortlist. VMware, Hyper-V, or at minimum Proxmox. Something with a web UI, a clustering tab and a marketing page full of enterprise features. The shortlist is so well worn that running VMs without one of those products feels like cutting corners. I don&amp;rsquo;t blame anyone for that. The marketing is good.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Arch vs NixOS as a Workstation: Professional and Personal Use</title>
      <link>https://tommeurs.nl/en/posts/arch-vs-nixos-workstation/</link>
      <pubDate>Tue, 03 Mar 2026 21:28:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/arch-vs-nixos-workstation/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; I&amp;rsquo;ve run Arch and NixOS as my daily machine, and today it&amp;rsquo;s Arch on both desktops and NixOS on the servers. Arch&amp;rsquo;s reputation for breaking is mostly a missing snapshot. With btrfs, Snapper, dotfiles in git and a package list, a rollback takes 2 minutes and the fear of updates goes away. NixOS earns its learning curve when several machines have to stay identical, and on a server I wouldn&amp;rsquo;t run anything else.&lt;/p&gt;</description>
    </item>
    <item>
      <title>CTF and Forensics Skills That Make You a Better DevOps Engineer</title>
      <link>https://tommeurs.nl/en/posts/ctf-skills-for-devops/</link>
      <pubDate>Fri, 27 Feb 2026 19:52:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/ctf-skills-for-devops/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; I spend my evenings on Hack The Box and it&amp;rsquo;s made me better at my day job, which is platform engineering and has nothing to do with pentesting. The first ten minutes on a &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Capture The Flag: a security exercise where you break into a deliberately vulnerable system and prove it by retrieving a planted token, the flag.&#34;&gt;CTF&lt;/span&gt;&#xA; box and the first ten minutes on a broken production server are the same loop, with &amp;ldquo;flag&amp;rdquo; swapped for &amp;ldquo;incident&amp;rdquo;. Below is the toolkit that loop runs on, from one host up to a Kubernetes pod, with the CTF use and the work use side by side for every command.&lt;/p&gt;</description>
    </item>
    <item>
      <title>When not to use Kubernetes</title>
      <link>https://tommeurs.nl/en/posts/when-not-to-use-kubernetes/</link>
      <pubDate>Sat, 17 Jan 2026 10:00:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/when-not-to-use-kubernetes/</guid>
      <description>&lt;p&gt;I write a lot about Kubernetes, and I run it at home every day. I like it.&lt;/p&gt;&#xA;&lt;p&gt;So it might surprise you that I spend a fair amount of my time talking people out of it.&lt;/p&gt;&#xA;&lt;p&gt;The situation I keep walking into goes like this. A small team, one product, a roadmap full of features customers are waiting for, and someone has decided the first milestone is a Kubernetes cluster. Three nodes minimum, etcd, a &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Container Network Interface: the plugin contract Kubernetes uses to give a pod its network. Cilium, Calico and Flannel are implementations of it.&#34;&gt;CNI&lt;/span&gt;&#xA;, an ingress controller, cert-manager, a monitoring stack. Weeks of work before a single customer sees anything. Everyone nods along, because that&amp;rsquo;s what a serious setup is supposed to look like.&lt;/p&gt;</description>
    </item>
    <item>
      <title>YubiKey &#43; Pass &#43; GPG &#43; SSH: One Key to Rule Them All</title>
      <link>https://tommeurs.nl/en/posts/yubikey-pass-gpg-ssh/</link>
      <pubDate>Tue, 13 Jan 2026 17:00:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/yubikey-pass-gpg-ssh/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; One YubiKey carries a GPG key with a signing, an encryption and an authentication subkey. gpg-agent hands the authentication subkey to SSH, git signs with the signing subkey and pass decrypts with the encryption subkey, so one PIN in the morning and a touch per action replace every password I used to type. The SSH part alone is three steps and worth doing on its own.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Every morning I plug in my YubiKey, type one PIN, and that&amp;rsquo;s the last secret I type until I pull it out again. SSH to a server, no password. Sign a commit, no passphrase. Pull a secret out of pass, touch the key. One thing in a USB port, and the rest of the day my authentication happens without me.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Unbreakable - My Fascination.</title>
      <link>https://tommeurs.nl/en/posts/unbreakable/</link>
      <pubDate>Tue, 23 Dec 2025 20:00:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/unbreakable/</guid>
      <description>&lt;p&gt;As a kid I called the things that fascinated me &lt;strong&gt;unbreakable&lt;/strong&gt;. Indestructible never fit, because indestructible means it never breaks. Unbreakable means it keeps working &lt;em&gt;after&lt;/em&gt; it breaks.&lt;/p&gt;&#xA;&lt;p&gt;I know exactly when it started. A photo of an A-10 Thunderbolt II back from a mission with half a wing gone, the tail in tatters and the fuselage full of holes. It still brought its pilot home.&lt;/p&gt;&#xA;&lt;p&gt;Luck had nothing to do with it. Somebody sat at a drawing board years before that mission and decided the plane had to come home looking like that.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Prometheus and Thanos: Metrics at Scale</title>
      <link>https://tommeurs.nl/en/posts/prometheus-thanos-metrics/</link>
      <pubDate>Sun, 31 Aug 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/prometheus-thanos-metrics/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Prometheus on its own keeps a couple of weeks of metrics on local disk and can&amp;rsquo;t answer a question across clusters. Thanos puts a sidecar next to each Prometheus, ships the blocks to object storage, and puts a querier in front that reads all of it back through the same PromQL. Below is the install, the four components, the HA and multi-cluster wiring, and the numbers I run in production.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes RBAC: Least Privilege in Practice</title>
      <link>https://tommeurs.nl/en/posts/kubernetes-rbac-least-privilege/</link>
      <pubDate>Tue, 19 Aug 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/kubernetes-rbac-least-privilege/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Kubernetes ships with a permission model that hands every pod a token and lets the default service account collect rights nobody remembers granting. Least privilege means a service account per workload, no token unless the app talks to the API, and roles that name the namespace, the resource and the verb. Below are the four RBAC resources, the five settings that cap the blast radius, the bindings I copy most often, and how I check and enforce all of it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Vault for Beginners: Secrets Management in Kubernetes</title>
      <link>https://tommeurs.nl/en/posts/vault-beginners-kubernetes/</link>
      <pubDate>Wed, 02 Jul 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/vault-beginners-kubernetes/</guid>
      <description>&lt;p&gt;The first time I ran &lt;code&gt;kubectl get secret myapp -o yaml&lt;/code&gt; and base64-decoded the value, I felt my stomach drop. There was my database password, sitting in etcd, readable by anyone who could reach the API with the right RBAC. Kubernetes Secrets are not secrets. They&amp;rsquo;re base64-encoded plain text with a fancy name. That&amp;rsquo;s the default, and it&amp;rsquo;s the thing nobody warns you about on day one.&lt;/p&gt;&#xA;&lt;p&gt;Every cloud provider has a fix for sale. AWS has Secrets Manager, Google has Secret Manager, Azure has Key Vault. They work. The catch shows up later: the day you need to migrate, the day you want to know exactly what happens to a secret after you write it, the day you realise your most sensitive data lives in a system you can&amp;rsquo;t inspect.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Container Image Scanning with Trivy in Your CI Pipeline</title>
      <link>https://tommeurs.nl/en/posts/trivy-container-scanning/</link>
      <pubDate>Sun, 08 Jun 2025 10:00:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/trivy-container-scanning/</guid>
      <description>&lt;p&gt;Pull a base image, copy in your code, push it to production. That&amp;rsquo;s the loop most of us run on autopilot. The part nobody looks at is the base image itself, which quietly drags in a few hundred packages you never chose. Any one of them could carry a known &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;Common Vulnerabilities and Exposures: the public identifier for one specific vulnerability, in the form CVE-2026-1234. The identifier says nothing about severity.&#34;&gt;CVE&lt;/span&gt;&#xA;, and you&amp;rsquo;d have no idea.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Automating Semantic Versioning with GitLab CI</title>
      <link>https://tommeurs.nl/en/posts/semantic-versioning-gitlab/</link>
      <pubDate>Tue, 27 May 2025 19:44:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/semantic-versioning-gitlab/</guid>
      <description>&lt;p&gt;Picture the end state first. You merge a branch to main, walk away to get coffee, and by the time you&amp;rsquo;re back the project has a new version tag, a fresh changelog entry, and a GitLab release with notes written from your commits. Nobody decided &amp;ldquo;is this 1.4.0 or 1.3.5?&amp;rdquo; because nobody had to. The version fell out of the work itself.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s where I want to get to in this post. I run a self-hosted GitLab and I do not want versioning to be a thing I think about. A version number that someone picks by hand is a tiny black box: it depends on whether that person remembered the rules, was paying attention, and agreed with everyone else about what counts as a breaking change. I would rather the number be a consequence of the commits, something I can inspect and reproduce.&lt;/p&gt;</description>
    </item>
    <item>
      <title>GitLab CI for Kubernetes: From Commit to Deployment</title>
      <link>https://tommeurs.nl/en/posts/gitlab-ci-kubernetes/</link>
      <pubDate>Thu, 15 May 2025 20:33:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/gitlab-ci-kubernetes/</guid>
      <description>&lt;p&gt;For a long time my CI lived on someone else&amp;rsquo;s servers. Push code, wait for a hosted runner, watch the minutes tick down against a monthly quota, hope the provider doesn&amp;rsquo;t change the rules next quarter. It worked, right up until I started asking where my source code actually sat while it was being built, and who else could see it.&lt;/p&gt;&#xA;&lt;p&gt;So I moved GitLab in-house. I run it self-hosted now, on my own hardware, next to the clusters it deploys to. That&amp;rsquo;s &lt;a href=&#34;https://tommeurs.nl/en/posts/sovereign-infrastructure/&#34;&gt;sovereignty&lt;/a&gt; applied to CI/CD: my code, my builds, my artifacts, no vendor that can change pricing, deprecate a feature I depend on, or read my repositories without me knowing.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD vs Flux: When to Choose What</title>
      <link>https://tommeurs.nl/en/posts/argocd-vs-flux/</link>
      <pubDate>Fri, 28 Mar 2025 21:33:00 +0200</pubDate>
      <guid>https://tommeurs.nl/en/posts/argocd-vs-flux/</guid>
      <description>&lt;p&gt;&amp;ldquo;Should I use ArgoCD or &lt;span class=&#34;def&#34; tabindex=&#34;0&#34; data-def=&#34;The other GitOps engine next to ArgoCD. Controllers in the cluster reconcile from Git with no UI of its own, where ArgoCD leads with one.&#34;&gt;Flux&lt;/span&gt;&#xA;?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;I get this question over coffee, in Slack, in PR comments, probably once a week. People want me to declare a winner so they can stop thinking about it and go build something.&lt;/p&gt;&#xA;&lt;p&gt;Here is my bias, up front, so you can weigh everything that follows: I run ArgoCD on my own clusters. I have a few years of it under my belt and I like it. That preference is going to leak through no matter how careful I am, so I am telling you now instead of pretending I am neutral.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ArgoCD for Beginners: Your First GitOps Deployment</title>
      <link>https://tommeurs.nl/en/posts/argocd-beginners/</link>
      <pubDate>Sun, 16 Mar 2025 19:15:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/argocd-beginners/</guid>
      <description>&lt;p&gt;Here is the thing I never want to do again: ask a cluster what it is running and not trust the answer. For years that was normal. Someone ran &lt;code&gt;kubectl apply&lt;/code&gt;, maybe from a laptop, maybe from a CI job nobody could find anymore, and the live state slowly drifted away from anything written down. When I switched to GitOps, that whole category of uncertainty disappeared. I push to Git, and the cluster converges to match. If I want to know what is deployed, I read a file.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes Network Policies: A Visual Guide to Pod Security</title>
      <link>https://tommeurs.nl/en/posts/kubernetes-network-policies-visual/</link>
      <pubDate>Sat, 08 Feb 2025 19:34:00 +0100</pubDate>
      <guid>https://tommeurs.nl/en/posts/kubernetes-network-policies-visual/</guid>
      <description>&lt;p&gt;Picture this: an attacker pops a single pod in your cluster, maybe through a vulnerable image or a leaked token. From that one foothold, they can reach every database, every internal API, every secret-fetching sidecar you run. Nothing stops them, because by default nothing tries to.&lt;/p&gt;&#xA;&lt;p&gt;Network Policies are the thing that stops them. They turn &amp;ldquo;one compromised pod&amp;rdquo; into &amp;ldquo;one compromised pod, and that&amp;rsquo;s it.&amp;rdquo; Everyone knows they should use them. Almost nobody actually does, because the YAML looks scary and the behaviour is weird until the mental model clicks.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
