<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Mert Öztürk</title>
    <description>Engineer focused on cloud-native infrastructure, Kubernetes, and observability. Design enthusiast and maker.</description>
    <link>https://mert.cc</link>
    <atom:link href="https://mert.cc/rss.xml" rel="self" type="application/rss+xml" />
    <image>
      <url>https://mert.cc/apple-icon.png</url>
      <title>Mert Öztürk</title>
      <link>https://mert.cc</link>
    </image>
    <language>en-US</language>
    <lastBuildDate>Tue, 29 Sep 2026 17:41:33 GMT</lastBuildDate>
    <item>
      <title>What I learned writing my first kubectl plugin</title>
      <link>https://mert.cc/blog/2026/09/29/what-i-learned-writing-my-first-kubectl-plugin</link>
      <guid isPermaLink="true">https://mert.cc/blog/2026/09/29/what-i-learned-writing-my-first-kubectl-plugin</guid>
      <pubDate>Tue, 29 Sep 2026 17:41:33 GMT</pubDate>
      <dc:creator>Mert Öztürk</dc:creator>
      <description>A week reading other people&apos;s kubectl plugins, a probes plugin I vibe coded, a dashboard I built and deleted, and what the Kubernetes API taught me.</description>
      <content:encoded><![CDATA[
<figure><img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/hero-light.png" alt="" /></figure>
<p>I&#x27;ve been running workloads on Kubernetes at work for years now, and probes are everywhere. Every Deployment has them (or should), every Helm chart ships its own opinion about them, and sooner or later every incident call has someone asking &quot;wait, what&#x27;s the liveness probe on this thing?&quot;. Add Kubestronaut prep on top of that, and lately I&#x27;m under the impression that I spend more time reading, tuning and troubleshooting probe configs than writing them.</p>
<p>So I wanted to find out whether I could build a neat, handy little tool for it. That tool is <a href="https://github.com/mertdotcc/kubectl-probes">kubectl probes</a>, and it&#x27;s my first kubectl plugin.</p>
<p>This post isn&#x27;t the pitch; the <a href="https://mert.cc/projects/kubectl-probes">project page</a> does that. This one is about how it got built: a week of reading someone else&#x27;s code, vibe coding my own, a dashboard I got way too excited about and then deleted, and a few things about the Kubernetes API that look different once you have to write code against them.</p>
<h2 id="a-handful-of-numbers">A handful of numbers</h2>
<p>A probe is a handler and five numbers. What those numbers add up to is never on your screen. <code>periodSeconds: 30</code> with <code>failureThreshold: 3</code> looks harmless. It&#x27;s also up to 90 seconds of traffic going to a container that is already broken. Not that I&#x27;d know anything about that. 🙃</p>
<p>Here&#x27;s what <code>kubectl describe</code> gives you for the Postgres pod in my demo cluster:</p>
<pre><code class="language-text">$ kubectl -n shop describe pod postgres-0 | grep -E &quot;^ +(Liveness|Readiness):&quot;
    Liveness:   exec [pg_isready -U shop] delay=45s timeout=30s period=20s successThreshold=1 failureThreshold=3
    Readiness:  exec [pg_isready -U shop -d shop] delay=0s timeout=3s period=5s successThreshold=1 failureThreshold=3
</code></pre>
<p>All the facts are there. What isn&#x27;t there: that liveness waits 45 seconds before its first check, that its 30-second timeout is longer than its own 20-second period, and that once Postgres stops answering, nothing restarts it for a full minute. Now do that arithmetic for every container in a namespace, once per replica, with the <code>Unhealthy</code> events sitting in a separate section of every single pod.</p>
<p>It&#x27;s not a hard problem, IMHO. It&#x27;s a tedious one, which makes it a perfect job for a small tool.</p>
<h2 id="the-plugins-i-live-in">The plugins I live in</h2>
<p>I use a bunch of kubectl plugins every day, and the two at the top of the list are both by <a href="https://ahmet.im/?ref=mert.cc">Ahmet Alp Balkan</a>: <a href="https://github.com/ahmetb/kubectx?ref=mert.cc">kubectx</a> (and its sibling <code>kubens</code>), which I&#x27;ve been using for 8 years now, and <a href="https://github.com/ahmetb/kubectl-node_resource?ref=mert.cc">kubectl node-resource</a>, for when I want to know how full my nodes actually are.</p>
<p>What I like about Ahmet&#x27;s plugins is that they&#x27;re clear, well scoped and efficient. <mark>They do one thing and they do it well.</mark> No config file, no TUI, no dashboard. You type a command, you get an answer, you move on. That was the bar I had in my head.</p>
<h2 id="a-week-inside-someone-elses-plugins">A week inside someone else&#x27;s plugins</h2>
<p>Before writing a single line of my own, I took a week to properly understand how these work. I used Matt Pocock&#x27;s <a href="https://github.com/mattpocock/skills?ref=mert.cc">skills</a> for Claude Code, especially <code>/teach</code>. If you haven&#x27;t tried it: you give it a mission (&quot;I want to understand how a kubectl plugin talks to the cluster, well enough to write one&quot;) and it turns a folder into a small course, with lessons, reference sheets, and learning records of what you actually understood, so every session starts where the last one left off.</p>
<p>I pointed it at kubectx and node-resource and went through both recursively, from <code>main</code> down to the last API call and back up to the output. Some of what I took away:</p>
<ul>
  <li><strong>Scope is a feature.</strong> kubectx is <code>kubectx [name]</code>: a few flags, one optional positional, nothing else. kubectl probes ended up shaped exactly like that: <code>kubectl probes</code> for a namespace, <code>kubectl probes deploy/web</code> for one workload. No subcommands to remember.</li>
  <li><strong>Don&#x27;t reinvent the flags.</strong> node-resource builds on <code>k8s.io/cli-runtime</code>, which gives you <code>--context</code>, <code>--kubeconfig</code>, <code>-n</code>, <code>--as</code> and friends for free, parsed exactly the way kubectl parses them. Nobody has to learn anything new to use your plugin.</li>
  <li><strong>Borrow the good bits.</strong> node-resource can end its output with a summary, or print only that with <code>--summary=only</code>: a distribution with histograms, so you see the whole cluster at a glance. I liked it so much that kubectl probes has the same flag, the same six percentile rows, and the same kind of histogram. You&#x27;ll see it further down.</li>
</ul>
<p>Only once I felt like I could explain those two codebases to someone else did I start on mine.</p>
<h2 id="then-i-vibe-coded-it-with-some-paperwork">Then I vibe coded it (with some paperwork)</h2>
<p>Let&#x27;s be honest: I vibe coded kubectl probes. Claude Code wrote most of the Go. ¯\_(ツ)_/¯</p>
<p>But I didn&#x27;t want it to become the kind of vibe-coded project where nobody, including me, knows why anything is the way it is. So I leaned on Matt&#x27;s skills again, and the repo has the paperwork to show for it:</p>
<ul>
  <li><strong>A glossary</strong> (<code>CONTEXT.md</code>) with the words the plugin uses and the ones it avoids. A <em>workload</em> is the top-most owner of a set of pods, a <em>finding</em> is an opinion and never a fact, and so on. Sounds pedantic, but it&#x27;s why the output, the docs and the code all say the same thing.</li>
  <li><strong>ADRs</strong>, 10 of them so far, for every decision I&#x27;d otherwise re-argue with myself a week later: the plugin is read-only, it trusts the running pod&#x27;s spec over the template, a set of <code>-f</code> files is read as one cluster...</li>
  <li><strong>Issues as tickets.</strong> Every piece of work started as a GitHub issue, small enough to review in one sitting.</li>
  <li><strong>Golden-file tests</strong> that run the whole pipeline against fixtures, no cluster needed.</li>
  <li><strong>A demo cluster</strong> in the repo, on kind or minikube, where every rule has something to fire on, plus a <code>make chaos</code> that breaks two probes on purpose. It was the release gate, and yes, it caught bugs before v0.1.0.</li>
</ul>
<p>It was supposed to be a little tool. It didn&#x27;t stay little for long, which brings me to...</p>
<h2 id="the-dashboard-i-built-and-then-deleted">The dashboard I built, and then deleted</h2>
<p>I got ahead of myself. As I tend to do.</p>
<p>Sam Rose wrote a <a href="https://ngrok.com/blog/probes?ref=mert.cc">gorgeous interactive essay about probes</a> for ngrok: a kubelet, pods, containers changing colour, every probe check pulsing across the screen. I read it and thought: what if kubectl probes had that, but for your real cluster? 🔥</p>
<p>So on a Sunday afternoon I vibed it. <code>kubectl probes --serve</code> started a local web server, streamed the report to the browser over Server-Sent Events, drew the pods and the kubelet as a diagram, and had a timeline you could scrub. It was fun to build. It even worked.</p>
<p>Then I ran it against the demo cluster mid-chaos, and it hit me: a real cluster doesn&#x27;t give you those pulses. That essay runs on <a href="https://github.com/ngrok/webernetes?ref=mert.cc">webernetes</a>, a Kubernetes that lives in the browser, with a simulated cluster and a simulated clock. In a real one, a probe that passes leaves no trace in the API server at all, and a failing one shows up as a deduplicated, rate-limited event (more on that below). The only honest thing my dashboard could draw was what the terminal already printed, just slower and prettier. It showed exactly two things the CLI couldn&#x27;t: which node the failures were on, and whether they bunched up in time.</p>
<p>Monday morning I took it out of the release and hid it behind a build tag, &quot;just in case&quot;. Monday night I deleted it. About 6,000 lines, gone. There&#x27;s even an ADR whose whole point is that there is no dashboard. 😅</p>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/meme-expanding-brain.png" alt="Expanding brain meme, four rows: a kubectl plugin that prints a table; with a Summary at the end, like node-resource; and a live browser Dashboard streaming over SSE, with a Timeline you can scrub; and, with the brightest brain, git rm -r internal/serve (−6,000 lines)" loading="lazy"/>
  <figcaption>Scope, as I experienced it that week</figcaption>
</figure>
<p>I struggled with scoping on this one, and even more with sticking to the scope once I&#x27;d set it. What made me want a plugin in the first place was Ahmet&#x27;s &quot;one thing, done well&quot;, and I almost built the exact opposite. <mark>A browser surface is a second product.</mark> And hiding it behind a build tag &quot;just in case&quot; didn&#x27;t make it free: it still cost CI time and attention on every single change, for something nobody would ever run.</p>
<h2 id="what-it-looks-like">What it looks like</h2>
<p>Everything below is the demo cluster from the repo, running on minikube, right after <code>make chaos</code> pointed <code>web</code>&#x27;s readiness probe and <code>catalog</code>&#x27;s liveness probe at paths that answer 404.</p>
<h3 id="worst-first">Worst first</h3>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/overview.png" alt="kubectl probes -n shop --sort severity --summary=hide: one row per container with its startup, readiness and liveness timing, ready pods, restarts, failures and findings; web is 2/3 ready with a drift marker on its readiness probe" loading="lazy"/>
  <figcaption>kubectl probes -n shop --sort severity --summary=hide</figcaption>
</figure>
<p>One row per container. Each probe column shows the handler and the one duration that probe is read for: the whole budget for startup, and for readiness and liveness the worst case between the container going bad and the probe acting on it. <code>--sort severity</code> puts what&#x27;s failing at the top.</p>
<p>The row to look at is <code>web</code>. The Deployment asks for 2 replicas, and there are 3 pods: the 2 old ones, plus 1 new one the rollout added, which isn&#x27;t ready. And there&#x27;s a <code>*</code> on its readiness cell, which means the running pods no longer match their own template.</p>
<h3 id="the-rollout-that-stalled-quietly">The rollout that stalled, quietly</h3>
<p>This is my favourite one, because it&#x27;s the sneakiest. Changing <code>web</code>&#x27;s readiness path started a rollout. The new pod runs the new probe, fails it, and never becomes ready, so the rollout just... stops there. The two old pods keep serving on the old probe. Nothing restarts, nothing crashes. And kubectl says:</p>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/get-deploy-web.png" alt="kubectl -n shop get deploy web: READY 2/2, UP-TO-DATE 1, AVAILABLE 2" loading="lazy"/>
  <figcaption>kubectl -n shop get deploy web</figcaption>
</figure>
<p><code>READY 2/2</code>. The only hint is <code>UP-TO-DATE 1</code>.</p>
<p>It doesn&#x27;t get better with time, either. Ten minutes in (<code>progressDeadlineSeconds</code> defaults to 600), the Deployment&#x27;s <code>Progressing</code> condition flips to <code>False</code> with <code>ProgressDeadlineExceeded</code>, and that&#x27;s it. Kubernetes <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/?ref=mert.cc#failed-deployment">takes no action on a stalled Deployment</a> other than reporting that condition, so nothing rolls back, and <code>get deploy</code> keeps saying <code>2/2</code>.</p>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/meme-this-is-fine.png" alt="The This Is Fine dog sitting in a burning room, captioned kubectl get deploy web → READY 2/2" loading="lazy"/>
  <figcaption>Based on <a href="https://kcgreendotcom.com/?ref=mert.cc">KC Green</a>&#x27;s <em>On Fire</em></figcaption>
</figure>
<p>Naming the workload puts all the pieces side by side:</p>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/web.png" alt="kubectl probes -n shop deploy/web: three pods, one not ready for 13 minutes, a Drift table with /readyz running and /readyz-broken in the template, and 124 readiness failures with status code 404" loading="lazy"/>
  <figcaption>kubectl probes -n shop deploy/web</figcaption>
</figure>
<p>The <code>*</code> on the readiness handler and the drift table say what changed: <code>/readyz</code> in the running pods, <code>/readyz-broken</code> in the template. The runtime state names the one pod that isn&#x27;t ready, and the failure evidence carries the kubelet&#x27;s own message: status code 404, 124 times in 13 minutes.</p>
<h3 id="killed-by-liveness-completed-exit-0">Killed by liveness, &quot;Completed, exit 0&quot;</h3>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/catalog.png" alt="kubectl probes -n shop deploy/catalog: liveness on /healthz-broken, 3 restarts, a last termination of Completed with exit 0, 9 liveness failures with status code 404, and three findings" loading="lazy"/>
  <figcaption>kubectl probes -n shop deploy/catalog</figcaption>
</figure>
<p><code>catalog</code>&#x27;s liveness probe fails 3 times in a row, the kubelet restarts the container, and the last termination reads... <code>Completed, exit 0</code>. That&#x27;s because the kubelet stops a container that failed liveness the same way it stops any container: SIGTERM and a grace period. An app that shuts down cleanly exits 0. So nothing about <em>how</em> the container exited tells you <em>why</em>. The failure evidence right below it does: the liveness probe, and the 404 it got.</p>
<p>The other two findings were there before anything broke: without a readiness probe the container counts as ready the moment it starts, and its liveness probe gives it the default 1 second to answer.</p>
<h3 id="the-pod-that-looks-perfectly-healthy">The pod that looks perfectly healthy</h3>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/postgres.png" alt="kubectl probes -n shop sts/postgres: ready with no restarts, one readiness failure from startup 16 hours ago, and findings about a fixed liveness delay standing in for a startup probe and a timeout longer than its period" loading="lazy"/>
  <figcaption>kubectl probes -n shop sts/postgres</figcaption>
</figure>
<p><code>postgres</code> is ready and has never restarted. The findings are about its config, and I&#x27;ll be honest: I&#x27;ve written this exact config before. More than once. Here are its probes from the manifest:</p>
<pre><code class="language-yaml">readinessProbe:
  exec:
    command: [&quot;pg_isready&quot;, &quot;-U&quot;, &quot;shop&quot;, &quot;-d&quot;, &quot;shop&quot;]
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 3
livenessProbe:
  exec:
    command: [&quot;pg_isready&quot;, &quot;-U&quot;, &quot;shop&quot;]
  initialDelaySeconds: 45
  periodSeconds: 20
  timeoutSeconds: 30
  failureThreshold: 3
</code></pre>
<p>Somebody measured that Postgres takes a while to come up and wrote that wait into the liveness probe: <code>initialDelaySeconds: 45</code>. That&#x27;s the liveness probe doing the startup probe&#x27;s job. It switches liveness off for the first 45 seconds of every start, even when Postgres comes up in a couple of seconds. And if a start ever runs long, say a slow recovery after a crash, liveness has its 3 failures within about 105 seconds (45 + 3 × 20) and restarts Postgres in the middle of it, which starts the slow recovery all over again.</p>
<p>The <a href="https://kubernetes.io/docs/concepts/configuration/liveness-readiness-startup-probes/?ref=mert.cc">Kubernetes docs</a> say it directly: if your container usually takes longer to start than <code>initialDelaySeconds + failureThreshold × periodSeconds</code>, add a startup probe that checks the same thing. Liveness and readiness don&#x27;t run until the startup probe succeeds, so you can give startup a generous budget (<code>failureThreshold × periodSeconds</code>, say 5 minutes) and keep liveness tight for the rest of the container&#x27;s life.</p>
<p>The second finding is the 30-second timeout on a 20-second period. The kubelet runs one check at a time and doesn&#x27;t start the next one while the last is still going, so if a check ever does hang for the full 30 seconds, failure takes longer to detect than the <code>1m</code> the numbers suggest.</p>
<h3 id="the-summary-aka-my-node-resource-homage">The summary, a.k.a. my node-resource homage</h3>
<figure>
  <img src="https://mert.cc/blog/what-i-learned-writing-my-first-kubectl-plugin/summary.png" alt="kubectl probes -A --summary=only: probe coverage by type, then readiness and liveness failure detection at P100, P99, P90, P50, P10 and P0 with the container named at each, and a histogram of each" loading="lazy"/>
  <figcaption>kubectl probes -A --summary=only</figcaption>
</figure>
<p>This is the part I borrowed from <a href="https://github.com/ahmetb/kubectl-node_resource?ref=mert.cc">node-resource</a>. Coverage first: how many containers have each kind of probe. Then failure detection as percentiles, each naming its container, so the outlier is one command away. In this cluster the slowest liveness detection belongs to the control plane&#x27;s own <code>kube-scheduler</code>, at 1m20s.</p>
<h2 id="what-i-learned">What I learned</h2>
<p>None of the following are secrets, and I knew all of them from running clusters. But writing code against something is a different game: every &quot;yeah, I know&quot; turned into &quot;okay, but what <em>exactly</em> happens when...&quot;.</p>
<h3 id="a-passing-probe-leaves-no-trace">A passing probe leaves no trace</h3>
<p>The kubelet runs the probes, and it only tells the API server when something happens. A probe that passes leaves nothing you could ever <code>kubectl get</code>. A probe that fails becomes an <code>Unhealthy</code> Event, and Events are lossy in ways that matter once you build on top of them. Here&#x27;s that same stalled <code>web</code> pod, about an hour into the chaos:</p>
<pre><code class="language-text">$ kubectl -n shop get events --field-selector reason=Unhealthy,involvedObject.name=web-58b7b6f6f9-n25b2 -o custom-columns=FIRST:.firstTimestamp,LAST:.lastTimestamp,COUNT:.count
FIRST                  LAST                   COUNT
2026-09-29T13:37:04Z   2026-09-29T14:32:04Z   678
</code></pre>
<p>A readiness probe failing every 5 seconds for 55 minutes, stored as <strong>one</strong> Event object with a counter. It gets better: client-go&#x27;s event recorder, which the kubelet uses, rate-limits per object. It lets 25 events through, then writes at most once every 5 minutes, carrying the running count with it. That&#x27;s why the screenshot above said &quot;last seen 3m7s ago&quot; for a probe that fails every 5 seconds. And then Events expire, after 1 hour by default (the API server&#x27;s <code>--event-ttl</code>).</p>
<blockquote>
  <p><strong>Careful</strong></p>
  <p>So the <code>FAILURES</code> column is recent history, not all history. <strong>Zero means nothing has failed recently, not that nothing has ever failed.</strong> The plugin&#x27;s README says so in bold, because I&#x27;d want someone to tell me.</p>
</blockquote>
<p>This is also what killed the dashboard, as you saw. You can&#x27;t animate data that was never recorded.</p>
<h3 id="controllers-create-down-owners-point-up">Controllers create down, owners point up</h3>
<p>You know the hierarchy: a Deployment has ReplicaSets, a ReplicaSet has Pods. What&#x27;s worth being precise about is which way the API records that relationship. The controllers work top-down: the Deployment controller creates a ReplicaSet, the ReplicaSet controller creates Pods. But the link is written on the <em>child</em>, pointing <em>up</em>:</p>
<pre><code class="language-text">$ kubectl -n shop get pod web-58b7b6f6f9-n25b2 -o json | jq -c &#x27;.metadata.ownerReferences[] | {kind, name, controller}&#x27;
{&quot;kind&quot;:&quot;ReplicaSet&quot;,&quot;name&quot;:&quot;web-58b7b6f6f9&quot;,&quot;controller&quot;:true}
$ kubectl -n shop get rs web-58b7b6f6f9 -o json | jq -c &#x27;.metadata.ownerReferences[] | {kind, name, controller}&#x27;
{&quot;kind&quot;:&quot;Deployment&quot;,&quot;name&quot;:&quot;web&quot;,&quot;controller&quot;:true}
$ kubectl -n shop get deploy web -o json | jq -c &#x27;.metadata.ownerReferences&#x27;
null
</code></pre>
<p>Nothing on the Deployment object lists its ReplicaSets or its Pods. Every link lives on the child as an <code>ownerReference</code>, with <code>controller: true</code> on the one that manages it. It&#x27;s the same pointer the garbage collector follows for cascading deletes: delete the Deployment, and everything whose owner chain leads back to it goes too.</p>
<blockquote>
  <p><strong>For a tool</strong></p>
  <p><strong>You walk up, not down.</strong> kubectl probes starts from the pods, because that&#x27;s where the probes are actually running, and follows <code>ownerReferences</code> until there&#x27;s no owner left: Pod → ReplicaSet → Deployment, Pod → Job → CronJob. Whatever sits at the top is what the plugin calls a workload.</p>
  <p>Then come the edge cases: a pod with no owner shows up as <code>pod/&lt;name&gt;</code>, a custom owner like an Argo Rollout is named by its resource, and static pods, whose mirror pods are owned by their Node, show up as <code>node/&lt;name&gt;</code>. That&#x27;s all the <code>node/probes-demo</code> rows in the summary above.</p>
</blockquote>
<p>Why start from the pods and not the templates? Because mid-rollout they disagree, and that disagreement is exactly the <code>*</code> from the stalled rollout.</p>
<h3 id="the-api-server-fills-in-the-blanks">The API server fills in the blanks</h3>
<p>Here&#x27;s the readiness probe from that Postgres manifest again, as it lives on the running pod:</p>
<pre><code class="language-text">$ kubectl -n shop get pod postgres-0 -o json | jq &#x27;.spec.containers[0].readinessProbe&#x27;
{
  &quot;exec&quot;: {
    &quot;command&quot;: [
      &quot;pg_isready&quot;,
      &quot;-U&quot;,
      &quot;shop&quot;,
      &quot;-d&quot;,
      &quot;shop&quot;
    ]
  },
  &quot;failureThreshold&quot;: 3,
  &quot;periodSeconds&quot;: 5,
  &quot;successThreshold&quot;: 1,
  &quot;timeoutSeconds&quot;: 3
}
</code></pre>
<p>The manifest never set <code>successThreshold</code>. The API server wrote the default in when the pod was created, and it does that for every probe field except <code>initialDelaySeconds</code>. So on a running pod, &quot;left unset&quot; and &quot;explicitly set to the default&quot; look exactly the same.</p>
<p>That one changed a design decision. I wanted the detailed view to mark defaults as defaults, and on a live pod there&#x27;s almost nothing left to mark. So the plugin shows a default in gray, like the <code>0s</code> delays in the screenshots, and the JSON report still tells the two apart wherever it can, for example in a manifest read with <code>-f</code>.</p>
<h3 id="a-plugin-is-just-a-binary-on-your-path">A plugin is just a binary on your PATH</h3>
<p>Any executable called <code>kubectl-&lt;something&gt;</code> on your <code>PATH</code> is a kubectl plugin. That&#x27;s the whole contract:</p>
<pre><code class="language-bash">#!/usr/bin/env bash
echo &quot;hello from a kubectl plugin, context: $(kubectl config current-context)&quot;
</code></pre>
<pre><code class="language-text">$ kubectl hello
hello from a kubectl plugin, context: probes-demo
</code></pre>
<p>Underscores in the name become dashes, which is why Ahmet&#x27;s binary is <code>kubectl-node_resource</code> and you type <code>kubectl node-resource</code>. Everything else is convention: <code>cli-runtime</code> for the flags, goreleaser to build archives for 5 platforms, and <a href="https://krew.sigs.k8s.io/?ref=mert.cc">krew</a> to find, install and upgrade plugins. <mark>The mechanism took 2 lines to understand. The conventions around it are what took a week of reading.</mark></p>
<h2 id="whats-next">What&#x27;s next</h2>
<p>v0.1.0 is out. Grab an archive from the <a href="https://github.com/mertdotcc/kubectl-probes/releases">releases</a>, or:</p>
<pre><code class="language-sh">go install github.com/mertdotcc/kubectl-probes@latest
</code></pre>
<p>It&#x27;s also submitted to krew-index and waiting for review, so <code>kubectl krew install probes</code> should work soon. 👀</p>
<p>If you want to poke at it without pointing it at a real cluster, the repo ships the same demo cluster I used for every screenshot in this post:</p>
<pre><code class="language-sh">cd hack/demo-cluster
make up          # or: make up TOOL=minikube
make chaos
kubectl probes -n shop
</code></pre>
<p>And I already have a couple more ideas for plugins, all aimed at real pain points of mine. Next up is <a href="https://mert.cc/projects/kubectl-svc-why">kubectl svc-why</a>: why is this Service returning 503, or showing no endpoints? It walks the request path and tells you where it breaks first. After that, there are ideas around CronJobs, startup time and NetworkPolicies sitting in my notes. Same rule for all of them: one thing, done well.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>I enjoyed this a lot. More than I expected, honestly.</p>
<p>Reading someone else&#x27;s code slowly, with a tutor that makes you explain it back, is a completely different experience from skimming a repo. Vibe coding on top of that understanding felt less like gambling and more like pairing. And deleting 6,000 lines taught me more about scope than I&#x27;d like to admit.</p>
<p>If you try kubectl probes and it tells you something wrong, or fails to tell you something it should, <a href="https://github.com/mertdotcc/kubectl-probes/issues">open an issue</a>. There&#x27;s literally one called <a href="https://github.com/mertdotcc/kubectl-probes/issues/47"><em>&quot;Dogfood log: what kubectl probes failed to tell me on a real cluster&quot;</em></a>, and I&#x27;d love your entries in it.</p>
<h2 id="further-reading--references">Further Reading &amp; References</h2>
<ol>
  <li><a href="https://github.com/mertdotcc/kubectl-probes">kubectl probes on GitHub</a>, with the ADRs under <code>docs/adr/</code></li>
  <li><a href="https://github.com/ahmetb/kubectx?ref=mert.cc">kubectx and kubens (Ahmet Alp Balkan)</a></li>
  <li><a href="https://github.com/ahmetb/kubectl-node_resource?ref=mert.cc">kubectl node-resource (Ahmet Alp Balkan)</a></li>
  <li><a href="https://github.com/mattpocock/skills?ref=mert.cc">Skills for Real Engineers (Matt Pocock)</a></li>
  <li><a href="https://ngrok.com/blog/probes?ref=mert.cc">🔥 Probes, the interactive essay (Sam Rose, ngrok)</a></li>
  <li><a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/?ref=mert.cc">Kubernetes Docs: Configure Liveness, Readiness and Startup Probes</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/?ref=mert.cc">Kubernetes Docs: Owners and Dependents</a></li>
  <li><a href="https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/?ref=mert.cc">Kubernetes Docs: Extend kubectl with plugins</a></li>
  <li><a href="https://krew.sigs.k8s.io/?ref=mert.cc">krew, the kubectl plugin manager</a></li>
</ol>
]]></content:encoded>
      <category>kubernetes</category>
      <category>kubectl</category>
      <category>go</category>
    </item>
    <item>
      <title>The Long Road to In-Place Pod Resize in Kubernetes</title>
      <link>https://mert.cc/blog/2026/01/05/in-place-pod-resize-in-kubernetes</link>
      <guid isPermaLink="true">https://mert.cc/blog/2026/01/05/in-place-pod-resize-in-kubernetes</guid>
      <pubDate>Mon, 05 Jan 2026 09:30:00 GMT</pubDate>
      <dc:creator>Mert Öztürk</dc:creator>
      <description>Complete guide to in-place Pod resizing in Kubernetes 1.35: from alpha evolution to GA release, API design, kubelet internals, and practical use cases.</description>
      <content:encoded><![CDATA[
<figure><img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/hero-light.png" alt="" /></figure>
<p>Kubernetes 1.35 finally makes in-place Pod resize a stable feature. [<a href="https://kubernetes.io/blog/2025/12/19/kubernetes-v1-35-in-place-pod-resize-ga/?ref=mert.cc">1</a>] That might not sound exciting at first, until you remember how often “just change the resources” used to mean restarting Pods, breaking caches, or disrupting stateful workloads. Ask me how I know. 🙃</p>
<p>In-place resize changes that model. It lets Kubernetes adjust CPU and memory on a running Pod, sometimes without even restarting the container. Getting there took years of design work and several API revisions. In this post, I want to walk through that journey, explain how the feature actually works under the hood, and talk about where it genuinely helps and where it still does not.</p>
<figure>
  <img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/screenshot3.png" alt="Kubernetes In-Place Pod Resize General Availability Announcement" loading="lazy"/>
  <figcaption>The GA announcement, <a href="https://www.linkedin.com/posts/vinaykul_kubernetes-kubecon-cncf-activity-7396015736667688960-MBIW/?ref=mert.cc">on LinkedIn</a></figcaption>
</figure>
<h2 id="history-and-evolution-of-in-place-pod-resize">History and Evolution of In-Place Pod Resize</h2>
<p>In-place Pod resize did not appear fully formed. It went through several iterations before reaching a shape that fits Kubernetes’ scheduling and runtime model.</p>
<p>The core problem was clear early on. CPU and memory are properties of a running workload, but Kubernetes treated them as immutable Pod attributes. Changing them required recreating the Pod, even when the workload itself could have continued running safely. <mark>The challenge was making those resources mutable without breaking scheduling guarantees, node stability, or workload expectations.</mark></p>
<figure><img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/visual1.png" alt="The full in-place Pod resize lifecycle, combining control plane events, kubelet reconciliation, runtime updates, and Pod state transitions." loading="lazy"/></figure>
<h3 id="kubernetes-127-alpha-and-first-usable-implementation">Kubernetes 1.27: Alpha and first usable implementation</h3>
<p>Kubernetes 1.27 introduced in-place Pod resize as an alpha feature. [<a href="https://kubernetes.io/blog/2023/05/12/in-place-pod-resize-alpha/?ref=mert.cc">2</a>] It was disabled by default and explicitly marked experimental.</p>
<p>At this stage, the feature focused on the minimum viable capability. CPU and memory requests and limits could be updated on a running Pod without deleting it. That alone required changes across the API, kubelet, and container runtime layers.</p>
<p>The constraints were strict. <strong>Memory limits could only be increased, not reduced.</strong> Support was limited to certain container types. The feature required enabling the <code>InPlacePodVerticalScaling</code> feature gate manually. None of this was accidental. The goal was to validate the mechanics before committing to a stable API.</p>
<p>Alpha answered a narrow question: can the kubelet and runtime safely apply new resource limits to an existing container while keeping the Pod intact.</p>
<h3 id="kubernetes-133-beta-and-api-stabilization">Kubernetes 1.33: Beta and API stabilization</h3>
<p>In Kubernetes 1.33, in-place Pod resize graduated to beta and was enabled by default. [<a href="https://kubernetes.io/blog/2025/05/16/kubernetes-v1-33-in-place-pod-resize-beta/?ref=mert.cc">3</a>] This was the point where the feature stopped being a proof of concept and started to look like something that could be relied on operationally.</p>
<p>Most of the important work in this phase was not about adding new capabilities, but about tightening the API surface and making resize behavior explicit, observable, and harder to misuse.</p>
<p>The beta release introduced <strong>major API changes and improvements</strong>:</p>
<ul>
  <li>
    <p><strong>Dedicated <code>resize</code> subresource</strong></p>
    <p>Resource updates must be performed through a new Pod subresource, <code>resize</code>, for example using <code>kubectl patch pod &lt;name&gt; --subresource=resize</code>. In earlier versions, resource changes were applied by patching the Pod spec directly, which blurred the line between immutable and mutable fields. Making resize a distinct subresource clarified intent and avoided accidental live resizes as a side effect of generic Pod updates.</p>
  </li>
  <li>
    <p><strong>Pod Conditions for resize status</strong></p>
    <p>Kubernetes shifted from an ambiguous <code>status.resize</code> field (used in alpha) to using <strong>Pod Conditions</strong> to track resize progress.</p>
    <p>Two conditions were introduced:</p>
    <ol>
      <li><code>PodResizePending</code>, used when a resize request cannot be satisfied immediately, with reasons such as <code>Infeasible</code> or <code>Deferred</code>.</li>
      <li><code>PodResizeInProgress</code>, used while the kubelet is actively applying resource changes.</li>
    </ol>
    <p>This change made resize behavior visible in a way that could be monitored and automated reliably.</p>
  </li>
  <li>
    <p><strong>Support for sidecar containers</strong></p>
    <p>Early implementations focused primarily on main containers. Beta added support for in-place resizing of sidecar containers as well. This was necessary for real-world deployments where sidecars are long-running and often resource-sensitive.</p>
  </li>
  <li>
    <p><strong>Stability and kubelet hardening</strong></p>
    <p>Much of the beta work focused on correctness under failure. Kubelet state tracking around resource updates was reworked to survive restarts using checkpoint files, reducing the risk of inconsistent state after node disruption. The Container Runtime Interface (CRI) was extended with more robust calls for updating container resources, closing several edge cases seen in earlier iterations.</p>
  </li>
  <li>
    <p><strong>Performance improvements</strong></p>
    <p>The Pod Lifecycle Event Generator (PLEG) was optimized to detect and apply resize events more quickly. This reduced the delay between a resize request being issued and the kubelet acting on it.</p>
  </li>
  <li>
    <p><strong>Feature gate enabled by default</strong></p>
    <p>The <code>InPlacePodVerticalScaling</code> feature gate was switched on by default in 1.33. While the feature was still beta, this signaled confidence that the design was stable enough for broad testing and non-production use without special cluster configuration.</p>
  </li>
</ul>
<p>Taken together, these changes set the shape of the API that GA would later stabilize. Alpha proved the idea. Kubernetes 1.33 made the behavior explicit and observable enough to use with confidence.</p>
<h3 id="kubernetes-135-ga-and-removal-of-key-restrictions">Kubernetes 1.35: GA and removal of key restrictions</h3>
<p>Kubernetes 1.35 promoted in-place Pod resize to GA. At this point, the feature stopped being experimental or provisional and became a supported part of the platform for production clusters.</p>
<p>Most of the underlying mechanics were already in place by beta. The GA release focused on removing long-standing restrictions, tightening behavior under pressure, and improving operational visibility.</p>
<p>The jump from beta to GA included some important final updates:</p>
<ul>
  <li>
    <p><strong>Memory limit reduction</strong></p>
    <p><mark>Earlier versions only allowed memory limits to be increased. Reducing a container’s memory limit in place was disallowed to avoid evictions and immediate OOM kills. Kubernetes 1.35 lifts this restriction.</mark> Memory limits can now be decreased in place, subject to best-effort safety checks. The kubelet will only apply a downward resize if the container’s current memory usage is below the new requested limit at the time of the operation. This does not eliminate risk entirely. A usage spike after the resize can still trigger an OOM kill. It does, however, make memory rightsizing possible without forcing a Pod restart.</p>
  </li>
  <li>
    <p><strong>Priority-based handling of pending resizes</strong></p>
    <p>When a node cannot satisfy all resize requests immediately, the kubelet now applies a defined ordering instead of handling them opportunistically. Resize requests are prioritized based on Pod priority class, QoS class, and how long the request has been deferred, with older requests handled first. This matters in clusters where resizes are frequent and node capacity is heavily utilized, and it avoids low-priority workloads blocking more critical ones.</p>
  </li>
  <li>
    <p><strong>Pod-level resource API (alpha)</strong></p>
    <p>Kubernetes 1.35 also introduced Pod-level resources as a new alpha feature. This is a separate, opt-in capability that allows resource limits to be specified at the Pod level, aggregating all containers, via <code>spec.resources</code>. Pod-level resources are gated behind the feature gates of <code>PodLevelResources</code> and <code>InPlacePodLevelResourcesVerticalScaling</code> and remain alpha in 1.35. While related, this is distinct from container-level resizing. Pod-level resources complement in-place resize by bounding total Pod resource usage, but they are still early. The rest of this article focuses on container-level resizing, which is stable and production-ready.</p>
  </li>
  <li>
    <p><strong>Observability improvements</strong></p>
    <p>The GA release added new events and metrics related to in-place resize operations. The kubelet now emits events when a resize is attempted, deferred, or fails, and exposes metrics that reflect resize activity. This closes an important gap for operators who need to understand why a resize did not happen, especially under node pressure.</p>
  </li>
</ul>
<p>Taken together, the GA release does not introduce a new model. It removes the last major constraints and makes the existing one safe to depend on. With that context in place, I will now try to show how in-place resizing actually works inside the control plane and on the node.</p>
<h2 id="how-in-place-pod-resize-works-under-the-hood">How In-Place Pod Resize Works Under the Hood</h2>
<figure>
  <img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/diagram1.png" alt="End-to-end flow of an in-place Pod resize, from API request through kubelet reconciliation to updated CPU and memory cgroups on the node." loading="lazy"/>
  <figcaption>What happens when a running Pod is resized? Resource changes flow from the API server to the kubelet and container runtime, updating cgroups in place without rescheduling or recreating the Pod.</figcaption>
</figure>
<p>Historically, Kubernetes treated a Pod’s resource requests and limits as <strong>immutable</strong> once the Pod was running. Any change required destroying and recreating the Pod, often through a Deployment rollout. In-place Pod resize changes this by allowing CPU and memory resources to be updated on an existing Pod.</p>
<p>Making that possible without breaking scheduling guarantees or node-level isolation required coordinated changes across several parts of the system. The kubelet, the scheduler, and the container runtime all play a role. I&#x27;ll go through the control flow and the responsibilities of each component during an in-place resize.</p>
<h3 id="resize-workflow-overview">Resize workflow overview</h3>
<p>At a high level, an in-place resize follows a predictable sequence. The Pod remains bound to the same node throughout, and no rescheduling occurs.</p>
<ol>
  <li>
    <p><strong>Resize request is issued</strong></p>
    <p>A resize starts when a user or controller updates a Pod’s resource requirements through the <code>resize</code> subresource. This is an explicit action and distinct from patching the Pod spec directly.</p>
    <p>For example, we can increase a container’s CPU allocation with a command like:</p>
    <pre><code class="language-bash">kubectl patch pod mypod --subresource=resize -p &#x27;
{
  &quot;spec&quot;: {
    &quot;containers&quot;: [
      {
        &quot;name&quot;: &quot;app&quot;,
        &quot;resources&quot;: {
          &quot;requests&quot;: {
            &quot;cpu&quot;: &quot;2&quot;
          },
          &quot;limits&quot;: {
            &quot;cpu&quot;: &quot;2&quot;
          }
        }
      }
    ]
  }
}&#x27;
</code></pre>
    <p>The API server validates the request in the same way it validates other resource changes. It checks namespace quotas, limit ranges, and basic correctness. If the request is valid, the updated Pod spec is persisted to <code>etcd</code>.</p>
    <p>No new Pod is created, and the Pod is not rescheduled.</p>
  </li>
  <li>
    <p><strong>API server updates the Pod object</strong></p>
    <p>Patching the resize subresource results in a new Pod spec generation with updated resource values. From the control plane’s perspective, this is a normal object update.</p>
    <p>The scheduler does not attempt to place the Pod again, since it is already assigned to a node. However, the scheduler will later take the updated resource intent into account when evaluating node capacity for other Pods.</p>
    <p>The kubelet running on the node hosting the Pod is notified of the change through the standard watch mechanism.</p>
  </li>
  <li>
    <p><strong>Kubelet evaluates node feasibility</strong></p>
    <p>Once the kubelet observes the updated Pod spec, it determines whether the node can satisfy the new resource request.</p>
    <p>If sufficient CPU and memory are available, the kubelet proceeds with the resize. If not, the kubelet does not evict other Pods or take corrective action on its own.</p>
    <p>Instead, the Pod is marked with a <code>PodResizePending</code> condition. The condition includes a reason:</p>
    <ul>
      <li><code>Infeasible</code> if the requested resources exceed what the node can ever provide</li>
      <li><code>Deferred</code> if the request might become feasible later, for example if other Pods terminate</li>
    </ul>
    <p>While a resize is pending, the Pod continues running with its existing resource allocation. The kubelet periodically retries deferred resizes when node resources change. Multiple pending resizes are ordered by Pod priority class, QoS class, and how long the request has been waiting.</p>
    <p>These conditions are visible in the Pod status, which makes pending or blocked resizes observable to users and automation.</p>
  </li>
  <li>
    <p><strong>Kubelet applies resource changes through the container runtime</strong></p>
    <p>When a resize can proceed, the kubelet applies the new resource values using the Container Runtime Interface (CRI).</p>
    <p>On Linux, this means updating cgroup parameters such as CPU quotas and memory limits for the affected container. The kubelet issues a call like <code>UpdateContainerResources</code> to the runtime, passing the new values.</p>
    <p>The runtime, whether <code>containerd</code> or <code>CRI-O</code>, updates the kernel cgroup configuration for the container. For CPU increases and most memory increases, this happens without stopping the container process.</p>
    <p>From Kubernetes 1.35 onward, the CRI also supports updating Pod sandbox resources, which improves behavior on some platforms and prepares the ground for better Windows support.</p>
  </li>
  <li>
    <p><strong>Container restarts when required</strong></p>
    <p>Not all resource changes can be safely applied without restarting a container. Kubernetes exposes this explicitly through the per-container <code>resizePolicy</code>.</p>
    <p>Each resource type can specify one of two policies:</p>
    <ul>
      <li><code>NotRequired</code>, which applies the new cgroup limits without restarting the container</li>
      <li><code>RestartContainer</code>, which restarts the container to ensure the new limits take effect</li>
    </ul>
    <p>CPU resizes typically use <code>NotRequired</code>. Memory resizes are more nuanced. <mark>Some runtimes and applications, such as the JVM, do not adjust internal memory limits dynamically. Increasing memory without a restart may not be effective, and decreasing memory may be unsafe.</mark></p>
    <p>When <code>RestartContainer</code> is specified, the kubelet applies the new limits and then restarts only the affected container. Other containers in the same Pod are not restarted. The Pod itself remains intact, including its IP address and volumes.</p>
  </li>
  <li>
    <p><strong>Pod status and scheduler accounting are updated</strong></p>
    <p>As the resize progresses, Kubernetes updates the Pod’s status to reflect both intent and reality.</p>
    <p>Each container status includes resource information that shows the desired, allocated, and actual resources. While a resize is in progress, the <code>PodResizeInProgress</code> condition is set. Once the resize completes, the condition is cleared.</p>
    <p>If a container was restarted as part of the resize, its <code>restartCount</code> is incremented, which can be observed through standard tooling.</p>
    <p>The scheduler uses this status information to maintain safe cluster-level accounting. During a resize, it considers the maximum of desired, allocated, and actual resources when deciding whether new Pods can fit on the node. This prevents overcommit during the window where a resize has been requested but not yet fully applied.</p>
  </li>
  <li>
    <p><strong>Resize completes</strong></p>
    <p>At this point, the Pod is running with its new resource allocation. No rescheduling occurred, no Pod identity changed, and no volumes were reattached.</p>
    <p>From the application’s perspective, the resize may be completely transparent, unless a container restart was required. Kubernetes emits events throughout this process, which can be used for monitoring and debugging.</p>
  </li>
</ol>
<h3 id="the-kubelets-role-and-guarantees">The kubelet’s role and guarantees</h3>
<figure>
  <img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/diagram2.png" alt="Kubelet decision flow for in-place Pod resize, including feasibility checks, Pending and InProgress states, and restart decisions." loading="lazy"/>
  <figcaption>How the kubelet decides whether a resize proceeds, waits, or fails? Desired and current resources are compared, feasibility is evaluated, and the Pod transitions through Pending, InProgress, or terminal states.</figcaption>
</figure>
<p>The kubelet is responsible for enforcing in-place resizes while preserving node stability.</p>
<p>If a resize is deferred, the kubelet retries it when node resources change. Retry ordering respects Pod priority and QoS, which ensures that higher-priority workloads are not waiting behind lower-priority ones.</p>
<p>Resize state is checkpointed to disk. If the kubelet restarts mid-resize, it can recover the last known state and continue. This avoids leaving Pods in partially applied configurations.</p>
<p>The kubelet never violates node capacity. If resources are not available, the resize remains pending indefinitely. Kubernetes does not currently preempt other Pods to satisfy a resize request, even for high-priority workloads.</p>
<p>QoS class is fixed at Pod creation and cannot change as a result of resizing. A Guaranteed Pod must remain Guaranteed. A BestEffort Pod cannot gain requests. Resize requests that would violate these invariants are rejected.</p>
<h3 id="scheduler-and-control-plane-considerations">Scheduler and control plane considerations</h3>
<p>The scheduler does not move Pods to new nodes as part of an in-place resize. The operation is strictly local to the node hosting the Pod.</p>
<p>If a resize cannot be satisfied, the Pod remains where it is with its original resources. It is up to the operator or higher-level tooling to decide whether to delete and recreate the Pod elsewhere.</p>
<p>The API server’s role is limited to validation and persistence. It does not orchestrate the resize. Controllers such as Deployments and StatefulSets do not currently perform in-place resizes automatically. Updating a Pod template still results in new Pods being created according to the rollout strategy.</p>
<p>In-place resize is therefore typically driven by direct user action or by specialized controllers such as vertical autoscalers.</p>
<h3 id="limitations-and-caveats">Limitations and caveats</h3>
<p>Even as a GA feature, in-place Pod resize has clear boundaries:</p>
<ul>
  <li>
    <p>Only <code>CPU</code> and <code>memory</code> can be resized. Other resources such as <code>GPUs</code>, <code>hugepages</code>, <code>ephemeral storage</code>, and <a href="https://kubernetes.io/docs/tasks/configure-pod-container/extended-resource/?ref=mert.cc">extended resources</a> remain immutable after Pod creation.</p>
  </li>
  <li>
    <p>In-place resizing is fully supported on Linux nodes. <strong>Windows nodes do not support in-place resource updates as of Kubernetes 1.35.</strong></p>
  </li>
  <li>
    <p>Pods using static CPU or memory manager policies cannot be resized in place, since those policies rely on fixed allocations.</p>
  </li>
  <li>
    <p>If a resize triggers a container restart, that container experiences a full stop-start cycle. Memory state is lost, and applications must tolerate the restart. Other containers in the Pod are unaffected.</p>
  </li>
</ul>
<blockquote>
  <p><strong>Careful</strong></p>
  <p>Some runtimes adapt poorly to live memory changes. For example, JVM-based applications fix their max heap size using the <code>-Xmx</code> parameter at startup (often based on initial Pod limits). Increasing a Java Pod’s memory limit won’t automatically enlarge the heap without additional tooling; <strong>decreasing the limit could even be dangerous</strong> if the JVM is using close to the old max heap.</p>
  <p>Similarly, Node.js may have an internal max memory setting (<code>--max-old-space-size</code>) that doesn’t change at runtime. Python and Go are generally more flexible. They will happily use more memory or CPU if available, making them good candidates for in-place adjustments. It’s important to test how your application behaves when resources are changed on the fly. If it doesn’t react well, you might be forced to use the <code>RestartContainer</code> strategy for safe resizing (or forego in-place resizing for that app).</p>
</blockquote>
<figure>
  <img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/screenshot2.png" alt="JVM Tuning Guide for Memory Scale Up" loading="lazy"/>
  <figcaption>For the source of the screenshot, see #9 in Further Reading &amp; References</figcaption>
</figure>
<blockquote>
  <p><strong>Note · Init and ephemeral containers</strong></p>
  <p><strong>Not for Init/Ephemeral Containers</strong>: Only <code>regular app containers</code> and <code>sidecars</code> are resizeable. <code>Init containers</code> run to completion before the Pod is running, so they are not subject to resizing (they’ve already executed). <code>Ephemeral containers</code> (debug containers) also cannot be resized; they’re considered special-case and short-lived.</p>
</blockquote>
<ul>
  <li>Finally, in-place resize does not allow changing a Pod’s <a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/?ref=mert.cc">QoS class</a>. All resize operations must remain within the bounds of the original spec.</li>
</ul>
<p>In practice, these constraints define where in-place resize is a good fit. When used with those limits in mind, it avoids Pod recreation in cases where restarting a workload would previously have been the <strong>only</strong> option.</p>
<h2 id="api-design-and-usage">API Design and Usage</h2>
<p>From a user’s perspective, in-place Pod resize introduces a small set of API changes rather than a completely new workflow. Most of the complexity lives in the kubelet and scheduler. The API surface is intentionally narrow.</p>
<p>In this section I&#x27;ll try to show what actually matters when enabling and using the feature in Kubernetes 1.35.</p>
<h3 id="feature-gates-and-client-requirements">Feature gates and client requirements</h3>
<p>The core feature gate for in-place Pod resize is <code>InPlacePodVerticalScaling</code>. As of Kubernetes 1.33, <a href="https://kubernetes.io/blog/2025/05/16/kubernetes-v1-33-in-place-pod-resize-beta/?ref=mert.cc#getting-started-and-providing-feedback">it is enabled by default</a> and considered stable. On a brand new 1.35 cluster, no additional configuration is required.</p>
<p>If you are working with older clusters where the feature was disabled by default, the gate must be enabled consistently across the API server, controller manager, and kubelets. For 1.35 and later, this is only relevant when upgrading legacy environments.</p>
<p>Working with in-place resize also requires a reasonably recent kubectl version. Explicit support for the <code>resize</code> subresource was added in kubectl 1.32. Clients older than that will not recognize the subresource and will fail with validation errors. In practice, keeping kubectl aligned with your cluster version avoids most issues here.</p>
<p>Pod-level resources, introduced as an alpha feature in 1.35, are controlled by a separate feature gate (namely, <code>PodLevelResources</code>) and are not covered further in this section.</p>
<h3 id="pod-spec-changes-resizepolicy-and-status-fields">Pod spec changes: <code>resizePolicy</code> and status fields</h3>
<p>In-place resize adds a small number of fields to the Pod API that control behavior and expose state.</p>
<h4 id="resizepolicy-on-containers"><code>resizePolicy</code> on containers</h4>
<p>Each container can declare how it should behave when a specific resource is resized. This is done through the <code>resizePolicy</code> field on the container spec.</p>
<p>Policies are defined per resource name. At the moment, valid resource names are <code>cpu</code> and <code>memory</code>. Each resource can specify one of two policies:</p>
<ul>
  <li><code>NotRequired</code>, which applies the new resource limits without restarting the container</li>
  <li><code>RestartContainer</code>, which restarts the container so the new limits take effect cleanly If <code>resizePolicy</code> is omitted, the default is <code>NotRequired</code> for both CPU and memory.</li>
</ul>
<p>Most workloads leave CPU as <code>NotRequired</code>. Memory is more application-specific. Runtimes that cannot safely adapt to changing memory limits often require <code>RestartContainer</code> to avoid undefined behavior.</p>
<p>A minimal example looks like this:</p>
<pre><code class="language-yaml">spec:
  containers:
  - name: nginx
    image: nginx:1.25.1-alpine
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer
    resources:
      requests:
        cpu: &quot;500m&quot;
        memory: &quot;256Mi&quot;
      limits:
        cpu: &quot;500m&quot;
        memory: &quot;256Mi&quot;
</code></pre>
<p>In this configuration, CPU changes will be applied in place, while memory changes will trigger a container restart.</p>
<p>If a Pod’s overall restart policy is <code>Never</code>, all container resize policies must be <code>NotRequired</code>. Kubernetes will reject any resize that would violate the no-restart guarantee.</p>
<h4 id="resource-state-in-pod-status">Resource state in Pod status</h4>
<p>Once a Pod is running with in-place resize enabled, resource information appears in the container status as well as in the spec.</p>
<ul>
  <li><code>spec.containers[].resources</code> represents the desired state.</li>
  <li><code>status.containerStatuses[].resources</code> reflects the resources actually applied by the kubelet.</li>
  <li><code>status.containerStatuses[].allocatedResources</code> is used internally for scheduler accounting during transitions.</li>
</ul>
<p>After a successful resize, the values in <code>status.containerStatuses[].resources</code> converge to match the spec. Watching this field is the most reliable way to tell when a resize has taken effect.</p>
<p>Resize progress is also surfaced through Pod Conditions. Two conditions may appear:</p>
<ul>
  <li><code>PodResizePending</code>, when a resize cannot be satisfied immediately</li>
  <li><code>PodResizeInProgress</code>, while the kubelet is actively applying the change</li>
</ul>
<p>Both include a reason and message that explain what is happening. These conditions are cleared once the resize completes or is resolved.</p>
<h3 id="performing-a-live-resize">Performing a live resize</h3>
<p>In-place resize always targets the <code>resize</code> subresource. Attempting to patch resource fields on the main Pod spec will be rejected, since those fields are normally immutable.</p>
<h4 id="patching-a-running-pod">Patching a running Pod</h4>
<p>The most direct way to resize a Pod is with <code>kubectl patch</code> and the <code>resize</code> subresource.</p>
<p>For example, to raise a container’s CPU request and limit from 700m to 800m:</p>
<pre><code class="language-bash">kubectl patch pod resize-demo --subresource=resize -p &#x27;{
  &quot;spec&quot;: {
    &quot;containers&quot;: [{
      &quot;name&quot;: &quot;pause&quot;,
      &quot;resources&quot;: {
        &quot;requests&quot;: {&quot;cpu&quot;: &quot;800m&quot;},
        &quot;limits&quot;: {&quot;cpu&quot;: &quot;800m&quot;}
      }
    }]
  }
}&#x27;
</code></pre>
<p>After issuing the patch, the Pod spec will reflect the new values immediately. The status will update once the kubelet applies the change. If no restart is required, the container’s <code>restartCount</code> will remain unchanged.</p>
<h4 id="editing-in-place">Editing in place</h4>
<p>For ad hoc adjustments, <code>kubectl edit pod &lt;name&gt; --subresource=resize</code> can be used. This opens an editor with only the fields that are allowed to change. Saving the file sends the resize request.</p>
<p>This is convenient for manual tuning, but easy to misuse if the <code>--subresource=resize</code> flag is not used. Without it, resource fields will be read-only.</p>
<h4 id="applying-from-a-manifest">Applying from a manifest</h4>
<p>In GitOps or CI workflows, resizing can be done with server-side apply:</p>
<pre><code class="language-bash">kubectl apply -f updated-pod.yaml --subresource=resize --server-side
</code></pre>
<p>Only the mutable fields are merged. The manifest must not attempt to change immutable parts of the Pod spec.</p>
<p>It is important to note that higher-level controllers do not use this path by default. <mark>Updating a Deployment or StatefulSet template with new resources will still result in Pod replacement. To perform true in-place resizes, Pods must be targeted directly, or controlled by components that are resize-aware.</mark></p>
<p>One notable exception is the Vertical Pod Autoscaler (VPA). When configured with an in-place update mode, it will attempt to resize Pods directly and fall back to recreation only when necessary.</p>
<h3 id="example-end-to-end-flow">Example end-to-end flow</h3>
<p>To tie it all together, consider this Pod spec with resizable resources (After creating the Pod, CPU can be increased in place without interruption. Memory increases will trigger a restart of the <code>app</code> container only.):</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
spec:
  containers:
  - name: app
    image: registry.k8s.io/pause:3.8
    # Allow CPU to change without restart, but memory requires a restart
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired 
    - resourceName: memory
      restartPolicy: RestartContainer
    resources:
      requests:
        cpu: &quot;500m&quot;
        memory: &quot;200Mi&quot;
      limits:
        cpu: &quot;500m&quot;
        memory: &quot;200Mi&quot;
</code></pre>
<ol>
  <li>
    <p><strong>Deploy the Pod</strong>: <code>kubectl apply -f pod.yaml</code>. The Pod starts with <code>0.5 CPU</code> and <code>200Mi memory</code>. It&#x27;s in <code>Guaranteed</code> QoS (requests == limits) in this example.</p>
  </li>
  <li>
    <p><strong>Increase CPU in-place</strong>:</p>
  </li>
</ol>
<pre><code class="language-bash">kubectl patch pod resize-demo --subresource=resize -p &#x27;{
  &quot;spec&quot;: {
    &quot;containers&quot;: [
      {
        &quot;name&quot;: &quot;app&quot;,
        &quot;resources&quot;: {
          &quot;requests&quot;: {
            &quot;cpu&quot;: &quot;800m&quot;
          },
          &quot;limits&quot;: {
            &quot;cpu&quot;: &quot;800m&quot;
          }
        }
      }
    ]
  }
}&#x27;
</code></pre>
<p>This raises CPU to 0.8 cores. Kubelet on the node updates cgroups without restarting the container. Checking <code>kubectl get pod resize-demo -o yaml</code> after a couple seconds should show the CPU updated in both spec and status, and <code>restartCount</code> still 0.</p>
<ol start="3">
  <li><strong>Increase Memory (requires restart)</strong>:</li>
