<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Blog on Statefulops</title>
		<link>https://www.statefulops.com/blog/</link>
		<description>Recent content in Blog on Statefulops</description>
		<generator>Hugo</generator>
		<language>en-GB</language>
		
		
		
		
			<lastBuildDate>Tue, 25 Aug 2026 14:33:34 +0000</lastBuildDate>
		
			<atom:link href="https://www.statefulops.com/blog/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>10: day 2 ops</title>
				<link>https://www.statefulops.com/blog/10-day-2-ops/</link>
				<pubDate>Tue, 26 May 2026 08:20:10 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/10-day-2-ops/</guid>
				<description>&lt;p&gt;Building a cluster is the easy part. Operating one for two years without losing data is the actual job. This last post in the series covers the three things that distinguish a cluster you can rely on from a cluster you’ve built: the upgrade workflow, etcd backups, and the failure modes you’ll meet in real use.&lt;/p&gt;</description>
			</item>
			<item>
				<title>9: storage ingress</title>
				<link>https://www.statefulops.com/blog/9-storage-ingress/</link>
				<pubDate>Tue, 26 May 2026 08:20:09 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/9-storage-ingress/</guid>
				<description>&lt;p&gt;The cluster is healthy and HA, but it’s not yet useful. Stateful workloads need somewhere to put their data, and HTTP workloads need a way to be reachable. This post installs &lt;code&gt;local-path-provisioner&lt;/code&gt; as a default StorageClass, &lt;code&gt;ingress-nginx&lt;/code&gt; as our ingress controller, and &lt;code&gt;cert-manager&lt;/code&gt; so we can pull Let’s Encrypt certificates without thinking about it.&lt;/p&gt;</description>
			</item>
			<item>
				<title>8: ha kube vip</title>
				<link>https://www.statefulops.com/blog/8-ha-kube-vip/</link>
				<pubDate>Tue, 26 May 2026 08:20:08 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/8-ha-kube-vip/</guid>
				<description>&lt;p&gt;One control plane node is enough for a working cluster. It is not enough for an HA cluster. If &lt;code&gt;cp-1&lt;/code&gt; goes down right now, every kubectl call hangs, no pods can be scheduled, and worker kubelets eventually start panicking about node leases they can’t renew. This post adds &lt;code&gt;cp-2&lt;/code&gt; and &lt;code&gt;cp-3&lt;/code&gt; to fix all of that.&lt;/p&gt;</description>
			</item>
			<item>
				<title>7: workers</title>
				<link>https://www.statefulops.com/blog/7-workers/</link>
				<pubDate>Tue, 26 May 2026 08:20:07 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/7-workers/</guid>
				<description>&lt;p&gt;One control plane node, one CNI, zero workers. Time to fix the third one. By the end of this post we’ll have &lt;code&gt;worker-1&lt;/code&gt; and &lt;code&gt;worker-2&lt;/code&gt; joined, Cilium running on them, and three nodes Ready. The mechanics are simple; the failure modes are where the interesting bits live.&lt;/p&gt;</description>
			</item>
			<item>
				<title>6: Cillium</title>
				<link>https://www.statefulops.com/blog/6-cillium/</link>
				<pubDate>Tue, 26 May 2026 08:20:06 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/6-cillium/</guid>
				<description>&lt;p&gt;At the end of post 5 we had a control plane running but &lt;code&gt;cp-1&lt;/code&gt; stuck in &lt;code&gt;NotReady&lt;/code&gt;. That’s because kubelet refuses to be Ready until a CNI plugin has installed itself into &lt;code&gt;/etc/cni/net.d/&lt;/code&gt; and can hand out pod IPs. This post installs Cilium, gets the node Ready, and gives us a working pod network with kube-proxy already replaced.&lt;/p&gt;</description>
			</item>
			<item>
				<title>5: Kubeadm init</title>
				<link>https://www.statefulops.com/blog/5-kubeadm-init/</link>
				<pubDate>Tue, 26 May 2026 08:20:05 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/5-kubeadm-init/</guid>
				<description>&lt;p&gt;This is the post where we actually get a Kubernetes API server running. After this, you’ll have a single-node control plane on &lt;code&gt;cp-1&lt;/code&gt; that’s already wearing its kube-vip VIP — ready for Cilium in post 6, workers in post 7, and the other two control plane nodes in post 8. The cluster won’t be highly available yet, but the certificates, the API endpoint, and the etcd configuration will all be HA-ready from the moment of birth.&lt;/p&gt;</description>
			</item>
			<item>
				<title>4: Install kubeadm</title>
				<link>https://www.statefulops.com/blog/4-install-kubeadm/</link>
				<pubDate>Tue, 26 May 2026 08:20:04 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/4-install-kubeadm/</guid>
				<description>&lt;p&gt;Three binaries, one apt repo, two gotchas. This is the shortest post in the series and one of the most consequential to get right, because the choice you make about which Kubernetes minor version to install in this step is the one you’ll live with until you decide to upgrade.&lt;/p&gt;</description>
			</item>
			<item>
				<title>3: Containerd</title>
				<link>https://www.statefulops.com/blog/3-containerd/</link>
				<pubDate>Tue, 26 May 2026 08:20:03 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/3-containerd/</guid>
				<description>&lt;p&gt;Kubelet doesn’t run containers directly. It talks over a socket to a container runtime, using the Container Runtime Interface (CRI). The runtime does the actual work — pulling images, creating namespaces and cgroups, calling out to &lt;code&gt;runc&lt;/code&gt; to start the process, and reporting back. &lt;code&gt;containerd&lt;/code&gt; is the runtime kubelet most commonly talks to these days, and it’s what we’re going to use.&lt;/p&gt;</description>
			</item>
			<item>
				<title>2: VM Prep</title>
				<link>https://www.statefulops.com/blog/2-vm-prep/</link>
				<pubDate>Tue, 26 May 2026 08:20:02 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/2-vm-prep/</guid>
				<description>&lt;p&gt;If post 1 was the setup, this post is the part where we make sure the VMs are actually ready to host a Kubernetes cluster. Nothing in this post is glamorous. All of it is the reason your cluster either comes up cleanly when you run &lt;code&gt;kubeadm init&lt;/code&gt;, or doesn’t.&lt;/p&gt;</description>
			</item>
			<item>
				<title>1: Why kubeadm and what are we building</title>
				<link>https://www.statefulops.com/blog/1-why-kubeadm-and-what-are-we-building/</link>
				<pubDate>Tue, 26 May 2026 08:20:01 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/1-why-kubeadm-and-what-are-we-building/</guid>
				<description>&lt;p&gt;Most “install Kubernetes” tutorials fall into one of two camps. There’s the five-minute k3s/kind crowd, where you run a single command and end up with something that boots but doesn’t behave like the cluster you’ll meet at work. And there’s the production-architecture-from-day-one crowd, where post one is a 4,000-word essay on etcd quorum before you’ve typed &lt;code&gt;kubeadm&lt;/code&gt;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Container Scanning on GitLab MRs with Trivy and Grype</title>
				<link>https://www.statefulops.com/blog/container-scanning-trivy-grype-gitlab/</link>
				<pubDate>Sun, 12 Apr 2026 17:53:26 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/container-scanning-trivy-grype-gitlab/</guid>
				<description>&lt;p&gt;In the &lt;a href=&#34;https://www.statefulops.com/blog/renovate-self-hosted-gitlab/&#34;&gt;previous post&lt;/a&gt; we got Renovate running on self-hosted GitLab CE, automatically discovering repos and raising MRs when new image versions are available. That handles the update workflow — but it doesn’t tell you whether the new image is actually safe to run. This post adds container scanning to the picture: every MR that touches a Docker Compose file gets scanned by both Trivy and Grype before it can merge.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Automated Dependency Updates on Self-Hosted GitLab with Renovate</title>
				<link>https://www.statefulops.com/blog/renovate-self-hosted-gitlab/</link>
				<pubDate>Sun, 12 Apr 2026 09:32:39 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/renovate-self-hosted-gitlab/</guid>
				<description>&lt;p&gt;If you’re running a self-hosted GitLab CE instance with a bunch of repos, keeping dependencies and Docker image tags up to date across all of them is a pain. Renovate solves this — it’s an open-source tool that opens MRs automatically when new versions are available. This post walks through setting it up from scratch on GitLab CE, including autodiscovery across a whole group and a 7-day hold before any MR gets raised.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Everything on StatefulOps — where to start</title>
				<link>https://www.statefulops.com/blog/start-here/</link>
				<pubDate>Wed, 25 Mar 2026 08:04:57 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/start-here/</guid>
				<description>&lt;p&gt;If you’ve landed here from a search or a shared link and you’re not sure where to start — this is the post for you.&lt;/p&gt;&#xA;&lt;p&gt;StatefulOps covers the stuff that actually matters when you’re running production Kubernetes: getting the fundamentals solid, wiring up GitOps so deployments stop being scary, and building the observability layer so you know what’s happening at 2am when things go sideways. Three series, 27 posts, all written for engineers who’d rather read something honest than wade through another docs-flavoured tutorial.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 09 — Observability in practice: debugging real incidents</title>
				<link>https://www.statefulops.com/blog/part-09-observability-in-practice-debugging-real-incidents/</link>
				<pubDate>Mon, 23 Mar 2026 08:32:04 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-09-observability-in-practice-debugging-real-incidents/</guid>
				<description>&lt;p&gt;Everything in this series has been building to this. You have Prometheus scraping metrics, Loki collecting logs, Tempo storing traces, and Grafana connecting them. You have dashboards, alerts, and runbooks. Now let’s use them.&lt;/p&gt;&#xA;&lt;p&gt;This post works through three real incident scenarios — the kind that happen in actual Kubernetes clusters, not contrived tutorial examples. Each one demonstrates the investigation workflow from first alert to root cause, using the actual queries you’d run. The scenarios get progressively more complex: a latency spike with no obvious cause, a slow memory leak leading to an OOMKill, and a cascading failure across multiple services.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 08 — The Grafana stack: putting it all together</title>
				<link>https://www.statefulops.com/blog/part-08-the-grafana-stack-putting-it-all-together/</link>
				<pubDate>Mon, 23 Mar 2026 08:29:51 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-08-the-grafana-stack-putting-it-all-together/</guid>
				<description>&lt;p&gt;The last six posts have covered the three pillars individually — Prometheus for metrics, Loki for logs, Tempo for traces. Each one is useful on its own. But the real leverage comes from using them together, connected in a way that lets you move fluidly between signals during an investigation without losing context.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 07 — Traces: following a request across services</title>
				<link>https://www.statefulops.com/blog/part-07-traces-following-a-request-across-services/</link>
				<pubDate>Mon, 23 Mar 2026 08:28:40 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-07-traces-following-a-request-across-services/</guid>
				<description>&lt;p&gt;Here’s the scenario that distributed tracing exists to solve. A user reports that checkout is slow — sometimes five seconds, sometimes twelve. Your error rate is fine. Your metrics look normal. CPU is healthy. Memory is healthy. Everything on every dashboard is green except that users are clearly experiencing something bad.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 06 — Logs: collecting, shipping, and querying</title>
				<link>https://www.statefulops.com/blog/part-06-logs-collecting-shipping-and-querying/</link>
				<pubDate>Mon, 23 Mar 2026 08:24:26 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-06-logs-collecting-shipping-and-querying/</guid>
				<description>&lt;p&gt;Metrics tell you something is wrong. Alerts wake you up about it. Then you need to understand what actually happened — and for that, you almost always end up in logs.&lt;/p&gt;&#xA;&lt;p&gt;Logs are the detailed record of what your application was doing at the time things went wrong. The error message. The stack trace. The specific request that triggered the failure. The sequence of events that led up to it. Metrics can tell you that error rate spiked at 14:32. Logs can tell you that at 14:32:07, pod &lt;code&gt;payments-api-6d8f9-xk2p&lt;/code&gt; started throwing &lt;code&gt;connection refused&lt;/code&gt; errors trying to reach &lt;code&gt;fraud-detection-svc:8080&lt;/code&gt;, and here are the request IDs of the forty-three affected transactions.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 05 — Alerting: being woken up for the right reasons</title>
				<link>https://www.statefulops.com/blog/part-05-alerting-being-woken-up-for-the-right-reasons/</link>
				<pubDate>Sun, 22 Mar 2026 17:02:26 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-05-alerting-being-woken-up-for-the-right-reasons/</guid>
				<description>&lt;p&gt;There’s a reliable way to tell if a team’s alerting is working well. Ask them how they feel about their on-call rotation. If the answer involves words like “exhausting,” “constant noise,” or “I stopped looking at alerts because most of them are false alarms” — the alerting is broken. Not broken in the sense of technically malfunctioning. Broken in the sense that it’s no longer doing its job.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 04 — Grafana: turning metrics into understanding</title>
				<link>https://www.statefulops.com/blog/part-04-grafana-turning-metrics-into-understanding/</link>
				<pubDate>Sun, 22 Mar 2026 17:00:20 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-04-grafana-turning-metrics-into-understanding/</guid>
				<description>&lt;p&gt;A common failure mode in Kubernetes observability goes like this. The team installs kube-prometheus-stack. They get Grafana. They open the pre-built dashboards and see hundreds of panels showing CPU, memory, network, disk, pod counts, container restarts, API server latency, etcd commit duration, and thirty other things. They feel like they have visibility. They add more panels for their own applications. The dashboard grows. It looks impressive on a monitor in the office.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 03 — PromQL: querying your metrics</title>
				<link>https://www.statefulops.com/blog/part-03-promql-querying-your-metrics/</link>
				<pubDate>Sun, 22 Mar 2026 16:59:08 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-03-promql-querying-your-metrics/</guid>
				<description>&lt;p&gt;Prometheus without PromQL is a database you can’t read. The metrics are there, the time series are accumulating, but until you can query them meaningfully you’re just storing numbers. PromQL is what turns those numbers into answers.&lt;/p&gt;&#xA;&lt;p&gt;It has a reputation for being difficult. That reputation is partly deserved — PromQL has some genuinely unintuitive moments — but it’s mostly unearned. The core concepts fit on one page, and once they click, you can write useful queries almost immediately. The complexity people run into is usually from jumping to advanced expressions before understanding the fundamentals.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 02 — Metrics: Prometheus from scratch</title>
				<link>https://www.statefulops.com/blog/part-02-metrics-prometheus-from-scratch/</link>
				<pubDate>Sun, 22 Mar 2026 16:56:55 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-02-metrics-prometheus-from-scratch/</guid>
				<description>&lt;p&gt;If you’ve spent any time around Kubernetes, you’ve heard of Prometheus. It shows up in helm charts, in monitoring stack recommendations, in job descriptions. It’s become so embedded in the Kubernetes ecosystem that it can be easy to assume you should just install it and move on without understanding what it actually does.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 01 — Why observability is different from monitoring</title>
				<link>https://www.statefulops.com/blog/part-01-why-observability-is-different-from-monitoring/</link>
				<pubDate>Sun, 22 Mar 2026 16:55:26 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-01-why-observability-is-different-from-monitoring/</guid>
				<description>&lt;p&gt;Here’s a scenario. It’s a Tuesday afternoon and your on-call phone goes off. The alert says “RabbitMQ queue depth exceeds 10,000 messages.” You look at your dashboard. Queue depth is definitely high. Memory usage looks elevated. CPU is fine. The alert doesn’t tell you why the queue is backing up — just that it is. You spend the next two hours digging through logs, checking deployment history, and asking colleagues if anything changed recently. Eventually you find it: a downstream service started timing out six hours ago, which caused consumers to slow down, which caused the queue to fill. The alert fired too late. The dashboard told you what but not why. And even finding the “why” required piecing together signals from three different places by hand.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 8 — Running GitOps at scale: multi-cluster and multi-team</title>
				<link>https://www.statefulops.com/blog/part-8-running-gitops-at-scale-multi-cluster-and-multi-team/</link>
				<pubDate>Fri, 20 Mar 2026 16:02:04 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-8-running-gitops-at-scale-multi-cluster-and-multi-team/</guid>
				<description>&lt;p&gt;Everything in this series so far has implicitly assumed a relatively simple setup: one team, one or a handful of clusters, one ArgoCD instance, a config repo with a reasonable number of applications. That setup works well and scales further than you’d think.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 7 — Progressive delivery: beyond all-at-once deploys</title>
				<link>https://www.statefulops.com/blog/part-7-progressive-delivery-beyond-all-at-once-deploys/</link>
				<pubDate>Fri, 20 Mar 2026 15:57:12 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-7-progressive-delivery-beyond-all-at-once-deploys/</guid>
				<description>&lt;p&gt;Rolling updates, which we covered back in the Kubernetes series, are a significant improvement over taking your service down to deploy a new version. But they have a blind spot: the moment the rollout completes, 100% of your traffic is hitting the new version. If that version has a subtle bug — one that only manifests under real production load, with real user data, at real traffic volumes — you find out the hard way.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 6 — Secrets in a GitOps workflow</title>
				<link>https://www.statefulops.com/blog/part-6-secrets-in-a-gitops-workflow/</link>
				<pubDate>Fri, 20 Mar 2026 15:53:20 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-6-secrets-in-a-gitops-workflow/</guid>
				<description>&lt;p&gt;Everything we’ve covered so far has a satisfying elegance to it. Desired state in Git. ArgoCD watching. Cluster reconciling. The whole system is auditable, reproducible, and self-healing.&lt;/p&gt;&#xA;&lt;p&gt;Then someone asks: “where do the database passwords go?”&lt;/p&gt;&#xA;&lt;p&gt;And the answer can’t be “in Git.” Not in plain text, anyway. Git repositories — even private ones — get cloned to developer laptops, backed up to third-party services, included in contractor access grants, and occasionally leaked. Committing a production database password to Git is not a theoretical risk; it’s a when-not-if.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 5 — Helm and Kustomize in a GitOps world</title>
				<link>https://www.statefulops.com/blog/part-5-helm-and-kustomize-in-a-gitops-world/</link>
				<pubDate>Fri, 20 Mar 2026 15:47:09 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-5-helm-and-kustomize-in-a-gitops-world/</guid>
				<description>&lt;p&gt;By now you have a Git repo, ArgoCD watching it, and a folder structure that separates environments. The next problem is one that every team hits within the first week: your payments API in dev and your payments API in production are almost identical, but not quite. Different image tags, different replica counts, different resource limits, different database hostnames.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 4 — Structuring your Git repository</title>
				<link>https://www.statefulops.com/blog/part-4-structuring-your-git-repository/</link>
				<pubDate>Fri, 20 Mar 2026 15:42:08 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-4-structuring-your-git-repository/</guid>
				<description>&lt;p&gt;Here’s a conversation that happens on almost every team adopting GitOps for the first time. You’ve got ArgoCD running, you’ve written your first Application, you’ve seen the sync work. Now someone asks: “where exactly does all this YAML live?”&lt;/p&gt;&#xA;&lt;p&gt;It’s a deceptively important question. Get the repo structure wrong early and you’ll spend months untangling it. Get it right and it scales from one service to hundreds without much additional ceremony.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 3 — ArgoCD: your first GitOps controller</title>
				<link>https://www.statefulops.com/blog/part-3-argocd-your-first-gitops-controller/</link>
				<pubDate>Fri, 20 Mar 2026 15:28:38 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-3-argocd-your-first-gitops-controller/</guid>
				<description>&lt;p&gt;The last two posts covered the theory. GitOps is a good idea. The four principles make sense. Now let’s talk about the software that actually implements it.&lt;/p&gt;&#xA;&lt;p&gt;ArgoCD is the most widely used GitOps controller for Kubernetes. It watches a Git repository, compares what’s there to what’s running in your cluster, and continuously reconciles the two. It also comes with a genuinely useful web UI — which, in a world of Kubernetes tooling that’s often CLI-only, is a pleasant surprise.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 2 — The four principles of GitOps</title>
				<link>https://www.statefulops.com/blog/part-2-the-four-principles-of-gitops/</link>
				<pubDate>Fri, 20 Mar 2026 15:25:07 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-2-the-four-principles-of-gitops/</guid>
				<description>&lt;p&gt;GitOps gets thrown around a lot as a buzzword, which means it gets applied to a lot of things that aren’t quite it. “We store our manifests in Git” — not quite. “Our CI pipeline runs kubectl apply” — closer, but still no. “We use ArgoCD” — probably, but not automatically.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Part 1 — What is GitOps and why does it exist?</title>
				<link>https://www.statefulops.com/blog/part-1-what-is-gitops-and-why-does-it-exist/</link>
				<pubDate>Fri, 20 Mar 2026 15:21:56 +0000</pubDate>
				<guid>https://www.statefulops.com/blog/part-1-what-is-gitops-and-why-does-it-exist/</guid>
				<description>&lt;p&gt;If you’ve been running Kubernetes for a while, you’ve probably settled into a deployment workflow that looks something like this. A developer finishes a feature. CI builds and pushes a new image. Someone — maybe a script, maybe a human — runs &lt;code&gt;kubectl apply&lt;/code&gt; against the cluster. The new pods roll out. Everyone moves on.&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