</ol>
<pre><code class="language-bash">kubectl patch pod resize-demo --subresource=resize -p &#x27;{
  &quot;spec&quot;: {
    &quot;containers&quot;: [
      {
        &quot;name&quot;: &quot;app&quot;,
        &quot;resources&quot;: {
          &quot;requests&quot;: {
            &quot;memory&quot;: &quot;300Mi&quot;
          },
          &quot;limits&quot;: {
            &quot;memory&quot;: &quot;300Mi&quot;
          }
        }
      }
    ]
  }
}&#x27;
</code></pre>
<p>This raises memory to <code>300Mi</code>. Because app container’s memory <code>resizePolicy</code> is <code>RestartContainer</code>, the kubelet will gracefully stop and restart the <code>app</code> container to apply the new memory limit. After patching, watch the Pod’s status: it may for a moment go through a <code>ContainerCreating</code> or <code>Running</code> (restarting) phase for that container. Once done, <code>status.containerStatuses[0].resources</code> will show <code>300Mi</code> and <code>restartCount</code> will have incremented by 1.</p>
<ol start="4">
  <li><strong>Handle Impossible Cases</strong>:</li>
</ol>
<p>Just to test it out, when I patch an absurd amount of CPU cores to the <code>app</code> container in my <code>resize-demo</code> Pod, such as 1000 cores, the Pod will get a <code>PodResizePending</code> with reason <code>Infeasible</code>.</p>
<pre><code class="language-bash">kubectl patch pod resize-demo --subresource=resize -p &#x27;{
  &quot;spec&quot;: {
    &quot;containers&quot;: [
      {
        &quot;name&quot;: &quot;app&quot;,
        &quot;resources&quot;: {
          &quot;requests&quot;: {
            &quot;cpu&quot;: &quot;1000&quot; # Notice 1000 instead of 1000m
          },
          &quot;limits&quot;: {
            &quot;cpu&quot;: &quot;1000&quot;
          }
        }
      }
    ]
  }
}&#x27;
</code></pre>
<p>Check it out via <code>k get po resize-demo -oyaml</code>.</p>
<p>The condition message will say something like &quot;Node didn&#x27;t have enough capacity&quot;.</p>
<p>This is how we know the request can’t be satisfied on the node. We could then decide to revert the request or delete the Pod to reschedule it manually on a bigger node.</p>
<h3 id="best-practices-for-using-in-place-resize">Best Practices for Using In-Place Resize</h3>
<ol>
  <li>
    <p><strong>Use gradual, step by step adjustments and not crazy jumps</strong>: Don&#x27;t dramatically increase resources in one go beyond what the node can handle. If you need to double or triple resources, consider ensuring the node has buffer or using cluster autoscaler to pre-provision capacity, otherwise your pod will sit in <code>Pending</code> state.</p>
  </li>
  <li>
    <p><strong>Monitor Pod Conditions</strong>: A script or pipeline that patches Pods could also watch for the <code>PodResizePending</code> condition to clear or to log why it&#x27;s deferred.</p>
  </li>
  <li>
    <p><strong>Leverage Autoscalers</strong>: When possible, delegate resizing decisions to an autoscaler (Horizontal or Vertical Pod Autoscaler). The VPA in particular is <a href="https://kubernetes.io/docs/concepts/workloads/autoscaling/vertical-pod-autoscale/?ref=mert.cc#updateMode-InPlaceOrRecreate">becoming resize-aware</a>. With the &quot;Auto&quot; mode (<code>InPlaceOrRecreate</code>) it will attempt an in-place update first and only fall back to recreating the Pod if absolutely necessary. This basically gives us automatic vertical scaling with minimal disruption.</p>
  </li>
  <li>
    <p><strong>Plan for Restarts (if needed!)</strong>: If your app is not 100% dynamic, that&#x27;s fine. You can still benefit by using restart-on-resize. For instance, <mark>a memory-intensive app like a database might require a restart to allocate a larger buffer pool.</mark> Doing that with in-place resize (with restart) still keeps the Pod consistent! Same identity, and it avoids the overhead of re-scheduling and re-attaching storage to that Pod (unlike a full Pod replace would). <mark>This is still a HUGE WIN.</mark></p>
  </li>
  <li>
    <p><strong>Resource Quotas and Limits</strong>: Check your namespace&#x27;s <code>ResourceQuotas</code> and <code>LimitRanges</code>. A resize is still technically a spec change. If it violates a quota (example: we try to give a Pod more CPU than allowed per quota), t will be denied by the API server just like a new Pod creation would. Make sure that any such policies are updated to accomodate the maximum sizes you intend to allow.</p>
  </li>
  <li>
    <p><strong>Testing</strong>: Test in-place resize with your specific workload in a staging environment. Watch how your application behaves, especially for <strong>memory downscaling</strong>. Some apps might need a tweak (like making Java respect <code>cgroup</code> limits for heap sizing, or making sure a Python app frees memory back to the OS -- <mark>I am planning on writing a technical deep-dive on JVM-based applications and &quot;gotcha&quot;s, tweaking the heap size, or playing with the -Xmx settings. If you think you would be interested in such a post, make sure to follow! 👀)</mark></p>
  </li>
</ol>
<p>Now that I&#x27;ve discussed how to use the feature, I want to give some example scenarios where in-place Pod resizing is <strong>particularly useful</strong>.</p>
<h2 id="real-world-use-cases-and-examples">Real-World Use Cases and Examples</h2>
<p>In-place Pod resize unlocks new possibilities for operational efficiency, cost optimization, and responsiveness. Below are, IMHO, some of the most important use cases showing how Kubernetes users can leverage this feature. I&#x27;ll cover examples in Java, Go, and Python applications, as well as how autoscalers and CI/CD processes can utilize vertical scaling without restarts.</p>
<ul>
  <li><strong>Startup Boost for Java Applications</strong>: Java services often require a CPU surge during startup (think JIT compilation, class loading, etc) but need far less CPU in steady conditions. Previously, what we would do is over-allocate CPU to avoid slow starts or rely on the VPA to kill and restart Pods with new limits; both are suboptimal. With in-place resize, we can deploy a Java app with a high CPU request initially and then <strong>scale it down in-place once startup completes</strong>. (<a href="https://kubernetes.io/blog/2025/05/16/kubernetes-v1-33-in-place-pod-resize-beta/?ref=mert.cc#why-does-in-place-pod-resize-matter">See</a>) For example, a Java webserver might start with 2 vCPUs for a fast bootstrap, and after a few minutes, an automated job or the VPA can reduce it to 0.5 vCPU without interrupting the service. This pattern (sometimes called &quot;CPU burst&quot; or startup boost) is now <mark>achievable seamlessly</mark>.</li>
</ul>
<figure>
  <img src="https://mert.cc/blog/in-place-pod-resize-in-kubernetes/screenshot1.png" alt="Kubernetes Application Startup Boost for Java/JVM Apps" loading="lazy"/>
  <figcaption>For the source of the screenshot, see #9 in Further Reading &amp; References</figcaption>
</figure>
<ul>
  <li>
    <p><strong>Dynamic Web Services in Go and Python</strong>: Many stateless services written in Go or Python have varying CPU/memory needs depending on load. HPA can add more replicas, but if your service scales better vertically (or if you hit replica limits), in-place resize is like a Christmas present. <mark>Go and Python apps typically handle additional CPU or memory with no special tuning,</mark> meaning you can add resources to a live container and it will <strong>immediately</strong> take advantage of them. For example, a Python Flask API under spike load could be given an extra 1 GB of memory to expand in-memory caches instead of spawning new replicas. Or a Go service doing image processing might be granted an extra core during a batch of heavy requests, then scaled down later. Since neither Go nor Python have a fixed heap or similar, the app just experiences less GC pressure or faster execution when more resources become available. This yields a more <strong>elastic response to traffic bursts</strong> without overshooting on replicas (which can add latency while new pods start).</p>
  </li>
  <li>
    <p><strong>Stateful Services and Databases 🔥</strong>: In-place resizing truly changes the game for stateful workloads that are costly to restart. <strong>Databases, caches, and stateful streaming apps</strong> can rarely be simply killed and relaunched without impact. Suppose you have a PostgreSQL Pod that suddenly needs more memory to execute a complex query or to load a larger working set into cache. We can now <strong>bump up its memory limit on the fly</strong> and let the query finish faster, then potentially scale it back down later. All of this happens with zero connection drops. The DB keeps running during the resize. Same story with Redis.</p>
  </li>
  <li>
    <p><strong>Batch Jobs and AI/ML Workloads</strong>: Batch processing jobs or ML model training often have variable resource needs during their lifecycle. For instance, a TensorFlow training Pod might not need much CPU during data loading but then require a lot of CPU and memory during the training phase with a larger model. If such jobs run as long-lived Pods, we can now write a simple script that handles resources increases right when the heavy lifting begins. The opposite of this angle is also true: if a job finishes early or enters a less intensive phase, we could simply dial down its resources to make room on the node for others.</p>
  </li>
  <li>
    <p><strong>Sidecar Proxy Scaling 🔥</strong>: In microservice architectures with service mesh sidecars (ie Envoy in Istio), the sidecar container might at times become a bottleneck. Instead of evicting the Pod or over-provisioning every sidecar for worst-case load, we can now resize the sidecar container&#x27;s resources in-place when needed.</p>
  </li>
  <li>
    <p><strong>Autoscalers Leveraging Vertical Resizing</strong>: I already touched on this earlier, but I can&#x27;t emphasize this enough: the Vertical Pod Autoscaler (VPA) can now operate in a mode that uses in-place resize by default. In Kubernetes 1.35, VPA&#x27;s <code>UpdateMode: InPlaceIfPossible</code> (beta) will try to patch running pods with new recommendations instead of evicting them. This means you can get automated vertical scaling with much less disruption. For workloads where horizontal scaling is not applicable, VPA with in-place updates can continuously right-size resources (up to set limits) to optimize usage.</p>
  </li>
</ul>
<p>These examples are just a few. I can go on and on (cost optimization for night/day workloads, CI/CD pipeline resource management, etc) about how <strong>flexible and powerful</strong> in-place Pod resizing can be. Whether it’s avoiding downtime for critical stateful apps, improving efficiency of autoscaling, or saving money by cutting down excess resources, the feature opens up new optimization opportunities. It’s important to always weigh the complexity: automated resizing introduces another layer of resource management logic. But I truly believe that for many scenarios <mark>the benefits far outweigh the costs</mark>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The graduation of In-Place Pod Resize to GA in Kubernetes 1.35 is, IMHO, one of the (if not <em>the</em>) most exciting developments. We can now basically treat CPU and memory as <strong>elastic properties</strong> of a running Pod, which opens up a realm of new optimization strategies for both performance tuning and cost savings.</p>
<p>To recap, I covered:</p>
<ul>
  <li><strong>History</strong> (the journey from 1.27 -&gt; 1.33 -&gt; 1.35)</li>
  <li><strong>Technical Deep-Dive</strong></li>
  <li><strong>API Usage</strong></li>
  <li><strong>Use Cases</strong></li>
  <li><strong>Performance</strong></li>
</ul>
<blockquote>
  <p><strong>Looking ahead</strong></p>
  <p>The Kubernetes community is already discussing the <a href="https://kubernetes.io/blog/2025/12/19/kubernetes-v1-35-in-place-pod-resize-ga/?ref=mert.cc#feature-expansion">next steps</a> such as broader resource support (aka <strong>GPU resizing</strong>), smarter controller integration, and handling of <a href="https://kubernetes.io/blog/2025/12/19/kubernetes-v1-35-in-place-pod-resize-ga/?ref=mert.cc#feature-expansion">edge cases</a> like node pressure and preemption during resizes. The ecosystem around this feature is growing, and cloud platforms are beginning to incorporate it.</p>
</blockquote>
<p>For now, if you manage Kubernetes workloads, give in-place Pod resizing a try on some non-critical workloads or in staging. Play with scaling pods up and down and see how your applications behave. With proper safeguards, you can then roll it out to production and reap the benefits of a more flexible and efficient cluster.</p>
<h2 id="further-reading--references">Further Reading &amp; References</h2>
<ol>
  <li><a href="https://kubernetes.io/blog/2025/12/19/kubernetes-v1-35-in-place-pod-resize-ga/?ref=mert.cc">💡 Kubernetes 1.35: In-Place Pod Resize Graduates to Stable (Natasha Sarkar)</a></li>
  <li><a href="https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/?ref=mert.cc">Kubernetes Docs: Resize CPU and Memory Resources assigned to Containers</a></li>
  <li><a href="https://kubernetes.io/blog/2025/05/16/kubernetes-v1-33-in-place-pod-resize-beta/?ref=mert.cc">Kubernetes v1.33: In-Place Pod Resize Graduated to Beta (Tim Allclair)</a></li>
  <li><a href="https://kubernetes.io/blog/2023/05/12/in-place-pod-resize-alpha/?ref=mert.cc">Kubernetes 1.27: In-place Resource Resize for Kubernetes Pods (alpha) (Vinay Kulkarni)</a></li>
  <li><a href="https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/1287-in-place-update-pod-resources?ref=mert.cc">KEP-1287: In-place Update of Pod Resources</a> - (for those who want to read the design rationale and future considerations)</li>
  <li><a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler/enhancements/4016-in-place-updates-support?ref=mert.cc">AEP-4016: Support for in place updates in VPA</a></li>
  <li><a href="https://github.com/kubernetes/autoscaler/pull/7863?ref=mert.cc">AEP-7862: Support CPU Startup Boost in VPA</a></li>
  <li><a href="https://www.youtube.com/watch?v=HuC4k7fmTBk&amp;ref=mert.cc">🔥 Video: In-Place Pod Resize in Kubernetes: Dynamic Resource Management Without Restarts (Tim Allclair &amp; Mofi Rahman)</a></li>
  <li><a href="https://www.youtube.com/watch?v=Hod4UPoMUPE&amp;ref=mert.cc">Video: Unleash the Power of Inplace Pod Resource Resizing for Startup and Cost Optimization (Zhang Zhen &amp; Yuxing Yuan)</a></li>
  <li><a href="https://www.youtube.com/watch?v=9lKa8bWU9II&amp;ref=mert.cc">Video: To Infinity and Beyond: Seamless Autoscaling with in-Place Resource Resize for Kubernetes Pods (Aya Ozawa &amp; Kohei Ota)</a></li>
  <li><a href="https://www.youtube.com/watch?v=jjfa1cVJLwc&amp;ref=mert.cc">Video: Resize Your Pods In-Place With Deterministic eBPF Triggers (Pablo Chico de Guzman &amp; Vinay Kulkarni)</a></li>
  <li><a href="https://blogs.halodoc.io/kubernetes-optimization-using-in-place-pod-resizing-and-zone-aware-routing/?ref=mert.cc">Real-world case study: Kubernetes Optimization Using In-Place Pod Resizing – Halodoc engineering blog on cost savings and implementation details of their resizing scheduler</a></li>
  <li><a href="https://palark.com/blog/in-place-pod-resizing-kubernetes/?ref=mert.cc">In-place Pod resizing in Kubernetes: How it works and how to use it - Palark Blog</a></li>
  <li><a href="https://engineering.doit.com/no-restarts-no-disruptions-seamless-pod-resource-updates-with-in-place-resizing-f3cf41654216?ref=mert.cc">DoiT Engineering Blog: No Restarts, No Disruptions: Seamless Pod Resource updates with In-Place Resizing</a></li>
</ol>
]]></content:encoded>
      <category>kubernetes</category>
      <category>cloud-native</category>
    </item>
    <item>
      <title>The New Beginnings: Creating More, Consuming Less</title>
      <link>https://mert.cc/blog/2026/01/01/the-new-beginnings</link>
      <guid isPermaLink="true">https://mert.cc/blog/2026/01/01/the-new-beginnings</guid>
      <pubDate>Thu, 01 Jan 2026 13:00:00 GMT</pubDate>
      <dc:creator>Mert Öztürk</dc:creator>
      <description>Finally launching this blog in 2026. My goal: create more, consume less. Sharing thoughts on AI, cloud-native architecture, DIY projects, design, BJJ, and more.</description>
      <content:encoded><![CDATA[
<figure><img src="https://mert.cc/blog/the-new-beginnings/hero-light.png" alt="" /></figure>
<p>Hey there! So I finally got around to updating my website and adding this blog section. Been meaning to do this for ages, but you know how it goes ... always something else that needs attention.</p>
<p>One of my main goals for 2026 is to flip the script on how I spend my time. Instead of endlessly scrolling and consuming content, I want to start creating more. And that includes sharing my thoughts and learnings here, not just the usual tech stuff from my day job.</p>
<p>I&#x27;m planning to write about my journey into design and art (still very much a beginner), woodworking projects that may or may not turn out as planned, 3D printing adventures and inevitable failures, running my home server (and all the rabbit holes that come with it), and various DIY electronics projects with Raspberry Pi, ESP32, and Arduino boards.</p>
<p>I&#x27;m also deep into Brazilian Jiu Jitsu these days, which has been humbling in the best way possible. Plus, I love traveling to places that aren&#x27;t on every Instagram feed, you know, the places your mum doesn&#x27;t want you to go to, or places that are simply unrecognized.</p>
<p>So yeah, that&#x27;s what&#x27;s coming. No promises on posting schedules or fancy content calendars (my ADHD and also procrastinating ass could never). Just me sharing stuff as I learn and build and explore. Should be fun.</p>
<p>Here&#x27;s to creating more in 2026.</p>
]]></content:encoded>
      <category>personal</category>
      <category>goals</category>
      <category>creating</category>
    </item>
  </channel>
</rss>
