<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Podo Stack]]></title><description><![CDATA[Tools that survived production. Weekly curation]]></description><link>https://podostack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!K687!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F647baa21-c6c0-4d23-bcf0-ddf3a7a641ed_500x500.png</url><title>Podo Stack</title><link>https://podostack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 08 May 2026 00:38:50 GMT</lastBuildDate><atom:link href="https://podostack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Ilia]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[podostack@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[podostack@substack.com]]></itunes:email><itunes:name><![CDATA[Ilia Gusev]]></itunes:name></itunes:owner><itunes:author><![CDATA[Ilia Gusev]]></itunes:author><googleplay:owner><![CDATA[podostack@substack.com]]></googleplay:owner><googleplay:email><![CDATA[podostack@substack.com]]></googleplay:email><googleplay:author><![CDATA[Ilia Gusev]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Firecracker: the minimalism that runs your Lambda function]]></title><description><![CDATA[How a Rust VMM with KVM, 125ms boot, and 5MB overhead per instance became the boundary nobody talks about]]></description><link>https://podostack.com/p/firecracker-microvm-lambda-isolation-rust-vmm</link><guid isPermaLink="false">https://podostack.com/p/firecracker-microvm-lambda-isolation-rust-vmm</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 06 May 2026 14:00:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!c-FR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c-FR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c-FR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!c-FR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!c-FR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!c-FR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c-FR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1674828,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195435440?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c-FR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!c-FR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!c-FR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!c-FR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58959d6f-eee6-45fb-aba3-3cbea0cd6836_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Every AWS Lambda function you've ever invoked ran inside a microVM that started in less than 125 milliseconds, used under 5 MB of memory for the VMM itself, and was destroyed when your function returned. That microVM runs on a piece of software called Firecracker, written in Rust, open-sourced in 2018, and now quietly sitting under Lambda, Fargate, Fly.io, Kata Containers, and half the serverless infrastructure that bills you for single-digit milliseconds at a time.</p><p>Most engineers have heard the name. Very few have looked at what Firecracker actually is, why it exists, and where it fits in the boundary-choice conversation that now dominates multi-tenant isolation, AI-agent sandbox platforms, and untrusted-code execution at scale.</p><p>Here's the full picture.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><h2>The dilemma that didn't have a clean answer</h2><p>Before 2018, two ways to isolate arbitrary user code:</p><p><strong>Containers (Docker, LXC).</strong> Fast start, high density, shared kernel with the host. Process-level isolation through namespaces and cgroups. Strong enough for internal workloads, weak for genuinely untrusted code. "Container escape" is a legitimate attack category.</p><p><strong>Traditional VMs (QEMU/KVM).</strong> Hardware isolation through a hypervisor. Strong security. Slow start, measured in seconds or minutes. Memory overhead in the hundreds of MB per instance.</p><p>AWS needed something for Lambda. Thousands of untrusted functions from different tenants per physical host. Millisecond start times. Strict isolation. Nothing on the shelf fit. QEMU was too heavy. Containers weren't strong enough.</p><p>Firecracker is the answer AWS built. A VMM that keeps the hardware isolation of a VM but strips out everything that isn't needed for ephemeral stateless workloads.</p><h2>What got cut</h2><p>Firecracker is minimalism as a security feature. The things QEMU emulates that Firecracker refuses to:</p><ul><li><p>No USB, no PCI bus, no BIOS, no graphics. A running microVM sees a minimal virtio-net device, a virtio-block device, a serial console, and one keyboard key for reboot. That's the full hardware surface.</p></li><li><p>No device passthrough. If you need a GPU, Firecracker is the wrong tool. Cloud Hypervisor or QEMU with VFIO handles that.</p></li><li><p>No live migration, no complex storage features, no snapshots (for a long time, though snapshots were added later).</p></li><li><p>No Windows guest support. Linux and OSv only.</p></li></ul><p>Everything cut is attack surface removed. A minimal device model is a minimal set of bugs.</p><h3>Links</h3><ul><li><p><a href="https://github.com/firecracker-microvm/firecracker">firecracker-microvm/firecracker</a></p></li><li><p><a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/design.md">Firecracker design overview</a></p></li></ul><h2>The architecture that makes sub-second boot work</h2><p>Three choices explain the performance:</p><p><strong>Rust at the foundation.</strong> Memory safety guarantees eliminate a whole class of bugs that plagued C-based hypervisors. Firecracker's CVE list is noticeably shorter than QEMU's for this reason.</p><p><strong>API-driven, not CLI-driven.</strong> Firecracker exposes a REST API over a Unix socket. You POST a machine configuration, POST a disk image path, POST a kernel image path, then send an InstanceStart action. No process spawning, no command-line parsing, no shell. Orchestrators build against the API directly.</p><p><strong>Jailer.</strong> A separate binary that sandboxes the Firecracker process itself using cgroups, namespaces, chroot, and seccomp-bpf syscall filters. If a guest escapes the microVM, it lands in a jailed process with minimal privileges. Two layers of isolation, both required.</p><p>The boot numbers are the headline:</p><ul><li><p>Startup to running guest code: under 125 ms.</p></li><li><p>VMM memory overhead: under 5 MB per microVM.</p></li><li><p>Density on i3.metal: thousands of microVMs per physical host.</p></li></ul><p>Built-in rate limiting for network and block I/O at the VMM layer means noisy-neighbor problems don't propagate across microVMs sharing a host.</p><h2>Where Firecracker actually runs in production</h2><p>Four categories, plus the one people forget:</p><ol><li><p><strong>AWS Lambda.</strong> The default execution environment. Your function runs inside a Firecracker microVM that was ready before your request landed.</p></li><li><p><strong>AWS Fargate.</strong> The task runtime for ECS and EKS. Firecracker under the hood, presenting a container API.</p></li><li><p><strong>Fly.io.</strong> Entire platform built on Firecracker as the primitive.</p></li><li><p><strong>Kata Containers.</strong> A CNCF-sandbox project that runs standard OCI containers inside lightweight VMs for stronger isolation. Kata supports multiple VMMs; Firecracker is one of the popular backends. If your cluster has <code>runtimeClassName: kata-fc</code>, Firecracker is the boundary.</p></li><li><p><strong>AI-agent sandboxes and sandbox-as-a-service platforms.</strong> E2B, Daytona, Modal, and the emerging category of remote code-execution platforms all use Firecracker-derived microVMs for isolating tool calls from agents or untrusted snippets from customer tenants. Strong boundary plus millisecond start is the combination that makes the category work.</p></li></ol><h3>Links</h3><ul><li><p><a href="https://www.usenix.org/conference/nsdi20/presentation/agache">Firecracker - Lightweight Virtualization for Serverless Applications (NSDI 2020 paper)</a></p></li><li><p><a href="https://katacontainers.io/">Kata Containers project</a></p></li><li><p><a href="https://fly.io/blog/sandboxing-and-workload-isolation/">Fly.io: Firecracker - Start a VM in Less Than a Second</a></p></li></ul><h2>Where Firecracker is the wrong tool</h2><p>Three categories of workload that Firecracker can't handle:</p><ul><li><p><strong>GPU-dependent workloads.</strong> No PCI passthrough, no direct device access. ML inference and training stay on QEMU or bare metal.</p></li><li><p><strong>Stateful databases.</strong> Disks are ephemeral by design. You can configure persistent storage, but you're working against the grain.</p></li><li><p><strong>Windows or macOS guests.</strong> Not supported. Linux guest kernel only, with OSv as the other option for unikernel use cases.</p></li></ul><p>If your use case needs any of these, look at Cloud Hypervisor (also Rust, newer, different design priorities), Kata with QEMU backend, or just QEMU directly.</p><h2>The comparison that matters for platform decisions</h2><p>A clean mental model for boundary strength:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!10iR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!10iR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 424w, https://substackcdn.com/image/fetch/$s_!10iR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 848w, https://substackcdn.com/image/fetch/$s_!10iR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 1272w, https://substackcdn.com/image/fetch/$s_!10iR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!10iR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png" width="1832" height="1098" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1098,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!10iR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 424w, https://substackcdn.com/image/fetch/$s_!10iR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 848w, https://substackcdn.com/image/fetch/$s_!10iR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 1272w, https://substackcdn.com/image/fetch/$s_!10iR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e9fea5-a078-4abb-98e9-628594c74a59_1832x1098.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>Docker container</strong> - boundary: shared kernel + namespaces, start: ms, overhead: MB, untrusted-safe: NO</p></li><li><p><strong>gVisor</strong> - boundary: userspace kernel, start: 100s of ms, overhead: tens of MB, untrusted-safe: yes (weak)</p></li><li><p><strong>Firecracker microVM</strong> - boundary: KVM hypervisor, start: ~125 ms, overhead: 5 MB VMM, untrusted-safe: YES (strong)</p></li><li><p><strong>QEMU VM</strong> - boundary: full hypervisor, start: seconds, overhead: 100s of MB, untrusted-safe: YES (strong)</p></li></ul><p>The rows aren't interchangeable. Picking Firecracker over Docker is a conscious trade: stronger isolation for the overhead of a VMM. Picking Firecracker over QEMU is a different trade: less feature surface for faster boot and lower overhead.</p><p>For your own platform, the decision usually comes down to three questions:</p><ul><li><p>Is the workload trusted or untrusted? (Trusted &#8594; Docker. Untrusted &#8594; microVM or VM.)</p></li><li><p>Does it need hardware passthrough or Windows? (Yes &#8594; QEMU. No &#8594; Firecracker.)</p></li><li><p>Does it need to start in under a second at scale? (Yes &#8594; Firecracker. No &#8594; QEMU is fine.)</p></li></ul><p>Those three cover most real decisions.</p><h3>Links</h3><ul><li><p><a href="https://github.com/cloud-hypervisor/cloud-hypervisor">cloud-hypervisor/cloud-hypervisor</a></p></li><li><p><a href="https://github.com/google/gvisor">google/gvisor</a></p></li></ul><h2>The operational pieces that aren't in the Getting Started docs</h2><p>If you stand up Firecracker yourself (outside a managed platform), a few things matter:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MbA3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MbA3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 424w, https://substackcdn.com/image/fetch/$s_!MbA3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 848w, https://substackcdn.com/image/fetch/$s_!MbA3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 1272w, https://substackcdn.com/image/fetch/$s_!MbA3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MbA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png" width="1832" height="1098" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1098,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MbA3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 424w, https://substackcdn.com/image/fetch/$s_!MbA3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 848w, https://substackcdn.com/image/fetch/$s_!MbA3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 1272w, https://substackcdn.com/image/fetch/$s_!MbA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3a8271c-b18c-425d-a973-818aaf8f2adc_1832x1098.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>Kernel choice for the guest.</strong> Firecracker documents a minimal Linux kernel config. Running a distro kernel inside works but wastes boot time. The "alpine-microvm" or custom-built minimal kernel is the right choice.</p></li><li><p><strong>Jailer is not optional.</strong> Running Firecracker without the jailer is a production mistake. Every serious deployment runs jailer.</p></li><li><p><strong>Snapshot/restore (added in later versions).</strong> Lets you pre-create a microVM in memory and restore it to a fresh copy in milliseconds. A key primitive for warm-pool patterns in FaaS, AI-agent sandbox platforms, and any sandbox-as-a-service that needs sub-second cold start.</p></li><li><p><strong>Networking model.</strong> Firecracker expects a tap device per microVM. At density, this becomes a host-networking concern, not a VMM concern. Common pattern: one physical host with thousands of tap devices bridged through a fast virtual switch.</p></li></ul><h3>Links</h3><ul><li><p><a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/jailer.md">Firecracker jailer docs</a></p></li><li><p><a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/snapshotting/snapshot-support.md">Snapshot/restore guide</a></p></li><li><p><a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/prod-host-setup.md">Production host setup</a></p></li></ul><h2>Summary</h2><p>Firecracker is what happens when a single use case (secure multi-tenant serverless) forces a complete rewrite of the VMM concept. The result is narrower than QEMU, stronger than a container, fast enough that boot latency stops dominating your worst-case tail.</p><p>If you're building a platform that runs untrusted code at scale, Firecracker belongs in your stack. If you're running standard workloads on standard clusters, Kata-with-Firecracker is an option for tenants you don't trust. And if you're watching the AI-agent sandbox and sandbox-as-a-service category, Firecracker-derived microVMs are the primitive that makes the rest of the security model work.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/p/firecracker-microvm-lambda-isolation-rust-vmm?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/p/firecracker-microvm-lambda-isolation-rust-vmm?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><p><em>For the container-runtime context including the Wasm alternative at the other extreme of the boundary axis, see <a href="/p/dapr-kargo-wasmedge-koordinator-openfeature">Tools From the Future</a>. For K8s-native runtime options, <a href="/p/ebpf-tetragon-parca-falco-sloth-alloy">eBPF Beyond Networking</a> covers Tetragon as a complementary runtime-security primitive.</em></p>]]></content:encoded></item><item><title><![CDATA[Podo #016: Kyverno Isn't Security. It's Governance - And You're Using 20% of It.]]></title><description><![CDATA[Mutating admission patterns, generate rules for cross-namespace propagation, cleanup policies with kor, CronJob silent failures, and the image hygiene pipeline]]></description><link>https://podostack.com/p/kyverno-beyond-admission-governance</link><guid isPermaLink="false">https://podostack.com/p/kyverno-beyond-admission-governance</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 05 May 2026 14:00:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!tNs1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tNs1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tNs1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!tNs1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!tNs1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!tNs1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tNs1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1303820,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434888?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tNs1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!tNs1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!tNs1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!tNs1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F776ef7d7-e921-4e84-bbe2-1b38e37a7329_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Welcome back to Podo Stack. Kyverno shows up in every platform stack these days, and it almost always arrives the same way: some team ships 80 admission policies they copied from a blog post, mostly image-signing and pod-security, and then declares "Kyverno is done." That's the checklist view.</p><p>Last week (<a href="https://podostack.com/p/cold-start-pod-first-60-seconds-cgroup-stargz">Podo #015</a>) I walked through how an attacker reads a pod's first 60 seconds and how each step in the pivot chain maps to a defensive control you probably already own. This week is the flip side: the governance engine those controls actually live in, and why most teams are using a fraction of what Kyverno can do.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>Five surfaces, five rule types, one engine.</p><p>Here's what's good this week.</p><div><hr></div><h2>Mutating admission is where the real power lives</h2><h3>Most teams reach for Validate when Mutate would be safer.</h3><p>A Validating policy rejects the request. Developer hits the wall, files a ticket, platform team writes an exception, and three releases later the policy rots into a set of exemptions nobody can reason about. A Mutating policy fixes the request silently. Developer never notices. The policy stays clean.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2JGi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2JGi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 424w, https://substackcdn.com/image/fetch/$s_!2JGi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 848w, https://substackcdn.com/image/fetch/$s_!2JGi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 1272w, https://substackcdn.com/image/fetch/$s_!2JGi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2JGi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png" width="1832" height="1950" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1950,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2JGi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 424w, https://substackcdn.com/image/fetch/$s_!2JGi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 848w, https://substackcdn.com/image/fetch/$s_!2JGi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 1272w, https://substackcdn.com/image/fetch/$s_!2JGi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e90bcb1-ce13-4dc8-b3d2-08a45cc651db_1832x1950.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Patterns that belong in Mutate, not Validate:</p><ul><li><p>Inject resource requests when a Pod omits them, so you stop landing in BestEffort QoS by accident.</p></li><li><p>Add a default securityContext (<code>runAsNonRoot: true</code>, <code>readOnlyRootFilesystem: true</code>) to legacy charts you don't own.</p></li><li><p>Strip the service account token (<code>automountServiceAccountToken: false</code>) for workloads that don't call the Kubernetes API. One line of governance closes Step 1 of last week's pivot chain.</p></li><li><p>Add cost-center labels from namespace annotations. FinOps attribution without involving application teams.</p></li><li><p>Rewrite image references to pull from an internal registry mirror, so egress auditing lives in one place.</p></li></ul><p>The architectural idea: Validate is the loud gate that stops bad things from entering, Mutate is the quiet correction that makes them acceptable on the way in. Both belong in a platform. Most platforms ship only the loud one.</p><p>There's a real warning attached. Mutate can hide misconfiguration. A pod arrives malformed, Kyverno silently fixes it, nobody learns. Pair every non-trivial Mutate with a policy report, and pipe it to an OpenTelemetry span or an audit stream. Without that trail, Mutate becomes a shadow config layer that outlives the engineer who wrote it.</p><p>Rule of thumb I use: if the fix is objective and the developer has no legitimate reason to object, Mutate. If there's a judgment call or a domain meaning the platform shouldn't override, Validate.</p><h3>Links</h3><ul><li><p><a href="https://kyverno.io/docs/writing-policies/mutate/">Kyverno Mutate Resources docs</a></p></li><li><p><a href="https://kyverno.io/docs/policy-reports/">Kyverno PolicyReports</a></p></li><li><p><a href="https://kubernetes.io/docs/concepts/security/pod-security-standards/">Pod Security Standards</a></p></li></ul><div><hr></div><h2>Generate rules make cross-namespace propagation trivial</h2><h3>Stop reaching for reflector-style third-party tools.</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BTAB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BTAB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!BTAB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!BTAB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!BTAB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BTAB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1708495,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434888?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BTAB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!BTAB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!BTAB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!BTAB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c3f92-de45-45f9-aca4-8e74e57d0b55_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Every platform team eventually has to distribute the same resource across many namespaces. A TLS wildcard cert. A docker-registry pull secret. A default NetworkPolicy baseline. A CA bundle for in-house registries. The usual answers are all wrong:</p><ul><li><p>Helm post-install hooks run once, drift after any change.</p></li><li><p>Argo CD ApplicationSets get ugly for dozens of tenants.</p></li><li><p>Reflector (the third-party controller) is one more binary in your supply chain.</p></li></ul><p>Kyverno Generate rule is the clean answer. Declare one source resource, declare a destination selector, Kyverno watches both. On source change, downstream copies update. On namespace create, generate fires immediately. With <code>synchronize: true</code>, it becomes bidirectional: manual edits on a downstream copy get reverted back to source.</p><p>Concrete examples worth building:</p><ul><li><p>Docker-registry pull secret in <code>platform-system</code>, copied into every namespace with label <code>pull-private: true</code>.</p></li><li><p>cert-manager-issued wildcard TLS secret in <code>cert-manager</code>, copied into every tenant namespace that needs ingress.</p></li><li><p>A baseline NetworkPolicy in <code>policy-templates</code>, instantiated in every new namespace as the default-deny starting point. This alone closes an entire class of lateral-movement attacks from last week's Step 3.</p></li></ul><p>One anti-pattern worth calling out: don't generate ServiceAccounts with broad permissions via Generate rules. Technically it works. It also breaks RBAC reasoning because the identity that holds a privilege was created by a cluster-scope controller rather than declared by the namespace owner. Generate is for data. RBAC still belongs in the hands of the team that owns the namespace.</p><h3>Links</h3><ul><li><p><a href="https://kyverno.io/docs/writing-policies/generate/">Kyverno Generate Resources docs</a></p></li><li><p><a href="https://github.com/emberstack/kubernetes-reflector">emberstack/kubernetes-reflector (compare)</a></p></li><li><p><a href="https://cert-manager.io/">cert-manager</a></p></li></ul><div><hr></div><h2>Cleanup policies plus kor: making orphans visible</h2><h3>Governance is also about what leaves the cluster.</h3><p>Every cluster accretes orphans. ConfigMaps from deleted Deployments. Secrets from rotated workloads. Services pointing at Pods that are gone. ServiceAccounts with no RoleBindings. Roles that nothing binds to. It's the kind of debt nobody wants to own and everyone pays for.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2uDY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2uDY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 424w, https://substackcdn.com/image/fetch/$s_!2uDY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 848w, https://substackcdn.com/image/fetch/$s_!2uDY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 1272w, https://substackcdn.com/image/fetch/$s_!2uDY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2uDY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png" width="1832" height="1726" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1726,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2uDY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 424w, https://substackcdn.com/image/fetch/$s_!2uDY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 848w, https://substackcdn.com/image/fetch/$s_!2uDY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 1272w, https://substackcdn.com/image/fetch/$s_!2uDY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ebe783-eeea-468d-91b0-b4c85b3cf4ad_1832x1726.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Two tools that compose cleanly.</p><p>Kyverno CleanupPolicy (built-in since 1.10) gives you declarative TTL. "PVC untouched for 30 days, delete it." Scheduled cleanup via cron expression, or on-event triggers. Always run in <code>auditing: true</code> mode for a week before switching to enforce, because the first cleanup policy a team writes is almost always too eager.</p><p><code>kor</code> (github.com/yonahd/kor) is a CLI that finds orphans Kubernetes-native tools miss. It doesn't just list unused ConfigMaps, it finds Secrets that aren't mounted, Services whose selectors match zero Pods, ServiceAccounts that nothing references, ClusterRoles with no bindings. Output in <code>table|json|yaml|csv</code> so it slots into any pipeline.</p><p>A workflow that earns its keep:</p><ol><li><p>Run <code>kor all --output json</code> as a scheduled Job in each namespace.</p></li><li><p>Pipe findings to a Slack channel per team. Namespace owners decide what's safe to delete.</p></li><li><p>Promote high-confidence categories (abandoned Secrets older than 90 days, Services with no endpoints for 30 days) into Kyverno CleanupPolicy as enforced rules.</p></li></ol><p>Why this matters beyond cost: an orphaned Secret is a credential-rotation blind spot. The pentester's view from last week (<a href="https://podostack.com/p/cold-start-pod-first-60-seconds-cgroup-stargz">Podo #015</a>, Step 1) lists SA tokens as the primary pivot. If nobody knows that Secret exists, nobody rotates it, and the pivot path stays open indefinitely. Hygiene is security.</p><h3>Links</h3><ul><li><p><a href="https://kyverno.io/docs/writing-policies/cleanup/">Kyverno CleanupPolicy docs</a></p></li><li><p><a href="https://github.com/yonahd/kor">yonahd/kor - Kubernetes Orphaned Resources</a></p></li></ul><div><hr></div><h2>CronJob has seven failure modes nobody warned you about</h2><h3>The quietest workload in Kubernetes has the loudest silent failures.</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oPt2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oPt2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!oPt2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!oPt2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!oPt2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oPt2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1477091,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434888?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oPt2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!oPt2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!oPt2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!oPt2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe52c5aa3-855f-4ad0-a2cc-5373d9db3be8_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>CronJob looks simple. You write a cron expression, you get a scheduled Job. No syslog-style notification when it fails. No on-call alert when it stops running. Three layers (CronJob &#8594; Job &#8594; Pod), each with its own silent-failure mode.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!liwe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!liwe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 424w, https://substackcdn.com/image/fetch/$s_!liwe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 848w, https://substackcdn.com/image/fetch/$s_!liwe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 1272w, https://substackcdn.com/image/fetch/$s_!liwe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!liwe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png" width="1832" height="2128" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2128,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!liwe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 424w, https://substackcdn.com/image/fetch/$s_!liwe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 848w, https://substackcdn.com/image/fetch/$s_!liwe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 1272w, https://substackcdn.com/image/fetch/$s_!liwe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d33c48-8e8a-4ea1-a30a-5ca2086d7278_1832x2128.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Seven modes, ordered by frequency in real clusters:</p><ol><li><p><strong>concurrencyPolicy: Forbid</strong>. Default is Allow, but many charts flip this to Forbid to avoid overlap. If the previous Job is still running when the next schedule fires, the new one is silently skipped. No error, no event, just a missed run.</p></li><li><p><strong>startingDeadlineSeconds too small</strong>. If the controller lags behind (busy apiserver, scheduler backlog) beyond this window, the invocation is skipped. Set it too short, you miss runs under load. Leave it null, it's valid forever. Some charts ship a 100-second default that is a trap on busy clusters.</p></li><li><p><strong>backoffLimit exhausted</strong>. After N Pod failures (default 6), Kubernetes suspends the Job. The next scheduled time creates a fresh Job, but the suspended one lingers in <code>kubectl get jobs</code> as confusing noise, and nobody alerts you that your nightly backup stopped three days ago.</p></li><li><p><strong>successfulJobsHistoryLimit and failedJobsHistoryLimit</strong>. Set to zero, you lose debug artifacts the moment something breaks. Set too high, etcd pays the bill.</p></li><li><p><strong>Resource constraints, Pod stuck Pending</strong>. No Node fits the requests. CronJob itself doesn't time out a Pending Pod. <code>activeDeadlineSeconds</code> on the Job is the watchdog; set it explicitly.</p></li><li><p><strong>ImagePullBackOff on a stale image reference</strong>. Nobody notices that the nightly backup hasn't pulled a fresh image in three weeks because the registry auth rotated. The Job fires, can't pull, backs off, cycles through retries, exhausts backoffLimit, goes silent.</p></li><li><p><strong>Kyverno-blocked Job</strong>. An admission denial returns no retry. The CronJob controller marks the invocation complete without ever creating a Pod. Your policy is doing the right thing and your scheduler is doing the right thing, and together they silently skip a workload.</p></li></ol><p>Vendor-neutral monitoring, because there's no native answer:</p><ul><li><p><code>kube_cronjob_next_schedule_time</code> from kube-state-metrics. Alert when <code>time() - last_schedule_time &gt; expected_interval * 2</code>.</p></li><li><p>A heartbeat beacon emitted from inside the Job container to an external endpoint. Independent of Kubernetes state, so you notice when Kubernetes lies to you.</p></li><li><p>A Kyverno validate rule that rejects CronJobs without an explicit <code>startingDeadlineSeconds</code> and <code>activeDeadlineSeconds</code>, forcing the author to make the decision.</p></li></ul><p>CronJob is the surface where Kyverno governance earns its keep at the architectural layer. The failure modes aren't bugs, they're the K8s API doing exactly what it said in the spec, silently.</p><h3>Links</h3><ul><li><p><a href="https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/">Kubernetes CronJob reference</a></p></li><li><p><a href="https://github.com/kubernetes/kube-state-metrics">kube-state-metrics</a></p></li><li><p><a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/">Kubernetes Job concepts (backoffLimit, activeDeadlineSeconds)</a></p></li></ul><div><hr></div><h2>The image hygiene pipeline</h2><h3>Seven vendor-neutral tools, one lifecycle.</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-mRq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-mRq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-mRq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-mRq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-mRq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-mRq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1540884,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434888?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-mRq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-mRq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-mRq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-mRq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0db620ef-001d-436e-8604-e867e8b04f56_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This could be seven compilation blocks. It's more useful as one pipeline. Each stage has a tool that does one thing well, and the seams between stages are where platforms earn their quality.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q006!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q006!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 424w, https://substackcdn.com/image/fetch/$s_!Q006!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 848w, https://substackcdn.com/image/fetch/$s_!Q006!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 1272w, https://substackcdn.com/image/fetch/$s_!Q006!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q006!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png" width="1832" height="1726" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1726,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Q006!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 424w, https://substackcdn.com/image/fetch/$s_!Q006!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 848w, https://substackcdn.com/image/fetch/$s_!Q006!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 1272w, https://substackcdn.com/image/fetch/$s_!Q006!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9069467-0b10-42f9-9ebc-a338d0e0e4ae_1832x1726.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Stage 1 - before build.</strong> Hadolint lints the Dockerfile statically. Catches anti-patterns (ADD instead of COPY, apt-get without cleanup, wildcard tags) before a single byte of CI compute burns. Runs in pre-commit and in CI, identical rules.</p><p><strong>Stage 2 - build validation.</strong> Dive walks the layered image and scores efficiency. "Your image is 1.2 GB and 40% is wasted in inter-layer churn" is a blameless ticket nobody fights. Docker Time Machine adds the time axis: how did this image grow across 20 commits, which PR bloated it by 300 MB. <code>dtm registry myapp --last 20</code> plots the curve. commit-bisect finds the culprit. Small caveat: DTM has 64 stars and slowed down in late 2025, so check repo pulse before baking into your pipeline. If it stalls, Dive alone still earns Stage 2.</p><p><strong>Stage 3 - registry operations.</strong> Skopeo and Google's crane both do registry ops without a Docker daemon. Skopeo copies, inspects, and deletes across registries. crane reads partial layers, does content addressing, and fits in slim CI containers. Both CRI-agnostic, both safe to run in ephemeral build agents.</p><p><strong>Stage 4 - admission (Kyverno VerifyImages).</strong> This is the supply-chain gate. Kyverno checks Cosign signatures (keyless flow via OIDC, signatures anchored in the rekor transparency log) and SLSA provenance attestations before allowing a Pod to start. Fail-closed: unsigned image, Pod rejection, human escalation. <a href="https://podostack.com/p/signed-images-runtime-watchtowers-docker-pull-act-of-faith">Podo #005</a> walked through why you want this, and the Sigstore/SLSA ecosystem has matured since. If you implemented #005 already, this is where Kyverno enforces the artifact you produced then.</p><p><strong>Stage 5 - node cleanup.</strong> Eraser removes stale images from nodes. Kyverno CleanupPolicy scrubs workloads that use <code>imagePullPolicy: Always</code> on tags that no longer exist in the registry. Together they close the loop on images that otherwise accumulate quietly on every node forever.</p><p>The architectural point: this is one pipeline, not seven tools. A team that adopts Dive without Eraser fixes their build efficiency and still ships nodes with 40 GB of dead image layers. A team that enforces VerifyImages without Hadolint blocks a lot of things at admission that should have been caught in CI. The value is in the seams.</p><h3>Links</h3><ul><li><p><a href="https://github.com/hadolint/hadolint">hadolint/hadolint - Dockerfile linter</a></p></li><li><p><a href="https://github.com/wagoodman/dive">wagoodman/dive - image layer explorer</a></p></li><li><p><a href="https://github.com/containers/skopeo">containers/skopeo</a></p></li><li><p><a href="https://kyverno.io/docs/writing-policies/verify-images/">Kyverno verifyImages with Cosign</a></p></li><li><p><a href="https://github.com/eraser-dev/eraser">eraser-dev/eraser</a></p></li></ul><div><hr></div><h2>Closing</h2><p>Kyverno as a governance engine, not an admission controller:</p><ul><li><p>Mutate for objective corrections, not just Validate for loud gates.</p></li><li><p>Generate for cross-namespace propagation, retire the third-party reflectors.</p></li><li><p>CleanupPolicy plus kor for the orphan debt nobody else owns.</p></li><li><p>CronJob rules for the failure modes nobody alerts on.</p></li><li><p>VerifyImages at the end of a pipeline, not as a standalone policy.</p></li></ul><p>Five rule types, one engine. If your platform ships 80 Kyverno policies and they're all Validate plus VerifyImages, you have four surfaces left to explore.</p><p>Which rule type did your team reach for last? Most platforms hit Validate and stop. Generate usually clicks next. CleanupPolicy is the one teams discover after a quarter of orphan debt.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/p/kyverno-beyond-admission-governance?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/p/kyverno-beyond-admission-governance?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p>Next week: Postgres on Kubernetes - five places where the control plane and the database fight over the same recovery, and what running CloudNativePG actually buys you.</p><p>- Ilia</p>]]></content:encoded></item><item><title><![CDATA[Server-Side Apply and managedFields: the Kubernetes feature that ends controller fights]]></title><description><![CDATA[How the API server tracks per-field ownership, why your operator and ArgoCD stopped overwriting each other, and the pitfalls you'll hit once you turn SSA on]]></description><link>https://podostack.com/p/server-side-apply-managed-fields-field-ownership</link><guid isPermaLink="false">https://podostack.com/p/server-side-apply-managed-fields-field-ownership</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Fri, 01 May 2026 14:02:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xGzq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xGzq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xGzq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!xGzq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!xGzq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!xGzq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xGzq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1887723,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195435328?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xGzq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!xGzq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!xGzq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!xGzq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a2bd4a1-0a2f-4aa4-84b7-d50ab0db747c_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most Kubernetes users never read the <code>metadata.managedFields</code> block in a YAML dump. It's boilerplate noise at the bottom of <code>kubectl get -o yaml</code>. Until the day ArgoCD and your operator start fighting over the same Deployment, the spec oscillates every five minutes, and the only signal is three-minute-old pods getting recreated nobody asked for. That's the day managedFields matters.</p><p>The structure exists because the client-side <code>kubectl apply</code> model had a fundamental flaw: the client held the "last-applied" annotation and the server had no idea who owned what. When two different actors both tried to be authoritative, whichever called apply most recently won. Server-Side Apply (SSA) moved that ownership tracking into the API server itself, per-field, so conflicts are visible, resolvable, and auditable.</p><p>Here's the full story.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><h2>What managedFields actually contains</h2><p>Every object in a cluster running SSA has a <code>managedFields</code> array in its metadata. Each entry describes one "manager" and the fields it owns:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;8b2403a1-4f8e-41bd-85b9-8115c3fca880&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">metadata:
  managedFields:
    - manager: "kubectl"
      operation: "Apply"
      apiVersion: "apps/v1"
      fieldsType: "FieldsV1"
      fieldsV1:
        f:spec:
          f:replicas: {}
          f:template:
            f:metadata:
              f:labels:
                f:app: {}
    - manager: "kube-controller-manager"
      operation: "Update"
      fieldsType: "FieldsV1"
      fieldsV1:
        f:status:
          f:availableReplicas: {}
          f:readyReplicas: {}</code></pre></div><p>Three things worth noticing.</p><p>First, ownership is per-field, not per-object. <code>kubectl</code> owns <code>spec.replicas</code> and specific labels. <code>kube-controller-manager</code> owns parts of <code>status</code>. Both coexist on the same Deployment without stepping on each other.</p><p>Second, the <code>operation</code> field distinguishes <code>Apply</code> (SSA explicit intent) from <code>Update</code> (legacy imperative PUT). The merge algorithm treats them differently.</p><p>Third, the field paths use a compact encoding (<code>f:</code> prefix, nested objects, <code>{}</code> for leaf fields). That's the FieldsV1 format. It's verbose in YAML but efficient for the server to diff.</p><h3>Links</h3><ul><li><p><a href="https://kubernetes.io/docs/reference/using-api/server-side-apply/">Server-Side Apply official docs</a></p></li><li><p><a href="https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/555-server-side-apply">KEP-555 Server-Side Apply</a></p></li></ul><h2>The conflict a client-side apply user never saw</h2><p>Imagine three actors touching the same Deployment:</p><ul><li><p>A GitOps tool (ArgoCD) reconciles the Deployment from a manifest in git.</p></li><li><p>An HPA autoscaler updates <code>spec.replicas</code> based on load.</p></li><li><p>A platform operator adds a sidecar container to <code>spec.template.spec.containers</code>.</p></li></ul><p>Under client-side apply, every one of them sends a full PUT. Last write wins. ArgoCD says "replicas should be 3", HPA says "replicas should be 17", the operator says "add sidecar". Every few minutes the values thrash. You see pods being recreated. Nobody logs anything useful because from each tool's perspective its own apply succeeded.</p><p>Under SSA, each actor declares which fields it intends to own:</p><ul><li><p>ArgoCD applies with <code>fieldManager=argocd</code> and declares ownership of everything except <code>spec.replicas</code>.</p></li><li><p>HPA applies with <code>fieldManager=horizontal-pod-autoscaler</code> and owns only <code>spec.replicas</code>.</p></li><li><p>The operator applies with <code>fieldManager=sidecar-injector</code> and owns the sidecar container entry.</p></li></ul><p>Now when ArgoCD reconciles, the API server sees ArgoCD doesn't claim <code>spec.replicas</code>, so HPA's value is preserved. When HPA scales up, the rest of the spec is untouched. When the operator injects a sidecar, ArgoCD's <code>spec.template.spec.containers</code> list is merged, not replaced.</p><p>This is the feature. Per-field ownership turns three fighting actors into three cooperating ones.</p><h2>Force, conflict, and what actually happens on collision</h2><p>When two managers both try to own the same field, SSA raises a conflict. The request fails with a 409 response and a message listing the conflicting paths. The client has three options:</p><ol><li><p><strong>Drop the conflicting field</strong> from its apply. The other manager keeps ownership.</p></li><li><p><strong>Send </strong><code>force=true</code> to take ownership. The other manager loses that field.</p></li><li><p><strong>Abort and investigate.</strong> Usually the right answer when the conflict is surprising.</p></li></ol><p><code>kubectl apply --server-side --force-conflicts</code> is the escape hatch for admins. Tools like ArgoCD and Flux offer equivalent flags. Using force without understanding the cause of the conflict is how three-way ownership oscillation starts.</p><p>The more disciplined pattern: if you repeatedly hit conflicts on a specific field, that's a signal the ownership model is wrong. Two managers shouldn't both be writing to the same field unless one of them is actively handing ownership off.</p><h3>Links</h3><ul><li><p><a href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply">kubectl apply --server-side reference</a></p></li><li><p><a href="https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/#server-side-apply">ArgoCD ServerSideApply sync option</a></p></li></ul><h2>List merge strategies: where subtle bugs live</h2><p>Lists are where SSA gets interesting. By default, a list is treated as atomic: the whole list is owned by whoever applied it. But most Kubernetes lists in practice have semantic identity per element (containers by name, ports by number, volumes by name). The API declares merge strategies for each such field using <code>x-kubernetes-list-type</code> and <code>x-kubernetes-list-map-keys</code> markers in the OpenAPI schema.</p><p>For example, <code>spec.template.spec.containers</code> is declared as <code>list-type: map</code> with <code>list-map-keys: [name]</code>. When two managers apply to the containers list, SSA merges element-by-element by container name. Manager A can own container "app", manager B can own container "sidecar", both survive.</p><p>If a list has the wrong marker, merge breaks. A common failure is a Custom Resource Definition that declares a list without map keys. Two actors apply different elements; the second apply replaces the entire list. The first actor's element silently disappears. You learn about this when something that was there is gone and nobody's manager is listed.</p><p>Operationally: when writing a CRD, always think through the list-type markers for every list field. When debugging a vanishing config, look at the OpenAPI schema of the resource and check if the list has proper identity markers.</p><h3>Links</h3><ul><li><p><a href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation">CRD validation and list-type markers</a></p></li><li><p><a href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema">structural-schema and list types</a></p></li></ul><h2>How ownership transfers happen</h2><p>Ownership isn't permanent. It transfers when a manager stops claiming a field. Three ways:</p><ol><li><p><strong>A manager applies without the field.</strong> If actor A previously owned <code>spec.replicas</code> by applying it, then applies a new manifest that omits <code>spec.replicas</code>, ownership is released. The next actor to apply the field becomes the owner.</p></li><li><p><strong>Explicit removal.</strong> A manager applies with the field present but flagged for removal (via a patch, not SSA directly). Useful for operators that want to clean up after themselves.</p></li><li><p><strong>Force takeover.</strong> Already discussed. <code>force=true</code> reassigns ownership to the caller.</p></li></ol><p>The subtle one is case 1. Many GitOps tools don't realize that adding or removing a line in a manifest changes ownership declaration on next reconcile. If you remove a field from git thinking it'll revert to default, ArgoCD stops claiming it, and whatever default the controller computes is now owned by the controller. Sometimes that's what you want. Sometimes that's a surprise.</p><h2>The operator pattern that gets SSA right</h2><p>Operators that coexist well with GitOps and HPAs follow a specific pattern:</p><ul><li><p><strong>Apply only what you own.</strong> Don't include fields in your SSA apply that belong to other actors. If your operator manages sidecars, send only the sidecar fields, not the whole <code>spec.template.spec</code>.</p></li><li><p><strong>Use a unique field manager name.</strong> <code>my-operator-v2</code>, not <code>my-operator</code>. This keeps ownership traceable across operator versions.</p></li><li><p><strong>Fail loudly on conflicts.</strong> Don't default to <code>force=true</code>. Surface conflicts as events on the managed resource so cluster operators can see what happened.</p></li><li><p><strong>Design for shared resources.</strong> Assume another actor might own adjacent fields. Don't assume you're alone.</p></li></ul><p>Crossplane (<a href="https://podostack.com/p/crossplane-infrastructure-api-compositions-claims">Podo #010</a>) is an example of a controller family that uses SSA this way by default. Karpenter's NodePool reconciliation also follows the pattern, which is part of why NodePool configurations coexist cleanly with kubectl edits.</p><h3>Links</h3><ul><li><p><a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client#Patch">controller-runtime SSA helpers</a></p></li><li><p><a href="https://github.com/crossplane/crossplane">crossplane/crossplane</a></p></li></ul><h2>When you still want client-side apply</h2><p>SSA isn't free. The server does more work per request. managedFields adds size to every object (sometimes multi-kilobyte blocks). The merge logic has edge cases (especially around map-typed fields with heterogeneous keys).</p><p>For one-shot scripts, kubectl admin fiddling, ephemeral test clusters, client-side apply remains simpler. The cost of tracking field ownership across actors is overhead you don't need when there's only one actor.</p><p>For anything with more than one source of truth (GitOps plus operator plus HPA, the common production shape), SSA is non-optional. The bugs you'll hit without it are worse than the overhead.</p><h2>Practical diagnostics</h2><p>Three commands that pay for themselves:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iDjA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iDjA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 424w, https://substackcdn.com/image/fetch/$s_!iDjA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 848w, https://substackcdn.com/image/fetch/$s_!iDjA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 1272w, https://substackcdn.com/image/fetch/$s_!iDjA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iDjA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png" width="1952" height="2262" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2262,&quot;width&quot;:1952,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iDjA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 424w, https://substackcdn.com/image/fetch/$s_!iDjA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 848w, https://substackcdn.com/image/fetch/$s_!iDjA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 1272w, https://substackcdn.com/image/fetch/$s_!iDjA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfa9407-d0a7-4011-904d-eb5e07c8a4cd_1952x2262.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ol><li><p><code>kubectl get &lt;resource&gt; -o yaml | yq '.metadata.managedFields'</code> - shows the full ownership map. Use when something changed and you don't know who changed it.</p></li><li><p><code>kubectl get &lt;resource&gt; -o json | jq '.metadata.managedFields[] | {manager, operation, fields: .fieldsV1}'</code> - structured view for scripting. Audit pipelines can parse this.</p></li><li><p><code>kubectl diff --server-side &lt;manifest&gt;</code> - shows what SSA will do before you actually apply. Includes conflict warnings. Indispensable for GitOps CI pipelines.</p></li></ol><p>One warning: <code>managedFields</code> can balloon. If dozens of actors with unique field manager names apply to the same resource over a long period, the block grows. Kubernetes 1.24+ has cleanup logic that removes entries for managers that haven't touched the object recently. Earlier clusters sometimes need manual pruning on long-lived resources.</p><h2>Summary</h2><p>managedFields is the piece of Kubernetes metadata that makes cooperation between controllers actually work. Per-field ownership, declared intent, explicit conflict surfacing. Every GitOps-meets-operator-meets-HPA production cluster depends on it, usually without realizing.</p><p>If you're writing operators, use a unique <code>fieldManager</code> name and apply only what you own. If you're running GitOps, choose a tool that uses SSA natively and set conflict policies consciously. If you're debugging a resource that keeps changing without a clear cause, <code>kubectl get -o yaml</code> and read the managedFields. The answer is usually there.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/p/server-side-apply-managed-fields-field-ownership?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/p/server-side-apply-managed-fields-field-ownership?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><p><em>For the broader GitOps context where this matters most, see <a href="/p/crossplane-infrastructure-api-compositions-claims">Your Infrastructure Has an API Now</a>. For image pipeline conflicts specifically, <a href="/p/karpenter-beyond-basics-nodepool-spot-topology">Karpenter Beyond the Demo</a> walks through NodePool/EC2NodeClass ownership cleanly.</em></p>]]></content:encoded></item><item><title><![CDATA[Prometheus WAL: the silent disk growth nobody diagnoses correctly]]></title><description><![CDATA[Write-ahead log internals, checkpointing, replay, and how cardinality becomes a disk problem]]></description><link>https://podostack.com/p/prometheus-wal-internals-cardinality-replay-corruption</link><guid isPermaLink="false">https://podostack.com/p/prometheus-wal-internals-cardinality-replay-corruption</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 29 Apr 2026 14:01:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fSgJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fSgJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fSgJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!fSgJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!fSgJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!fSgJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fSgJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1155461,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195435225?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fSgJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!fSgJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!fSgJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!fSgJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51fadc67-f5ce-42a9-8073-3b4cd73e5e29_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You don't think about the write-ahead log until the day your Prometheus takes 22 minutes to start after an OOM kill, or you notice your data/wal directory is 40 GB on a cluster that scrapes "maybe a couple thousand targets", or a dashboard query returns nothing for the last two hours and the only log line is <code>corruption detected</code>. Each of those is a WAL problem with a very specific cause you can diagnose once you know what the thing actually is.</p><p>Prometheus buffers new samples in memory (the Head Block) and simultaneously appends them to a journal on disk (the WAL). The in-memory copy is fast to query. The on-disk copy is durable. Between them, they answer "what happens if the process dies" without asking you to run a cluster of synchronously-replicated databases just to collect metrics. The tradeoff is that the journal is now an operational surface with its own failure modes.</p><p>Here's the full story.</p><h2>What the WAL actually contains</h2><p>The WAL lives at <code>data/wal/</code> inside your TSDB data directory. Inside, you'll find sequentially numbered files like <code>000002</code>, <code>000003</code>, <code>000004</code>. Each segment is capped at 128 MiB. That size is hardcoded, you can't tune it with a flag.</p><p>Inside each segment, three kinds of records:</p><ol><li><p>Raw samples: a timestamp plus a value, tagged with a series reference.</p></li><li><p>Series descriptors: the label sets for each new time series, with a monotonic ID.</p></li><li><p>Tombstones: markers for deleted data, created when you call the delete API.</p></li></ol><p>The WAL only holds the active Head Block's data, which is typically the last 2 to 3 hours of scrapes. Once Prometheus compacts older samples into a persistent block, those samples get purged from the WAL. What remains in the WAL is whatever the running Head Block still needs. The journal is a window, not an archive.</p><h3>Links</h3><ul><li><p><a href="https://prometheus.io/docs/prometheus/latest/storage/">Prometheus TSDB storage docs</a></p></li><li><p><a href="https://github.com/prometheus/prometheus">prometheus/prometheus on GitHub</a></p></li></ul><h2>Ingestion through the WAL: the write path</h2><p>When a scrape completes, each new sample goes through three phases. First, the sample gets appended to the current active WAL segment on disk. Second, it gets inserted into the in-memory Head Block so queries can see it immediately. Third, when the active segment crosses 128 MiB, Prometheus creates a new segment and starts writing there.</p><p>The order matters. If the process crashes between the WAL append and the Head insert, the next startup will replay the WAL and rebuild the Head. If the crash happens before the WAL append completes, that sample is simply lost, which is the expected tradeoff for a metrics system as opposed to a bank ledger.</p><p>Rotation is continuous. A busy Prometheus scraping thousands of targets rotates segments every few minutes. A quiet one might keep the same segment for an hour.</p><h2>Checkpointing: the mechanism that makes WAL bounded</h2><p>This is the part most engineers never look at, and it's where the real magic is.</p><p>Every two hours (by default, driven by <code>min_block_duration</code>), the Head Block truncates. The oldest samples compact into a persistent block on disk, and the Head now covers a shorter time window. But the WAL still contains records for samples that are now in the persistent block, plus records for series that no longer have any samples in the Head.</p><p>To clean this up, Prometheus writes a checkpoint. A checkpoint is essentially a filtered WAL, containing only the records that are still relevant to the active Head:</p><ul><li><p>Series descriptors for series that still have active samples.</p></li><li><p>Raw samples within the current Head window.</p></li><li><p>Tombstones that haven't been fully applied yet.</p></li></ul><p>After the checkpoint finishes, Prometheus deletes the old WAL segments. The WAL size drops. The next replay after a crash will read the checkpoint plus any WAL segments written after it.</p><p>This is why WAL size is bursty. A healthy Prometheus sawtooths: grows linearly between compactions, drops at each compaction boundary. If you watch <code>data/wal/</code> over time and see the sawtooth, you're fine. If you see monotonic growth with no drops, something is wrong. Usually the something is cardinality.</p><h2>Cardinality as a WAL problem, not a memory problem</h2><p>Most writeups about cardinality focus on RAM. Prometheus running out of memory, query OOMs, slow lookups. That's all true. The less-discussed side is that every new series gets a new series descriptor record in the WAL, and every scrape of every series appends another sample record.</p><p>If your services legitimately expose 50 series each and you have 200 services, your WAL grows at a rate determined by 10,000 series times your scrape interval. Predictable and manageable.</p><p>If your services expose 50 base series but those series carry a <code>pod_template_hash</code> label and you deploy four times a day, you're actually generating new series every deployment. Each deployment means 10,000 new series descriptor records in the WAL, and then the old 10,000 series keep getting samples until the Head drops them.</p><p>"High churn" is the Prometheus jargon for this. Labels that change frequently. Kubernetes pods with hashed names. Kafka consumer groups with generation IDs. Job labels that include the CI build ID. Each of these quietly inflates the WAL because every unique label combination creates a new series, and the series descriptor has to live in the WAL until the checkpoint catches up.</p><p>Diagnose churn with one PromQL query:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;cc69c481-657a-4c09-a834-b7b92c2a0037&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">rate(prometheus_tsdb_head_series_created_total[1h])</code></pre></div><p>Divide by the rate of deletion. If creation runs an order of magnitude faster than deletion, you have churn. The WAL is telling you.</p><h3>Links</h3><ul><li><p><a href="https://prometheus.io/docs/practices/naming/">Prometheus best practices: histograms and labels</a></p></li><li><p><a href="https://prometheus.io/blog/2023/03/14/prometheus-2-43-0-released/">Cardinality is key (Prometheus blog)</a></p></li></ul><h2>Why your Prometheus took 22 minutes to start</h2><p>When Prometheus boots, it doesn't trust that memory is correct. The Head Block starts empty. To rebuild it, Prometheus reads the most recent checkpoint, then reads every WAL segment written after that checkpoint, replaying the records in order.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!76ee!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!76ee!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 424w, https://substackcdn.com/image/fetch/$s_!76ee!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 848w, https://substackcdn.com/image/fetch/$s_!76ee!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 1272w, https://substackcdn.com/image/fetch/$s_!76ee!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!76ee!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png" width="1832" height="1680" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1680,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!76ee!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 424w, https://substackcdn.com/image/fetch/$s_!76ee!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 848w, https://substackcdn.com/image/fetch/$s_!76ee!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 1272w, https://substackcdn.com/image/fetch/$s_!76ee!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b9b4a30-0d2f-4ce7-9e01-f97748bf3fa1_1832x1680.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The time this takes is linear in the size of the WAL. A 1 GB WAL replays in maybe 30 seconds on a reasonable disk. A 20 GB WAL (which happens in churn-heavy clusters) can take 20 minutes. Your Prometheus is up but not ingesting until the replay completes. Grafana dashboards dependent on that Prometheus are lying to you the entire time.</p><p>Two mitigations, in order of reach:</p><ol><li><p><strong>Enable WAL compression.</strong> The flag is <code>--storage.tsdb.wal-compression</code>, on by default since Prometheus 2.20. If you're running an older version, turn it on explicitly. Snappy-compressed WAL reads faster because there's less disk to read. Typical 2-3x size reduction.</p></li><li><p><strong>Reduce cardinality.</strong> Everything in the previous section. Drop the <code>pod_template_hash</code> label, use service-level aggregation, metric_relabel_configs to blacklist high-cardinality labels at scrape time. This is the structural fix.</p></li></ol><p>There's a third mitigation that's an escape hatch, not a solution: <strong>delete the WAL directory.</strong> Your last 2-3 hours of data go away but Prometheus starts clean. Do this only when the alternative is hours of unavailability and you're willing to accept the data loss.</p><h3>Links</h3><ul><li><p><a href="https://prometheus.io/docs/prometheus/latest/command-line/prometheus/">Prometheus storage flags reference</a></p></li><li><p><a href="https://github.com/prometheus/prometheus/releases/tag/v2.20.0">WAL compression release announcement (2.20)</a></p></li></ul><h2>WAL corruption: what actually happens</h2><p>Disk failures, filesystem issues, or interrupted writes during a crash can produce a corrupted WAL segment. Startup fails with <code>corruption detected at record N in segment 00000X</code>.</p><p>Since Prometheus 2.10 or so, recovery is mostly automatic. Prometheus skips the corrupt record, logs a warning, and continues replay from the next valid record. You lose whatever samples were in the bad record, typically a few seconds of data for a subset of series.</p><p>The remaining manual cases:</p><ul><li><p>Prometheus crashes during replay, unable to skip. Delete the bad segment manually (find it by log line), restart. You lose that segment's samples but keep the rest.</p></li><li><p>A checkpoint is corrupt. Delete the checkpoint file and let Prometheus recompute from the preceding WAL segments.</p></li><li><p>Everything is corrupt. Delete <code>data/wal/</code> entirely. Keep your persistent blocks in <code>data/</code> untouched. You lose the last Head window (2-3 hours), historical data is intact.</p></li></ul><p>The right default is to treat the WAL as recoverable but ephemeral. Never manually edit it. Never try to recover specific records. Either you trust Prometheus to skip and move on, or you wipe and accept the loss.</p><h2>Disk layout that actually matters</h2><p>If you're sizing storage, the WAL and the persistent blocks live under the same <code>data/</code> directory. They grow differently:</p><ul><li><p>WAL: bursty, sawtooth, bounded by Head Block window times churn rate. Rarely over 10 GB on a well-tuned instance.</p></li><li><p>Persistent blocks: monotonic in retention window times ingestion rate. Can be hundreds of GB or TBs.</p></li></ul><p>If you run Prometheus on a single filesystem, the WAL can sometimes fill the disk before the persistent blocks notice. The symptom is <code>write error: disk full</code> in the logs and all scrapes failing simultaneously. The fix is either to put <code>data/wal/</code> on a separate volume with its own capacity guarantee, or to monitor free space on the shared volume with an alert well below full.</p><h2>The three signals to actually alert on</h2><p>Most Prometheus monitoring setups alert on <code>up{job="prometheus"} == 0</code>. That's fine but it's a lagging signal. Three leading signals that catch WAL issues before they become outages:</p><ol><li><p><code>prometheus_tsdb_wal_corruptions_total</code>: any non-zero rate means disk or filesystem trouble. Page on a rate greater than zero.</p></li><li><p><code>prometheus_tsdb_head_series</code>: plot over time. A linear climb without drops means your checkpoints are not catching up, cardinality is probably exploding. Warn if growth is 2x over 24h.</p></li><li><p><code>prometheus_tsdb_head_max_time</code><strong> - </strong><code>prometheus_tsdb_head_min_time</code>: the Head window. Should be roughly <code>min_block_duration</code>. If it drifts wider, compaction is falling behind. Warn if it exceeds 3 hours.</p></li></ol><p>These three capture the main operational failure modes: corruption, cardinality explosion, and compaction lag. None of them are in the default dashboards shipped with kube-prometheus-stack, so you have to add them yourself.</p><h3>Links</h3><ul><li><p><a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack">prometheus-community/kube-prometheus-stack</a></p></li><li><p><a href="https://prometheus.io/docs/prometheus/latest/feature_flags/">Prometheus self-monitoring metrics</a></p></li></ul><h2>Summary</h2><p>The WAL isn't complicated once you see the mechanics. A bounded journal, checkpointed every two hours, replayed on startup. The failure modes are all downstream of one root cause: your metric shape. Low cardinality plus compression plus one filesystem with headroom is a WAL that takes care of itself. High churn plus no compression plus a shared volume is a WAL that will wake you up on a weekend.</p><p>If you only walk away with one operational change, it's this: turn on WAL compression, alert on <code>prometheus_tsdb_wal_corruptions_total</code>, and put a cardinality SLO in your FinOps conversation. The WAL will stop being interesting, which is what you want.</p><div><hr></div><p><em>More on observability cost math in our <a href="/p/prometheus-finops-cardinality-cost">Prometheus Cardinality FinOps</a> deep dive. For the eBPF-based alternative that measures per-workload resource pressure directly, see <a href="/p/ebpf-tetragon-parca-falco-sloth-alloy">eBPF Beyond Networking</a>.</em></p>]]></content:encoded></item><item><title><![CDATA[Podo #015: The Quietest Performance Bug: What Happens in a Pod's First 60 Seconds]]></title><description><![CDATA[cgroup v2 weight inheritance, image preload operators, Stargz/Spegel refresh, noisy neighbor interference, and the pentester's view of fresh pods]]></description><link>https://podostack.com/p/cold-start-pod-first-60-seconds-cgroup-stargz</link><guid isPermaLink="false">https://podostack.com/p/cold-start-pod-first-60-seconds-cgroup-stargz</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 28 Apr 2026 14:01:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ixvz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ixvz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ixvz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!ixvz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!ixvz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!ixvz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ixvz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1754125,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434010?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ixvz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!ixvz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!ixvz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!ixvz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b210e54-ff55-4360-8540-a2a110284398_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Welcome back to Podo Stack. Every SRE I know has a graph they can't explain - pods that looked fine at deploy time but started eating P99 latency when the cluster filled up. Most of the time it isn't a memory leak, not GC, not a bad query. It's the first 60 seconds of a pod's life, where four separate things go wrong quietly and no standard dashboard shows any of them.</p><p>This week: cgroup v2 weight drift that ate 61% of your CPU priority without telling you, four different ways to beat image pull delay, why the scheduler can't see the noisiest neighbors, and what a pentester sees when a pod lands in your cluster.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>Here's what's good this week.</p><div><hr></div><h2>cgroup v2 silently ate 61% of your CPU priority</h2><h3>The linear formula was the wrong shape.</h3><p>If you migrated to cgroup v2 recently (modern K8s distros require it now), you probably ran smoke tests, saw pods running, called it done. What you actually shipped, depending on your OCI runtime version, is a cluster where <code>cpu.weight</code> values are silently wrong.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M_KP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M_KP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 424w, https://substackcdn.com/image/fetch/$s_!M_KP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 848w, https://substackcdn.com/image/fetch/$s_!M_KP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 1272w, https://substackcdn.com/image/fetch/$s_!M_KP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M_KP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png" width="1832" height="1456" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1456,&quot;width&quot;:1832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M_KP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 424w, https://substackcdn.com/image/fetch/$s_!M_KP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 848w, https://substackcdn.com/image/fetch/$s_!M_KP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 1272w, https://substackcdn.com/image/fetch/$s_!M_KP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4482be05-b7bb-4bc4-b2c1-b2a5388d2a19_1832x1456.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The short story: runc and crun used a linear formula to translate v1 <code>cpu.shares</code> into v2 <code>cpu.weight</code>. Math-wise, that's the wrong shape. v1 shares are on a log scale, v2 weight is bounded on [1, 10000] with default 100. A Pod with 1024 shares (one full CPU) ended up with weight 39, not the expected default of 100. A pod with 100m CPU request got weight 4 - not enough granularity for sub-cgroups to mean anything.</p><p>Under contention, that pod behaves as if it has 39% of the priority it thinks it has. You don't see it in <code>kubectl top</code>. You don't see it in node-exporter. You see it in the P99 graphs nobody can explain.</p><p>The fix (kubernetes.io/blog/2026/01/30/new-cgroup-v1-to-v2-cpu-conversion-formula/, Itamar Holder at Red Hat) is a quadratic formula in log space:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;7365a0e6-bb31-4233-a6db-c99c180c3d2c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">cpu.weight = &#8968;10^(L&#178;/612 + 125L/612 - 7/34)&#8969;
where L = log&#8322;(shares)</code></pre></div><p>After the fix, 1024 shares map to weight 102 (near the default 100). 100m CPU maps to weight 17, which is actually usable for sub-cgroup arithmetic.</p><p>The critical catch: the fix lives in the OCI runtime, not in Kubernetes. runc 1.3.2+ or crun 1.23+. If your nodes run an older containerd that bundles older runc, you still ship the broken formula. GitHub issue kubernetes/kubernetes#131216 tracks the adoption lag.</p><p>How to actually check on a running node:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;61f7ae89-fae4-49e6-884d-bbe6e3e01a36&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">$ cat /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/.../cpu.weight
39
$ runc --version
runc version 1.1.12</code></pre></div><p>That's weight 39 instead of ~100. Time to upgrade the runtime.</p><p>This touches bin-packing too. Karpenter's consolidation (<a href="https://podostack.com/p/karpenter-beyond-basics-nodepool-spot-topology">Podo #014</a>) and the FinOps math from <a href="https://podostack.com/p/spot-consolidation-pod-packing-40-percent-overpaying">Podo #007</a> both assume cpu.weight reflects the workload's actual priority claim. When weights are wrong by a factor of 2-3x, cost decisions inherit that error. The silent-failure category is the worst kind - everything keeps running, just quieter and worse than it should, forever.</p><h3>Links</h3><ul><li><p><a href="https://kubernetes.io/blog/2026/01/30/new-cgroup-v1-to-v2-cpu-conversion-formula/">New cgroup v1 to v2 CPU conversion formula (kubernetes.io)</a></p></li><li><p><a href="https://github.com/kubernetes/kubernetes/issues/131216">kubernetes/kubernetes#131216 - runtime adoption tracker</a></p></li><li><p><a href="https://github.com/opencontainers/runc/releases">runc v1.3.2 release notes</a></p></li></ul><div><hr></div><h2>Four ways to kill image pull delay</h2><h3>One problem, four philosophies, and they compose.</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1H9d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1H9d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!1H9d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!1H9d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!1H9d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1H9d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1099816,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434010?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1H9d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!1H9d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!1H9d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!1H9d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda9f445-c978-42a6-93a7-9183f268a448_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cold image pull is the dominant source of pod startup latency once you've fixed the cgroup problem. You have four options in production, and most teams only know one.</p><p><strong>Stargz</strong> (<a href="https://podostack.com/p/spegel-pixie-and-why-latest-is-evil">Podo #001</a>) is lazy pull. The image is read on demand, so a 2 GB container can start serving traffic within seconds instead of waiting for the full pull. Pros: zero pre-work, cold nodes fast on first touch. Cons: first access to any uncached chunk still hits the registry, and you've added a dependency in the hot path.</p><p><strong>Spegel</strong> turns nodes into peers. Each node serves image layers it already has to other nodes in the cluster over P2P. Pros: kills registry pressure on burst scale, keeps bandwidth inside the cluster. Cons: every node has to participate, and someone still cold-starts the first copy somewhere.</p><p><strong>Image Preload Operator</strong> runs as a DaemonSet that pulls a declared set of images ahead of time. Pros: deterministic readiness, CRI-agnostic (containerd, CRI-O, Docker). Cons: fetches images that pods may never schedule, so disk goes to waste on node pools with high tenant variance.</p><p><strong>Baked AMI</strong> (or golden images) bakes the container image into the node image itself. Pros: literally zero pull at boot, simplest ops surface. Cons: every image update means an AMI rebuild, slow feedback loop, and cross-version fleets get painful.</p><p>When to pick what:</p><ul><li><p>Burst capacity, unpredictable image set &#8594; Stargz</p></li><li><p>Steady workload, small stable image set &#8594; Baked AMI</p></li><li><p>Multi-tenant cluster with shared base images &#8594; Spegel</p></li><li><p>Scheduled known workload per node pool &#8594; Preload Operator</p></li></ul><p>These aren't mutually exclusive. Stargz + Spegel is a real production combination: Stargz handles cold reads, Spegel shaves registry egress when multiple nodes pull the same layer in the same burst.</p><p>What's wrong with picking just one: cold-start tail latency is multimodal. Single-philosophy solutions optimize the median and leave the tail. Layered solutions cost more to run but cap the worst case, which is the case you're actually paying for when a node group scales from zero.</p><h3>Links</h3><ul><li><p><a href="https://github.com/containerd/stargz-snapshotter">containerd/stargz-snapshotter</a></p></li><li><p><a href="https://github.com/spegel-org/spegel">spegel-org/spegel - stateless P2P registry mirror</a></p></li><li><p><a href="https://kubernetes.io/docs/concepts/containers/images/#updating-images">Kubernetes image pull policy reference</a></p></li></ul><div><hr></div><h2>The scheduler can't see the neighbor stealing your latency</h2><h3>Cache contention lives in a blind spot.</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sY5_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sY5_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!sY5_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!sY5_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!sY5_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sY5_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1146376,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434010?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sY5_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!sY5_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!sY5_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!sY5_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1417fdb2-984a-472f-b375-a0a4af138bf4_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The default Kubernetes scheduler scores nodes on CPU requests, memory requests, and a handful of affinity rules. That model assumes workloads are CPU- or memory-bound in ways that show up in requests.</p><p>The failure case is micro-architectural contention. A Pod with 20% CPU utilization can thrash the L3 cache hard enough to add 65% to the P99 latency of a neighbor pod. Neither pod violates its limits. Both look healthy in <code>kubectl top</code>.</p><p>Metrics absent from requests and limits:</p><ul><li><p>LLC (last-level cache) miss rate</p></li><li><p>Memory bandwidth saturation (DRAM pressure)</p></li><li><p>Disk I/O wait at the block layer</p></li><li><p>NUMA cross-socket traffic</p></li></ul><p>How to collect any of this on a running cluster:</p><ul><li><p><strong>eBPF programs</strong> (Tetragon, Parca - <a href="https://podostack.com/p/ebpf-tetragon-parca-falco-sloth-alloy">Podo #011</a> covers the stack) hook cache-miss and IPC counters per cgroup.</p></li><li><p><strong>Intel RDT</strong> (Resource Director Technology) surfaces hardware counters for cache occupancy and memory bandwidth per core.</p></li><li><p><strong>PMU counters</strong> via <code>perf</code> are classic but per-node, not per-workload, without extra wiring.</p></li></ul><p>What you can actually do once you have data:</p><ul><li><p><strong>KubeAttention</strong> is one example of an ML scheduler plugin that scores nodes on 26 interference features. Not the answer, representative of the category.</p></li><li><p><strong>Intel CAT</strong> (Cache Allocation Technology) partitions L3 cache between cgroup groups at the hardware level, works with cpuset isolation.</p></li><li><p><strong>Descheduler</strong> is the reactive option: it moves pods off nodes where interference crossed a threshold. Fixes the symptom, not the placement.</p></li><li><p><strong>cpuManagerPolicy: static</strong> plus topology manager pins CPUs per pod, avoiding NUMA crossing.</p></li></ul><p>The frustrating part: teams hit this problem, spend weeks blaming the application code, and never notice the scheduler decision was the bug. No metric the default scheduler reads captures cache contention, so the root cause lives outside its field of view.</p><p>Interference-aware scheduling sounds exotic until you hit the wall. After that it looks obvious.</p><h3>Links</h3><ul><li><p><a href="https://github.com/kubernetes-sigs/descheduler">kubernetes-sigs/descheduler</a></p></li><li><p><a href="https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/">Kubernetes CPU Manager static policy</a></p></li><li><p><a href="https://kubernetes.io/docs/tasks/administer-cluster/topology-manager/">Topology Manager policies</a></p></li></ul><div><hr></div><h2>A pentester's view of your brand-new pod</h2><h3>The same 60 seconds, read from the other side.</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gIXs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gIXs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!gIXs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!gIXs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!gIXs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gIXs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1908329,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/195434010?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gIXs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!gIXs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!gIXs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!gIXs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51cfc39-5a6a-4f9b-86e1-a846bde2984e_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Flip the perspective. You've deployed a workload. Some dependency had a CVE, or a test app was exposed on NodePort by accident, or a supply-chain compromise landed an RCE in a library you vendored last quarter. Either way, an attacker has code execution inside a Pod.</p><p>What do they see in the first 60 seconds? The same window we've been staring at, from the other side.</p><p><strong>Step 1 - Service Account token.</strong><code>/var/run/secrets/kubernetes.io/serviceaccount/token</code> is mounted by default unless you disabled it. <code>kubectl auth can-i --list</code> with that token enumerates the SA's reach. Most workloads carry more RBAC than they need because defaults accrete across Helm chart generations.</p><p><strong>Step 2 - RBAC escalation.</strong> Typical wins: <code>create pods</code> in kube-system, <code>exec</code> into arbitrary pods, <code>patch clusterrole</code>, <code>bind clusterrole</code> to a new SA. None of these should live in an application SA, and all are common in real clusters.</p><p><strong>Step 3 - Kubelet API on neighbor nodes.</strong> Port 10250 on every node speaks the Kubelet API. With the right token or relaxed auth, <code>kubeletctl</code> exec's into any pod on that node, reads the secrets mounted there, pivots through the node without ever touching kube-apiserver. Audit logs won't show this path - the apiserver never saw the call.</p><p><strong>Step 4 - IMDS.</strong><code>169.254.169.254</code> is the cloud metadata endpoint. On AWS, it hands over the node's IAM role credentials if IMDSv2 with hop-limit 1 isn't enforced. From there, an AssumeRole chain takes the attacker out of Kubernetes entirely and into your AWS/GCP/Azure account.</p><p><strong>Step 5 - hostPath or hostPID escape.</strong> If your cluster allows privileged pods anywhere, the attacker mints a new Pod with <code>hostPath: /</code> and reads the kubelet's kubeconfig directly. Now they are the kubelet.</p><p>Each step maps to a defensive control most platforms already own:</p><ul><li><p>SA over-reach &#8594; Kyverno policy restricting SA permissions, plus explicit <code>automountServiceAccountToken: false</code> where safe</p></li><li><p>RBAC escalation &#8594; audit with <code>rakkess</code> or <code>kubectl-who-can</code>, Kyverno ClusterRole constraints</p></li><li><p>Kubelet port 10250 &#8594; NetworkPolicy blocking the port between namespaces</p></li><li><p>IMDS &#8594; Kyverno policy blocking metadata access from workload pods</p></li><li><p>hostPath escape &#8594; Pod Security Standards at <code>restricted</code>, plus Tetragon runtime policy</p></li></ul><p>The offensive view is what gives the defensive policy corpus its meaning. Most clusters ship 80+ Kyverno policies as a checklist. If you can't trace each one back to an attack step it blocks, you're maintaining config theatre. And config theatre expires - someone refactors a policy for "clarity", the mapping it was guarding gets lost, and you find out three years later during an incident review.</p><p>References worth keeping near the desk: CIS Kubernetes Benchmark, NIST SP 800-190, OWASP Kubernetes Top Ten, OWASP Kubernetes Goat (a lab you can practice against in a kind cluster).</p><p>Next week's issue is the defensive half of this story - Kyverno beyond admission, the governance layer that makes this policy corpus hold together.</p><h3>Links</h3><ul><li><p><a href="https://github.com/cyberark/kubeletctl">cyberark/kubeletctl - Kubelet API client</a></p></li><li><p><a href="https://owasp.org/www-project-kubernetes-top-ten/">OWASP Kubernetes Top Ten</a></p></li><li><p><a href="https://github.com/madhuakula/kubernetes-goat">madhuakula/kubernetes-goat - intentionally vulnerable cluster</a></p></li><li><p><a href="https://www.cisecurity.org/benchmark/kubernetes">CIS Kubernetes Benchmark</a></p></li></ul><div><hr></div><h2>Closing</h2><p>Demos ship. Production ships. Between those, four silent failures share one trait - they only show up under contention or compromise:</p><ul><li><p>cgroup weight that makes your pod 40% of the priority it thinks it has</p></li><li><p>image pull stalls nobody attributed to cold start</p></li><li><p>neighbor pods eating your cache while everyone blames your code</p></li><li><p>an attacker's pivot chain that starts exactly where your platform stops looking</p></li></ul><p>The first 60 seconds of a pod's life are where demos lie and where platform engineering earns its keep.</p><p>Which of these did you find the hard way? Reply and tell me - cgroup drift, image pull tail, noisy neighbor P99, or a compromised SA token. I'm collecting stories for a future issue.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/p/cold-start-pod-first-60-seconds-cgroup-stargz?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/p/cold-start-pod-first-60-seconds-cgroup-stargz?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p>- Ilia</p>]]></content:encoded></item><item><title><![CDATA[HDFS Lost. How Object Storage and Table Formats Won the Data Lake]]></title><description><![CDATA[Why data locality is dead, why S3's rename problem didn't matter, and why Iceberg and Delta Lake turned a key-value store into an ACID database]]></description><link>https://podostack.com/p/data-lake-evolution-decoupled-storage-compute</link><guid isPermaLink="false">https://podostack.com/p/data-lake-evolution-decoupled-storage-compute</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Fri, 24 Apr 2026 14:02:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!inC_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ten years ago, the conventional wisdom for building a data lake was carved in stone. Data is too big to move, so move the code to the data. Run your Spark jobs on the same machines where your HDFS blocks live. The whole architecture of Hadoop was built on this single principle - data locality - and for a while it was unquestionable.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!inC_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!inC_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!inC_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!inC_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!inC_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!inC_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1901333,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!inC_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!inC_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!inC_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!inC_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c2d21-7345-4d36-844a-d70d2107c169_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Today that principle is dead, and data engineering is happier for it. Somewhere between 2018 and 2022, the industry quietly abandoned HDFS for object storage plus ephemeral compute, and the world kept turning. If you joined the field in the last five years, you might have missed the memo that this was once controversial.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>So what actually happened? Why did data locality lose? And why is S3, which was supposed to be terrible for analytics, now the default?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0Vzo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0Vzo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!0Vzo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!0Vzo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!0Vzo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0Vzo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24015,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0Vzo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!0Vzo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!0Vzo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!0Vzo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e3ff60-2d0a-4bf4-813d-96d102c56d5d_1200x675.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The collapse of data locality</h2><p>When Hadoop was designed, data center networking was 1 Gbps and hard drives were spinning rust. Reading 100 MB from a local disk took about a second. Sending the same 100 MB across the network took eight seconds. Of course you moved the code to the data - the math didn&#8217;t leave any other option.</p><p>In a modern cloud, the math is completely different. EC2 instances routinely get 25 Gbps of network throughput, with some families hitting 100 Gbps. Meanwhile, a local SATA SSD maxes out at maybe 500 MB/s, and even NVMe tops out at a few GB/s per drive. Network bandwidth has caught up to disk bandwidth and in many cases surpassed it.</p><p>The moment network I/O is no longer the bottleneck, the whole reason for data locality disappears. You can read a Parquet file from S3 in the same time you&#8217;d read it from local disk. The bottleneck has moved elsewhere - to CPU, specifically to the cost of deserializing columnar data into your query engine&#8217;s memory format. &#8220;Move code to the data&#8221; became &#8220;move data to whatever compute is cheapest right now.&#8221;</p><h2>The coupling problem</h2><p>HDFS doesn&#8217;t just assume data locality - it bakes it into the architecture. Your DataNodes are both storage and compute. They hold the blocks and they run the YARN containers. You can&#8217;t scale them independently because they&#8217;re the same thing.</p><p>This creates a painful dilemma every time you want to resize your cluster. Running out of storage? You add nodes, each of which brings CPU and RAM you may not need. Running out of CPU? You add nodes, each of which brings disks you may not need. Your actual resource ratio almost never matches the fixed ratio that your instance type offers, so you&#8217;re always overpaying for one thing to get enough of the other.</p><p>And you pay 24/7. The nodes have to stay up because they store data. Even during the long stretches when nothing is running, you&#8217;re paying full price for compute that&#8217;s idle because the disks attached to it have your data.</p><p>The object storage model breaks this coupling completely. Your data lives in S3. Your compute lives on EC2 Spot or Kubernetes or a managed service. You scale them independently. You turn compute off when you don&#8217;t need it. Your nightly ETL runs on a cluster that exists for 40 minutes and then disappears. You&#8217;re paying for seconds of compute, not months of idle storage + compute bundles.</p><h2>The rename problem and why it didn&#8217;t matter</h2><p>There&#8217;s one technical reason people initially thought S3 would never work for serious data lakes. HDFS is a filesystem with a proper tree structure. Operations like <code>rename </code>are atomic metadata operations - rename a directory, flip a pointer, done. Spark&#8217;s output committers relied on this: jobs wrote to <code>_temporary/attempt_X/</code>, and on success, renamed the temp directory to the final location in one atomic step.</p><p>S3 is not a filesystem. It&#8217;s a flat key-value store pretending to have directories. There&#8217;s no &#8220;rename&#8221; operation. Moving an object means copying it to the new key and deleting the old one. Renaming a &#8220;directory&#8221; with 10,000 objects means copying 10,000 objects - and it&#8217;s not atomic, so a failure halfway through leaves you with a mess.</p><p>The industry&#8217;s first attempts to deal with this were hacks on top of hacks. S3A got &#8220;magic committers&#8221; that abused S3 multipart upload to simulate atomic commits. It worked but was fragile and required tuning.</p><p>The real fix came from somewhere unexpected: the metadata layer. Table formats - Iceberg, Delta Lake, Hudi - solved the rename problem by refusing to use directories as state. Instead of &#8220;the data for this table is whatever files are in this directory,&#8221; they maintain a manifest of which files belong to the current version of the table. Committing a new version means writing a new manifest and atomically swapping a pointer to it. The underlying object store doesn&#8217;t need directories, doesn&#8217;t need rename, doesn&#8217;t need anything clever. It just stores immutable files and serves them back.</p><p>This wasn&#8217;t just a workaround. It was an upgrade. Table formats gave you ACID transactions, schema evolution, time travel, hidden partitioning, and efficient deletes - none of which HDFS-based data lakes had.</p><h2>What won</h2><p>The stack that replaced HDFS looks like this: S3 (or equivalent) for cheap durable storage, a table format (Iceberg or Delta Lake) for ACID transactions and metadata, and ephemeral compute (Spark, Trino, DuckDB, Snowflake, whatever) that reads directly from S3. Each layer is independent. You can swap engines without touching data. You can run two engines against the same table. Your analyst uses Trino, your ML pipeline uses Spark, your dashboards use DuckDB - all pointing at the same files.</p><p>The operational savings are real. No NameNode to babysit. No block balancer to tune. No &#8220;we need a Hadoop admin&#8221; line in your job requisition. The cloud provider handles durability, replication, and scaling - you pay for what you store and what you read.</p><h2>When HDFS still makes sense</h2><p>There&#8217;s one case where HDFS is still the right answer: you already own the hardware. If you&#8217;ve got a rack full of servers in your own datacenter, each with local drives, you don&#8217;t benefit from decoupling storage and compute - you can&#8217;t turn the servers off to save money because you&#8217;ve already bought them. HDFS extracts the most value from that hardware because it uses both the disks and the CPUs at the same time.</p><p>For cloud-based data platforms, running HDFS on EC2 instances is an anti-pattern. You&#8217;re paying for compute you can&#8217;t turn off, you&#8217;re getting none of the benefits the on-prem model offers, and you&#8217;re fighting every managed service and ecosystem tool that assumes S3.</p><h2>The wider lesson</h2><p>Data locality was not a timeless principle. It was a consequence of a specific hardware ratio - slow networks, slow disks, local CPU - that stopped being true. When the ratio shifted, the architecture built on it stopped making sense, and the industry quietly moved on.</p><p>There&#8217;s probably a similar &#8220;timeless principle&#8221; in your stack right now that&#8217;s actually a snapshot of 2015&#8217;s hardware tradeoffs. Worth looking for.</p><div><hr></div><p><em>Still running Hadoop on cloud VMs because that&#8217;s how you learned it? The math has changed. S3 + Iceberg + ephemeral compute is cheaper, faster to operate, and won&#8217;t wake you up to restart a NameNode.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Your Prometheus Bill Isn't Prometheus. It's Cardinality.]]></title><description><![CDATA[FinOps for observability: measure, attribute, and aggressively drop the metrics you don't need]]></description><link>https://podostack.com/p/prometheus-finops-cardinality-cost</link><guid isPermaLink="false">https://podostack.com/p/prometheus-finops-cardinality-cost</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 22 Apr 2026 14:02:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BNwR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Prometheus is free. Thanos is free. Grafana is free. And yet the observability line item on your cloud bill keeps climbing. Compute for the query path, memory for the head block, S3 for long-term storage, egress charges when dashboards span clusters - the tools are open source but the infrastructure around them is very much not.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BNwR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BNwR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!BNwR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!BNwR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!BNwR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BNwR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2037675,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958681?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BNwR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!BNwR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!BNwR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!BNwR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd3b69d2-93b4-4d72-95af-b7dd234d93e5_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The thing nobody tells you when they hand you a Prometheus stack is that the cost driver is almost never &#8220;more metrics.&#8221; It&#8217;s cardinality. The number of unique time series - not raw data points, not storage bytes - is what determines how much RAM your Prometheus needs, how fast your queries are, and how much you pay S3 every month.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>And cardinality explodes quietly. A developer adds a <code>pod_template_hash</code> label to track which deployment version a metric came from. Another adds <code>request_id</code> because they wanted to trace something. A third exports a Go runtime metric with the goroutine ID as a label. Each of these looks innocent locally. Together they can 100x your series count over a quarter.</p><h2>Step 1: find the metrics that are eating you alive</h2><p>Prometheus can tell you exactly which metrics are the worst offenders. Run this against your Prometheus or Thanos Querier:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;9e6ef6b0-975c-4545-a59d-f2d4aaf6d4a4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">topk(20, count by (__name__)({__name__!=""}))</code></pre></div><p>This ranks metric names by how many time series they produce. The top 20 usually contains 80% of your total series count. Write down the names.</p><p>Then drill into the worst one. Say <code>http_requests_total</code> is at the top with 400,000 series. Which label is blowing it up?</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;647bcb47-1b70-49eb-9445-5bd165aaaba2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">topk(10, count by (pod)({__name__="http_requests_total"}))
topk(10, count by (path)({__name__="http_requests_total"}))
topk(10, count by (status_code)({__name__="http_requests_total"}))</code></pre></div><p>One of these breakdowns will reveal the offender - usually a high-cardinality label that shouldn&#8217;t exist. Common culprits: a <code>path</code> label that contains user IDs, a <code>user_id</code> label that contains user IDs, a <code>pod_template_hash</code> that changes every deploy, a <code>request_id</code> that changes every request. Each of these turns a reasonable 50-series metric into a 50,000-series metric.</p><p>Histogram metrics (the ones ending in <code>_bucket</code>) deserve their own investigation. Each bucket is a separate series, so a histogram with 10 buckets and 100 label combinations is already 1,000 series before you add anything.</p><h2>Step 2: attribute the cost to the team that created it</h2><p>Once you know where the series are, the next question is who owns them. &#8220;We spent $3,200 on observability last month&#8221; is not an actionable number. &#8220;Team alpha spent $1,400, team bravo spent $900, team charlie spent $300&#8221; is.</p><p>The pattern is to require a team label on every metric. You can enforce this in two ways:</p><p><strong>In the application.</strong> Your metrics library adds a <code>team</code> label at registration time, pulled from an environment variable that your deployment pipeline sets. Every metric from that service inherits the label. This is the cleanest but requires changes in every service.</p><p><strong>At the scrape layer via </strong><code>relabel_configs</code><strong>.</strong> Your <code>ServiceMonitor</code> or scrape config reads a <code>team</code>label from the pod annotations and copies it onto every metric from that pod. No code changes required.</p><p>Once you have the label, you can query cost-relevant breakdowns:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;205d2df3-c5c5-4ec7-8dbe-f6351428d652&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript"># Hot series in memory, by team
sum(prometheus_tsdb_head_series) by (team)

# Ingestion rate (samples per second), by team
sum(rate(prometheus_tsdb_head_samples_appended_total[5m])) by (team)</code></pre></div><p>Make these dashboards public. The moment a team sees its own line on a &#8220;monthly observability cost per team&#8221; chart, conversations about cardinality get much easier.</p><h2>Step 3: downsample aggressively with Thanos</h2><p>Raw resolution data - one sample every 15 seconds - is useful for about 30 days. After that, nobody is debugging a specific incident at second-level granularity; they&#8217;re looking at trends. Thanos Compactor can aggregate your data into coarser resolutions as it ages:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;7bfb2298-c3c7-4896-a1eb-f31419d68116&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">args:
  - --retention.resolution-raw=30d
  - --retention.resolution-5m=1y
  - --retention.resolution-1h=5y
  - --downsampling.disable=false</code></pre></div><p>This is roughly equivalent to saying &#8220;keep 30 days of raw data, then 5-minute rollups for a year, then hourly rollups for 5 years.&#8221; For storage purposes, 5-minute resolution cuts your sample count by 20x compared to 15-second raw data. Hourly resolution cuts it by another 12x on top of that.</p><p>Your long-retention S3 bill gets a lot cheaper. Your queries over long time windows also speed up dramatically - <code>rate(errors[90d])</code> no longer has to scan 518,400 raw samples per series, it scans 26,000 5-minute samples.</p><h2>Step 4: drop the labels you don&#8217;t query</h2><p>The most underused optimization is <code>metric_relabel_configs</code> with <code>action: labeldrop</code>. This strips a label at scrape time, before anything is written to storage. Series that differ only in that label collapse into one.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;90c51aba-4600-4b99-8cf7-033682860627&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">metric_relabel_configs:
  - action: labeldrop
    regex: pod_template_hash</code></pre></div><p>This one line, on a busy cluster, can cut your series count by a factor of 5. Every time a deployment rolls out, <code>pod_template_hash</code> changes, and if nothing drops it, every metric from that deployment becomes a new set of series. The old series don&#8217;t delete - they just stop receiving samples and linger in storage until they age out.</p><p>Other common drops: <code>instance_id</code> if you don&#8217;t need per-instance granularity, <code>git_commit</code> if you&#8217;re exposing it as a label (make it an info metric instead), any label whose value is a random string.</p><p>The rule I use: if a label has never been in a <code>by()</code> clause in any query I&#8217;ve actually run, it should be dropped at ingestion. Measure your query log for a month and you&#8217;ll be surprised at how few labels are actually used.</p><h2>Making this a continuous process</h2><p>Cardinality control is not a one-time cleanup. New services get added. Old services grow new metrics. Labels drift. The only way to keep it from regressing is to make cardinality visible, reviewed, and owned.</p><p>The minimum setup that actually works:</p><ul><li><p>A dashboard showing total series count over time, broken down by team and metric name.</p></li><li><p>An alert that fires when a single metric crosses 100,000 series, or when total series grow by more than 10% week-over-week.</p></li><li><p>A <code>relabel_configs</code> allowlist or denylist that&#8217;s reviewed as part of every new service onboarding.</p></li><li><p>A monthly &#8220;cost per team&#8221; report sent to engineering leads.</p></li></ul><p>None of this is hard. All of it is unglamorous. The teams that skip it end up with observability bills that look like a runaway data warehouse - and the solution is never &#8220;buy more storage,&#8221; it&#8217;s always &#8220;stop writing data nobody reads.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yNF3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yNF3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!yNF3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!yNF3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!yNF3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yNF3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18034,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958681?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yNF3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!yNF3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!yNF3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!yNF3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18b3227d-291d-4798-a1d4-6a2b6ad878cc_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p><em>If your Prometheus memory usage climbs every week and nobody knows why, start with </em><code>topk(20, count by (__name__)({__name__!=&#8221;&#8220;}))</code><em> - you&#8217;ll find the offender in five minutes.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Karpenter Beyond the Demo: The Patterns That Save Real Money]]></title><description><![CDATA[NodePool and EC2NodeClass architecture, Spot-to-On-Demand fallback, topology spread pitfalls, SpotToSpot consolidation, and why Descheduler hurts you]]></description><link>https://podostack.com/p/karpenter-beyond-basics-nodepool-spot-topology</link><guid isPermaLink="false">https://podostack.com/p/karpenter-beyond-basics-nodepool-spot-topology</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 21 Apr 2026 14:03:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FGBg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FGBg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FGBg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!FGBg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!FGBg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!FGBg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FGBg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2266298,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958457?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FGBg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!FGBg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!FGBg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!FGBg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80224ed9-351c-4f9e-86ff-5d723600f815_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Welcome back to Podo Stack. Karpenter is one of those tools that looks trivial in the intro tutorial - <code>kubectl apply</code> a NodePool, watch nodes appear, feel clever. Then you run it in production for a month and discover that your workloads drift into the wrong zones, your spot strategy silently falls back to full on-demand pricing, and your consolidation fights with Descheduler until half your cluster is in permanent pod churn.</p><p>This week: five patterns that separate a demo-grade Karpenter setup from one that actually saves you money in production.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Here&#8217;s what&#8217;s good this week.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yTAF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yTAF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!yTAF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!yTAF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!yTAF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yTAF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18929,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958457?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yTAF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!yTAF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!yTAF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!yTAF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34930e8d-febc-417c-90d0-1bae2b4fec18_1200x675.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128640; The Foundation: NodePool and EC2NodeClass Separation</h2><h3>Stop mixing policy with plumbing</h3><p>When Karpenter moved from the <code>v1alpha5</code> Provisioner to <code>v1beta1</code>, the monolithic CRD got split into two. <code>NodePool</code> owns the autoscaling policy: which instance types are allowed, what capacity types (spot/on-demand), consolidation rules, resource limits. <code>EC2NodeClass</code> owns the AWS plumbing: AMI family, IAM role, subnet and security group selectors, user data.</p><p>This split isn&#8217;t cosmetic. It lets platform teams own the infrastructure concerns - which subnets, which IAM role, which AMI - while letting application teams define their own scheduling policies without touching security-sensitive fields.</p><p>The NodePool uses attribute-based requirements instead of hardcoded instance types. You don&#8217;t list <code>m5.large, m5.xlarge, m5.2xlarge</code> - you say &#8220;instance-category in [c, m, r], instance-generation &gt; 2, arch in [amd64, arm64]&#8221;. Karpenter picks the cheapest available instance that fits.</p><pre><code><code>apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: general-purpose
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["2"]
      nodeClassRef:
        name: default-node-class
  disruption:
    consolidationPolicy: WhenUnderutilized
    consolidateAfter: 30s
    expireAfter: 720h
</code></code></pre><p><code>expireAfter: 720h</code> is worth highlighting. It forces every node to rotate within 30 days. You get patched AMIs, you get fresh kernels, and you stop accumulating weird node-local state - all without cron or human intervention.</p><h3>Links</h3><ul><li><p><a href="https://karpenter.sh">Karpenter Docs</a></p></li><li><p><a href="https://karpenter.sh/docs/concepts/nodepools/">NodePool reference</a></p></li></ul><div><hr></div><h2>&#128142; Hidden Gem: Spot-to-On-Demand Fallback with Weights</h2><h3>One knob turns spot from &#8220;dev-only&#8221; into &#8220;run everything on it&#8221;</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ejQK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ejQK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!ejQK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!ejQK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!ejQK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ejQK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/def44591-f627-4145-a7ee-be20e84707ef_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26380,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958457?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ejQK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!ejQK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!ejQK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!ejQK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef44591-f627-4145-a7ee-be20e84707ef_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The biggest fear teams have about Spot in production is capacity: what if AWS has no <code>c6i.large</code> in <code>us-east-1a</code> when I need it? The answer isn&#8217;t &#8220;don&#8217;t use Spot.&#8221; The answer is two NodePools and the <code>weight</code> field.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;5f0d3bed-7bb0-4c46-8390-01814609fa9f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: spot-pool
spec:
  weight: 80    # higher = higher priority   (spot-pool)
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
---
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: ondemand-pool
spec:
  weight: 20           # fallback
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]</code></pre></div><p>When a pod goes Pending, Karpenter tries the lowest-weight NodePool first. If it can&#8217;t find spot capacity - no matching instance type is available right now - it falls through to the next NodePool and provisions an on-demand node. The pod never notices. Your bill notices when spot is plentiful (70% off on-demand) and quietly recovers when spot is scarce.</p><p>The subtle trick here is instance flexibility. If you lock your spot NodePool to a single instance family (<code>c6i</code> only), spot availability is fragile. Open it up to categories <code>c, m, r</code> across multiple generations and architectures, and Karpenter has dozens of pools to pick from. Spot interruption rates drop because you&#8217;re not concentrated on a single pool.</p><p>Pair this with correct PodDisruptionBudgets on your critical workloads and Karpenter will handle spot interruptions gracefully - cordon the doomed node, spin up a replacement, drain within PDB limits.</p><h3>Links</h3><ul><li><p><a href="https://karpenter.sh/docs/concepts/disruption/#spot">Spot Best Practices</a></p></li></ul><div><hr></div><h2>&#128293; The Trap: Topology Spread Constraints with <code>DoNotSchedule</code></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tLAC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tLAC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 424w, https://substackcdn.com/image/fetch/$s_!tLAC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 848w, https://substackcdn.com/image/fetch/$s_!tLAC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 1272w, https://substackcdn.com/image/fetch/$s_!tLAC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tLAC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png" width="1456" height="766" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:766,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79410,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958457?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tLAC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 424w, https://substackcdn.com/image/fetch/$s_!tLAC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 848w, https://substackcdn.com/image/fetch/$s_!tLAC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 1272w, https://substackcdn.com/image/fetch/$s_!tLAC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d0a8591-a0a8-4de0-9ef6-889b8e95bf55_1832x964.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><code>topologySpreadConstraints</code> looks like the obvious way to tell Karpenter &#8220;spread my pods across zones.&#8221; And it works - until the day it deadlocks your scaling and the on-call engineer spends three hours figuring out why perfectly healthy pods are stuck Pending.</p><p>The trap is <code>whenUnsatisfiable: DoNotSchedule</code>. That&#8217;s a hard constraint. Here&#8217;s the scenario: <code>maxSkew: 1</code>, three zones, 10 pods in A, 10 in B, 9 in C. A new pod arrives. Karpenter reads the TSC, sees the new pod must go to zone C to keep <code>maxSkew: 1</code>. Karpenter tries to provision a node in C - and the spot pool in C is exhausted. Karpenter <em>cannot</em> fall back to A or B because that would violate <code>DoNotSchedule</code>. The pod sits Pending forever. Your dashboards go yellow. Your PagerDuty goes off.</p><p>For most workloads, use <code>ScheduleAnyway</code> instead. It&#8217;s a soft hint: Karpenter tries to spread evenly, but if it can&#8217;t find capacity in the &#8220;right&#8221; zone, it provisions where it can. You sleep at night.</p><p>Reserve <code>DoNotSchedule</code> for genuinely quorum-sensitive systems: etcd, ZooKeeper, Kafka controllers. For those, losing zone balance is worse than a failed schedule. For your API servers? <code>ScheduleAnyway</code> is the right default.</p><p>One more trap: <code>topologyKey: kubernetes.io/hostname</code> with <code>maxSkew: 1</code>. Karpenter reads this literally - &#8220;at most one extra pod per node&#8221; - and provisions one node per pod. Your 10-pod deployment becomes 10 nodes running at 8% utilization. Use <code>podAntiAffinity</code> if you want &#8220;never colocate,&#8221; not TSC.</p><div><hr></div><h2>&#9889; Deep Dive: SpotToSpotConsolidation</h2><h3>Karpenter becomes a proactive fleet manager instead of a reactive scaler</h3><p>By default, Karpenter refuses to move a pod from one spot node to another. The logic is sensible: both nodes are interruptible, so moving between them buys no stability, and the pod churn has a real cost. Consolidation only touches empty nodes or moves pods from on-demand to spot.</p><p>Turn on <code>SpotToSpotConsolidation</code> and the calculus changes. Karpenter will now actively hunt for two things:</p><p><strong>Cheaper spot pools.</strong> Spot prices fluctuate constantly. Your pod is on a <code>m5.large</code> at $0.05/hour. A compatible <code>c6a.large</code> in the same zone just dropped to $0.03/hour. Karpenter provisions the cheaper node, drains the pod within your PDB, and kills the old one. 40% savings, transparent to the app.</p><p><strong>Lower-interruption pools.</strong> AWS exposes interruption frequency forecasts per spot pool. If your current pool is trending toward &#8220;high interruption risk&#8221; and a similarly priced pool is trending &#8220;low risk,&#8221; Karpenter rebalances you <em>before</em> the interruption notice arrives. You&#8217;re paying the same price for a more stable fleet.</p><p>The trade-off is controlled churn. Your pods will be rescheduled more often. This means two hard requirements: PodDisruptionBudgets on everything that can&#8217;t tolerate instant termination, and applications that are actually stateless - or idempotent at startup. If your app takes 90 seconds to warm caches on boot, SpotToSpot rotation will hurt. Measure before you enable.</p><h3>Links</h3><ul><li><p><a href="https://karpenter.sh/docs/concepts/disruption/">Disruption controls</a></p></li></ul><div><hr></div><h2>&#128163; The One-Liner: Delete Your Descheduler</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JJFk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JJFk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 424w, https://substackcdn.com/image/fetch/$s_!JJFk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 848w, https://substackcdn.com/image/fetch/$s_!JJFk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 1272w, https://substackcdn.com/image/fetch/$s_!JJFk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JJFk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png" width="1456" height="731" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:731,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:94867,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958457?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JJFk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 424w, https://substackcdn.com/image/fetch/$s_!JJFk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 848w, https://substackcdn.com/image/fetch/$s_!JJFk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 1272w, https://substackcdn.com/image/fetch/$s_!JJFk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f68bd8d-1dc4-4546-9619-8fe9481c4f1d_1832x920.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>kubectl delete deployment descheduler -n kube-system</code></code></pre><p>If you&#8217;re running Descheduler alongside Karpenter, stop. It&#8217;s an anti-pattern that creates constant pod churn without the payoff you expect.</p><p>Descheduler was built to compensate for Cluster Autoscaler&#8217;s passivity. CA only removes a node when it&#8217;s <em>completely</em> empty, so Descheduler evicts pods from low-utilization nodes to help CA drain them. That architecture made sense in 2019.</p><p>Karpenter doesn&#8217;t wait for empty nodes. Its built-in Consolidation does the same job across the whole cluster: it looks at every node, finds nodes that can be merged into fewer, larger ones, and performs the drain-and-replace atomically. The key word is <em>atomically</em> - Karpenter decides which pod goes where <em>before</em> it drains, so the reshuffling is a single coordinated operation.</p><p>Run Descheduler on top and the two tools fight. Descheduler evicts a pod to balance utilization. Kube-scheduler places it on another node. A minute later, Karpenter&#8217;s Consolidation decides the new placement is suboptimal and starts its own drain. The pod bounces three times in ten minutes and your users see 503s.</p><p>Same story for other Descheduler strategies. <code>RemovePodsViolatingNodeTaints</code>? Karpenter&#8217;s Drift detection handles taint changes by replacing the whole node. <code>RemovePodsViolatingInterPodAntiAffinity</code>? Karpenter&#8217;s Consolidation respects anti-affinity when it picks placement. Everything Descheduler does, Karpenter does better and without the churn.</p><p>If you&#8217;re on Karpenter, delete Descheduler. Your pods will thank you.</p><div><hr></div><p>Questions? Feedback? Reply to this email. I read every one.</p><div><hr></div><p>Podo Stack - Ripe for Prod.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Invisible Indexes: The Safest Way to Delete an Index You're Not Sure About]]></title><description><![CDATA[MySQL 8.0's hidden indexes let you A/B test index removal in production without DDL risk]]></description><link>https://podostack.com/p/invisible-indexes-mysql-testing</link><guid isPermaLink="false">https://podostack.com/p/invisible-indexes-mysql-testing</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Fri, 17 Apr 2026 14:02:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!yfkb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yfkb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yfkb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!yfkb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!yfkb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!yfkb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yfkb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2389909,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193957484?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yfkb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!yfkb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!yfkb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!yfkb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1387a-6eb2-4239-a4a1-c5d1f477d30c_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s the classic production database problem. You find an index that looks useless. <code>ix_orders_user_status</code> - nobody in the team remembers adding it. It&#8217;s 4 GB. The query that supposedly uses it runs once a day for a report nobody reads anymore.</p><p>But you don&#8217;t want to drop it. Because the one time in two years that someone runs that report, dropping the index will turn a 200ms query into a 40-minute full scan, and you&#8217;ll hear about it from the CFO.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>MySQL 8.0 has a feature for this exact problem. It&#8217;s called an invisible index, and it solves the &#8220;maybe I should drop this&#8221; problem without the risk. You hide the index from the optimizer while keeping it physically on disk. If nothing breaks for a few days, you drop it. If something breaks, you flip it visible again in one ALTER TABLE.</p><h2>What &#8220;invisible&#8221; actually means</h2><p>An invisible index is a normal B-tree index that the optimizer pretends doesn&#8217;t exist. Every INSERT, UPDATE, and DELETE still maintains it - the writes still hit the index pages. Every query plan is generated as if the index weren&#8217;t there.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;bf279c63-9f93-4e2d-8845-f6bd7d416346&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE orders ALTER INDEX ix_user_status INVISIBLE;</code></pre></div><p>That&#8217;s it. One statement. No data movement, no rewriting, no table rebuild. The index goes from &#8220;visible to the optimizer&#8221; to &#8220;invisible to the optimizer&#8221; instantly.</p><p>And if you need it back:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;2b5ca45a-4554-463e-b48b-d6ce859fdfcc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE orders ALTER INDEX ix_user_status VISIBLE;</code></pre></div><p>Also instant. The index was never physically gone - it was just hidden. You flip the bit, the optimizer can see it again, query plans using it start working immediately.</p><h2>The three things this is perfect for</h2><p><strong>Auditing unused indexes.</strong> Over years of schema evolution, dead indexes accumulate. Some are duplicates (two indexes with the same leading columns in different order). Some were added &#8220;just in case&#8221; by someone who left. Dropping them feels risky because you can&#8217;t be sure nothing depends on them. Mark them invisible for a week, watch for regressions, then drop the ones that caused zero complaints.</p><p><strong>Forcing the optimizer to try a different plan.</strong> Sometimes the optimizer stubbornly picks a bad index. You suspect another index would be better, but you can&#8217;t remove the &#8220;bad&#8221; one because other queries need it. Make it invisible temporarily. The optimizer is forced to pick a different plan. If the new plan is better across the board, congratulations - you have evidence for a refactor. If it&#8217;s worse, flip it back.</p><p><strong>Rolling out a new index carefully.</strong> Create the new index as invisible. It gets maintained on all writes but doesn&#8217;t affect any query plans. Run your performance tests on a replica with real traffic. If the metrics look good, flip it visible on production. If not, drop it without ever having risked a plan regression.</p><h2>The workflow</h2><p>The whole process looks like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;9e3a4f0b-71a0-4ea5-ba38-ba53c96a52a8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- 1. Pick a candidate via SHOW INDEX and slow query log
SHOW INDEX FROM orders WHERE Key_name = 'ix_user_status';

-- 2. Make it invisible (instant)
ALTER TABLE orders ALTER INDEX ix_user_status INVISIBLE;

-- 3. Wait 24-48 hours. Watch your metrics:
--    - p95 and p99 latency per endpoint
--    - handler reads per second
--    - slow query log entries
--    - any alerts from your APM

-- 4a. If everything is fine, drop it
ALTER TABLE orders DROP INDEX ix_user_status;

-- 4b. If something is worse, flip it back
ALTER TABLE orders ALTER INDEX ix_user_status VISIBLE;</code></pre></div><p>The reason this is so much better than the straight <code>DROP INDEX</code> approach is reversibility. <code>DROP INDEX</code> on a large table is not free. The rollback is <code>CREATE INDEX</code>, which takes minutes or hours and might lock the table depending on your MySQL version and storage engine settings. Invisible-then-drop gives you a cheap, instant rollback during the observation window, and only after you&#8217;re confident do you pay the DDL cost of actually dropping it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y4p1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y4p1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!Y4p1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!Y4p1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!Y4p1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y4p1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26121,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193957484?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Y4p1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!Y4p1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!Y4p1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!Y4p1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36fa8085-c7b6-4b31-9dc4-a0101aad8911_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The catches</h2><p><strong>Primary keys and unique indexes can&#8217;t be made invisible.</strong> They participate in constraint enforcement, not just query optimization, so MySQL refuses. You&#8217;ll get an error if you try.</p><p><strong>Writes still hit the index.</strong> This is the big one. The index is invisible to reads but alive for writes. If your goal is to reduce write amplification on a heavy OLTP table, making the index invisible does not help. You have to actually drop it to see the write savings. Invisible is for testing the read path, not the write path.</p><p><strong>The query hint escape hatch.</strong> If you&#8217;re desperate to test a query with the invisible index anyway, you can force it with <code>SET SESSION optimizer_switch='use_invisible_indexes=on';</code>. This makes invisible indexes visible for your session only. Useful for debugging &#8220;would this index help if I re-enabled it?&#8221; without affecting production traffic.</p><p><strong>Replica considerations.</strong> Make sure you understand how your replication topology handles DDL. On a typical MySQL replication setup, the <code>ALTER TABLE ... INVISIBLE</code> propagates through the binary log like any other DDL. If you run multi-source or group replication, verify the behavior matches your expectations before you rely on it.</p><h2>Why it matters</h2><p>Before MySQL 8.0, index management was a one-way door. You either kept the index (paying its storage cost and write overhead) or dropped it (risking a plan regression you wouldn&#8217;t notice until it was too late to easily recover). Invisible indexes turn that binary decision into a reversible experiment.</p><p>The same DBA pattern works in both directions - testing removals and testing additions. Combined with a proper slow query log and per-query metrics, you can run real A/B experiments on your index set without ever risking production stability.</p><p>If you&#8217;re running MySQL 8.0 and you&#8217;ve never used this feature, the next time someone asks &#8220;can we drop this index?&#8221; the answer should be &#8220;let&#8217;s make it invisible first.&#8221;</p><div><hr></div><p><em>Still dropping indexes and hoping for the best? Flip them invisible first - it&#8217;s one ALTER TABLE and it buys you a real rollback window.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[AUTO_INCREMENT Is Silently Throttling Your Writes]]></title><description><![CDATA[Why MySQL's oldest ID generator becomes a bottleneck at scale and what to replace it with]]></description><link>https://podostack.com/p/auto-increment-bottleneck-distributed-writes</link><guid isPermaLink="false">https://podostack.com/p/auto-increment-bottleneck-distributed-writes</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 15 Apr 2026 14:02:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rd8Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rd8Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rd8Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!rd8Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!rd8Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!rd8Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rd8Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/200bb104-9e59-412b-9549-800d97421d96_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2187067,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958224?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rd8Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!rd8Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!rd8Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!rd8Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F200bb104-9e59-412b-9549-800d97421d96_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For years, <code>AUTO_INCREMENT</code> has been the boring, obvious choice. You declare <code>id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY</code>, and MySQL hands out unique numbers. Nobody questions it because it just works.</p><p>Then your write volume crosses some invisible line - usually in the tens of thousands of inserts per second - and things get strange. Your p99 insert latency starts climbing. Your CPU looks fine. Your I/O looks fine. But inserts are queueing, and you can&#8217;t figure out why.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>The culprit is the auto-increment counter itself. Under the hood, it&#8217;s a lock. And locks don&#8217;t scale.</p><h2>The hidden mechanism</h2><p>When you insert a row into a table with <code>AUTO_INCREMENT</code>, InnoDB needs to hand you a unique monotonically increasing integer. The way it does that is with a special lock called the AUTO-INC lock. The lock is acquired, the counter is incremented, the new value is stored in your row, the lock is released.</p><p>In the default <code>innodb_autoinc_lock_mode = 1</code> (&#8221;consecutive&#8221;), the lock is held only for the duration of the increment - brief, but still serialized across all concurrent inserts into that table. Under heavy concurrency, you end up with threads queueing behind each other for nothing but their turn to get a number.</p><p><code>innodb_autoinc_lock_mode = 2</code> (&#8221;interleaved&#8221;) relaxes this and avoids the lock for simple inserts, but at the cost of non-consecutive IDs within a transaction. If anything in your codebase or replication tooling assumes contiguous IDs, mode 2 will break it.</p><p>Either way, the AUTO-INC counter is a single point of serialization. It&#8217;s invisible until you&#8217;re doing tens of thousands of writes per second, and then it shows up as insert latency that no amount of CPU or disk tuning can fix.</p><h2>When it stops scaling</h2><p>There are three scenarios where AUTO_INCREMENT reliably breaks down:</p><p><strong>Single-table write storms.</strong> Your event log, your audit trail, your analytics event sink - anything that funnels writes from many sources into one table. The AUTO-INC lock becomes the serialization point for everything.</p><p><strong>Multi-master replication.</strong> If you&#8217;re running MySQL group replication or multi-source replication with writes on multiple nodes, two masters can hand out the same ID at roughly the same time. The classic fix is <code>auto_increment_increment</code> and <code>auto_increment_offset</code> - master A hands out 1, 3, 5, 7; master B hands out 2, 4, 6, 8 - but that only works for exactly two masters and breaks when you add a third. And the moment you fail over and the offset assignment drifts, you get duplicate key errors in replication.</p><p><strong>Distributed IDs as external references.</strong> The AUTO_INCREMENT ID of your <code>users</code> table starts appearing in URLs, in Kafka messages, in caches, in other services&#8217; foreign keys. Now you have a problem if you ever need to split the <code>users</code> table across shards. There&#8217;s no way to shard cleanly by an integer that was handed out sequentially - all your recent users end up on the same shard.</p><h2>The distributed alternatives</h2><p>When AUTO_INCREMENT is the bottleneck, the answer is to move ID generation out of the storage layer. You have three common choices:</p><p><strong>UUID (version 4). </strong>128 bits of random data. Guaranteed unique across any number of writers without coordination. The downside for MySQL specifically: UUIDs are effectively random, so inserting them into the primary B-tree causes scattered writes and destroys your cache locality. This is why naive UUID primary keys kill MySQL performance on large tables. UUIDv7 (time-ordered) fixes most of this and should be the default when you need a globally unique ID in 2026.</p><p><strong>ULID.</strong> 128 bits, but the high bits are a millisecond timestamp. The result sorts roughly in time order, which makes it much friendlier to B-tree indexes. Same no-coordination property as UUID. Slightly less standard than UUID but widely supported.</p><p><strong>Snowflake-style IDs.</strong> 64 bits: timestamp + machine ID + sequence. The ID fits in a BIGINT, sorts by time, and can be generated by any worker as long as each worker has a unique machine ID. Twitter invented this, Discord uses it, Instagram uses a variant. It&#8217;s the choice when you want distributed generation but also want numeric IDs that fit in existing schemas.</p><p>For any new schema at scale, I&#8217;d default to ULID or UUIDv7 for the primary key and never look back. The storage overhead - 16 bytes versus 8 for BIGINT - is a rounding error compared to the scaling properties you get.</p><h2>The migration trap</h2><p>Here&#8217;s the part people underestimate. Switching from AUTO_INCREMENT to distributed IDs on an existing schema is a whole project, not a one-line change.</p><p>Your foreign keys all point at integer IDs. Your indexes are sized for BIGINT. Your application code parses IDs as integers. Your event payloads serialize them as numbers. Every one of those assumptions needs to be revisited. The safe pattern is to add a new column - <code>public_id BINARY(16)</code> for UUID, or <code>VARCHAR(26)</code> for ULID - populate it for new rows first, backfill in the background, switch read paths one at a time, and only then retire the integer ID as the external reference.</p><p>The old integer ID can stay as a clustered primary key for InnoDB storage efficiency. You use the new distributed ID as the external-facing reference. This hybrid is often the right answer - you get the InnoDB benefit of a sequential integer clustered key, plus the distributed ID for everything that crosses a service boundary.</p><h2>A debugging checklist</h2><p>Before you conclude that AUTO_INCREMENT is your bottleneck, verify. The symptoms overlap with other issues.</p><ul><li><p>Run <code>SHOW ENGINE INNODB STATUS\G</code> and look for threads waiting on the AUTO-INC lock.</p></li><li><p>Query <code>performance_schema.data_locks</code> filtered to your hot table.</p></li><li><p>Check <code>innodb_autoinc_lock_mode</code> - if it&#8217;s 1 and you&#8217;re seeing contention, try mode 2 after confirming nothing depends on consecutive IDs within a transaction.</p></li><li><p>Measure the actual benefit. If switching to distributed IDs adds 2ms to every insert for network round-trips to an ID service but removes a 20ms lock wait, you&#8217;re ahead. If it adds 5ms and removes 3ms, you&#8217;re worse off. Measure before you migrate.</p></li></ul><p><code>AUTO_INCREMENT</code> is fine for most workloads. It&#8217;s the hidden lock in the corner for the few that outgrow it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WkD6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WkD6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!WkD6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!WkD6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!WkD6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WkD6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193958224?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WkD6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!WkD6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!WkD6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!WkD6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450b50c-c04a-43a8-adfa-868f4ceb7331_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly database patterns, Kubernetes internals, and Cloud Native tools ripe for production.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Your Business Logic Is Spread Across Cron, Queues, and State Flags. Temporal Kills All Three.]]></title><description><![CDATA[Durable workflows, deterministic replay, signals, retry policies, and versioning in production]]></description><link>https://podostack.com/p/temporal-workflows-state-hell-event-sourcing</link><guid isPermaLink="false">https://podostack.com/p/temporal-workflows-state-hell-event-sourcing</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 14 Apr 2026 14:03:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ruDf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ruDf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ruDf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!ruDf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!ruDf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!ruDf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ruDf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2192248,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193957117?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ruDf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!ruDf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!ruDf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!ruDf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7ecec71-50fe-40f9-8225-701fc7d99fc3_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s a workflow you&#8217;ve probably written before. Register a user. Send a welcome email. </p><p>Wait 24 hours. If they didn&#8217;t finish onboarding, send a reminder. Wait 3 more days. If still nothing, send a final nudge. Otherwise give them a bonus.</p><p>Now implement that. Where does the &#8220;wait 24 hours&#8221; live? In a cron job that polls a database every minute? In a delayed message queue that might drop the job during a broker restart? In a state flag column plus a scheduler plus a retry table plus a dead-letter queue?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This is what teams call the state management hell. The business logic - six lines of pseudocode in your head - gets smeared across five systems. Debugging means grepping across all of them. Changing &#8220;wait 3 days&#8221; to &#8220;wait 4 days&#8221; means a migration script for in-flight state. Testing it means mocking time and half your infrastructure.</p><p>Temporal solves this by making durable workflows a primitive. You write the six lines as six lines. The platform handles everything else.</p><div><hr></div><h2>The Pattern: Sequential Code for Distributed Logic</h2><h3>Your workflow is just code. Temporal makes it durable.</h3><p>A Temporal workflow looks like an ordinary function. It calls activities, waits on timers, reacts to signals. The magic is that the function can be interrupted at any point - server crash, deployment, network blip - and resume exactly where it left off. No state flags. No cron. No &#8220;which step was I on again?&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZvWp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZvWp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!ZvWp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!ZvWp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!ZvWp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZvWp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24662,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193957117?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZvWp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!ZvWp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!ZvWp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!ZvWp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9694a77-26a4-4698-b421-cfbfc4d6b375_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;go&quot;,&quot;nodeId&quot;:&quot;d6d81401-35d5-4cec-b21b-a18b31c09225&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-go">func OnboardingWorkflow(ctx workflow.Context, userID string) error {
    // Step 1-2: activities for side effects
    if err := workflow.ExecuteActivity(ctx, CreateUser, userID).Get(ctx, nil); err != nil {
        return err
    }
    workflow.ExecuteActivity(ctx, SendWelcomeEmail, userID).Get(ctx, nil)

    // Step 3: literally sleep for 24 hours
    workflow.Sleep(ctx, 24*time.Hour)

    // Step 4: remind them
    workflow.ExecuteActivity(ctx, SendProfileReminder, userID).Get(ctx, nil)

    // Step 5: wait for a signal or timeout after 3 days
    profileCh := workflow.GetSignalChannel(ctx, "profile_filled")
    selector := workflow.NewSelector(ctx)
    filled := false
    selector.AddReceive(profileCh, func(c workflow.ReceiveChannel, more bool) { filled = true })
    selector.AddFuture(workflow.NewTimer(ctx, 72*time.Hour), func(f workflow.Future) {})
    selector.Select(ctx)

    if filled {
        return workflow.ExecuteActivity(ctx, GiveBonus, userID).Get(ctx, nil)
    }
    return workflow.ExecuteActivity(ctx, SendFinalNudge, userID).Get(ctx, nil)
}</code></pre></div><p>That <code>workflow.Sleep(ctx, 24*time.Hour)</code> is not sleeping your process. It&#8217;s a durable timer stored by the Temporal server. The worker can crash, the pod can be rescheduled, the region can fail over - the timer fires on schedule because it lives outside your process.</p><p>The trade-off is real. Temporal adds infrastructure: a server cluster (or Temporal Cloud), a persistence backend (Cassandra, PostgreSQL, or MySQL), and workers you run yourself. It&#8217;s not free. But if you&#8217;re already running cron plus delayed queues plus state machines, you were paying for durability anyway - just in duplicated code and operational pain.</p><h3>Links</h3><ul><li><p><a href="https://docs.temporal.io">Temporal docs</a></p></li><li><p><a href="https://temporal.io/cloud">Temporal Cloud</a></p></li></ul><div><hr></div><h2>Hidden Gem: Determinism and Replay</h2><h3>The worker doesn&#8217;t &#8220;resume&#8221; - it re-executes and skips</h3><p>Here&#8217;s the part that catches everyone off guard. When a worker picks up a workflow after a crash, it doesn&#8217;t load state from a snapshot. It re-executes your workflow function from the top. Every call to <code>ExecuteActivity</code>, every <code>Sleep</code>, every <code>Select</code> - runs again.</p><p>Why isn&#8217;t this disastrous? Because Temporal records every completed activity result in an event history. When the workflow re-runs, each activity call is intercepted: if the event history already has a result, Temporal returns it immediately without calling the activity again. The function walks past the already-completed steps at CPU speed until it hits the last unfinished point.</p><p>This is why workflow code must be deterministic. Same input, same path, every time. No <code>rand.Float64()</code>, no <code>time.Now()</code>, no direct HTTP calls, no iteration over Go maps without sorting. All non-determinism has to live in activities, where it&#8217;s recorded once and replayed from history.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;9bd691b5-738d-4d61-9c23-b7c18dac94a5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">WorkflowStarted                       (input: userID=alice)
ActivityTaskScheduled                 (CreateUser)
ActivityTaskCompleted                 (CreateUser &#8594; ok)
ActivityTaskScheduled                 (SendWelcomeEmail)
ActivityTaskCompleted                 (SendWelcomeEmail &#8594; ok)
TimerStarted                          (24h)
TimerFired                            &#8592; worker crashed here
ActivityTaskScheduled                 (SendProfileReminder)
...</code></pre></div><p>Re-execution is free. Determinism is the contract that makes it work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8HVn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8HVn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!8HVn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!8HVn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!8HVn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8HVn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33066,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193957117?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8HVn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!8HVn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!8HVn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!8HVn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a8e6a25-c59c-486e-b9eb-e3a2fb575251_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Links</h3><ul><li><p><a href="https://docs.temporal.io/workflows#determinism">Workflow Determinism</a></p></li></ul><div><hr></div><h2>The Showdown: Workflow vs Activity</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WlB9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WlB9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!WlB9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!WlB9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!WlB9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WlB9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22845,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/193957117?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WlB9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!WlB9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!WlB9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!WlB9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F492174e7-d3e4-4fd2-a5fb-77caa9de33f0_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Workflow:</strong> Deterministic. Orchestration logic only. Cannot make network calls, read files, or get the current time directly. Re-executes from event history on recovery. Lives as long as your business process - minutes, days, years.</p><p><strong>Activity:</strong> Non-deterministic. Does the actual work - database queries, API calls, file uploads. Runs at-least-once (your activity must be idempotent or use Temporal&#8217;s idempotency keys). Has its own retry policy, timeout, and heartbeat. Lives only as long as one execution.</p><p>The mental model: workflows describe <em>what</em> happens and in what order. Activities do <em>the actual thing</em>. If you find yourself wanting to call a REST API inside a workflow, that&#8217;s a smell - wrap it in an activity. If you find yourself tracking &#8220;which step am I on&#8221; inside an activity, you&#8217;ve reinvented the workflow in the wrong place.</p><p>Activities get the observability for free. Every invocation is a row in the event history: scheduled, started, completed or failed, with payload and timestamp. You can see exactly which call timed out, which one retried, how long each took. No custom logging infrastructure needed.</p><div><hr></div><h2>Deep Dive: Retry Policies, Timeouts, and Heartbeats</h2><h3>Three different timeouts. Most people only know one.</h3><p>When you call an activity, Temporal gives you three separate timeout knobs, and getting them wrong is the number one source of confusion.</p><p><strong>Start-to-Close</strong> is the one people actually mean when they say "timeout." How long can a single attempt take? Set it to your p99 plus headroom. If the activity exceeds this, Temporal treats that attempt as failed and applies the retry policy.</p><p><br><strong>Schedule-to-Close</strong> is the end-to-end deadline across all retries. If you set Start-to-Close to 30 seconds and the retry policy allows up to 10 attempts, Schedule-to-Close puts a hard ceiling on the whole thing - say, 10 minutes. After that, no matter how many retries remain, the activity gives up.<br><br><strong>Schedule-to-Start</strong> is how long the task can sit in the task queue before a worker picks it up. This is the alarm bell for worker fleet problems: if this fires, your workers are overloaded or dead, not your business logic.</p><p>The retry policy itself is surprisingly rich. Initial interval, backoff coefficient, maximum interval, maximum attempts - and a list of non-retryable error types so you don&#8217;t waste retries on <code>ValidationError</code>. For long-running activities (ETL jobs, file uploads, ML training), you use heartbeats: the activity calls <code>RecordHeartbeat</code> periodically, and if Temporal doesn&#8217;t see one for <code>HeartbeatTimeout</code>, it assumes the worker is dead and reschedules.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;907520ce-591d-4424-826c-b823474ae04e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">retryPolicy:
  initialInterval: 1s
  backoffCoefficient: 2.0
  maximumInterval: 100s
  maximumAttempts: 10
  nonRetryableErrorTypes:
    - ValidationError
    - PermissionDenied</code></pre></div><h3>Links</h3><ul><li><p><a href="https://docs.temporal.io/activities#timeouts">Activity Timeouts</a></p></li></ul><div><hr></div><h2>The One-Liner: Versioning Workflows in Production</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;go&quot;,&quot;nodeId&quot;:&quot;acae5345-7cd3-4d43-aca9-249c0e0a56c2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-go">v := workflow.GetVersion(ctx, "add-bonus-check", workflow.DefaultVersion, 1)</code></pre></div><p>This is the single most important line if you ever plan to change a workflow while real workflows are in flight. And you will.</p><p>Remember the determinism rule - workflows re-execute from history. If you deploy new code that adds a step between step 2 and step 3, any workflow that was mid-flight on the old code will replay with the new code and hit a non-deterministic execution error. The history says &#8220;after step 2 we started timer X&#8221; and the new code says &#8220;after step 2 we called activity Y&#8221;. Temporal refuses to continue and the workflow is stuck.</p><p><code>GetVersion</code> is how you branch safely. On first execution, it records the version in history. On replay, it returns the recorded version. You use it to fork the code path: <code>if v == DefaultVersion { oldPath() } else { newPath() }</code>. Old workflows keep running the old path; new ones get the new path. Once all old workflows have drained, you clean up the <code>GetVersion</code> calls.</p><p>Alternatives: use workflow reset to replay from a specific event, or just launch a new workflow type and let the old one finish. But in practice <code>GetVersion</code> is the pragmatic default for any non-trivial change to a running workflow.</p><h3>Links</h3><ul><li><p><a href="https://docs.temporal.io/workflows#workflow-versioning">Workflow Versioning</a></p></li></ul><div><hr></div><p>Questions? Feedback? Reply to this email. I read every one.</p><div><hr></div><p>Podo Stack - Ripe for Prod.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Podo Stack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Denormalization: When JOINs Kill Your Read Performance]]></title><description><![CDATA[Intentional data duplication for read-heavy systems - trade-offs, patterns, and when it's actually worth it]]></description><link>https://podostack.com/p/denormalization-when-joins-kill-performance</link><guid isPermaLink="false">https://podostack.com/p/denormalization-when-joins-kill-performance</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Fri, 10 Apr 2026 09:01:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!et9K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every database course teaches normalization first. Third normal form. No redundancy. Single source of truth. And it&#8217;s the right starting point - until your dashboards take 12 seconds to load because the query joins seven tables to show a username next to an order.</p><p>Normalization optimizes for writes. Denormalization optimizes for reads. Most production systems are 90%+ reads. You do the math.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>I&#8217;m not saying throw away your foreign keys. I&#8217;m saying there are specific, measurable situations where copying a column into another table saves you seconds of query time and megabytes of memory. And that trade-off is worth making consciously.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!et9K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!et9K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!et9K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!et9K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!et9K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!et9K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1067884,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191683219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!et9K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!et9K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!et9K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!et9K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dda417e-5cda-469c-a10f-8d329fa15e1e_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>The actual cost of JOINs at scale</h2><p>A single JOIN between two indexed tables is fast. Two JOINs - still fine. But production queries rarely stop there.</p><p>Consider an order details page: <code>orders</code> JOIN <code>users</code> JOIN <code>products</code> JOIN <code>shipping_addresses</code> JOIN <code>payment_methods</code> JOIN <code>order_items</code>. Six tables. Each JOIN multiplies the work - fetching pages from different parts of disk, building hash tables in memory, matching keys across indexes.</p><p>At 1,000 requests per second, those JOINs don&#8217;t just cost time. They cost buffer pool pages, CPU cycles, and connection slots. The query that takes 15ms at low traffic takes 200ms under load because every JOIN competes for the same memory.</p><h2>The pattern: embed what you read together</h2><p>The core idea is simple. If you always read <code>user_name</code> when you read <code>orders</code> - put <code>user_name</code> in the <code>orders</code> table.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;ceb601af-bf7b-482d-b25b-4aeadfc4f072&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE orders
  ADD COLUMN customer_name VARCHAR(100),
  ADD COLUMN customer_country CHAR(2);</code></pre></div><p>Now your order list query goes from:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;ee43adc9-65e7-4d3e-8539-b3516e1a80f5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT o.id, o.total, u.name, u.country
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at &gt; '2026-01-01';</code></pre></div><p>To:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;2bad8d74-172b-4f8b-8a3a-116a80ca9fcf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT id, total, customer_name, customer_country
FROM orders
WHERE created_at &gt; '2026-01-01';</code></pre></div><p>One table. One index scan. No JOIN. The query that used to touch two B-trees now touches one.</p><div><hr></div><h2>When denormalization makes sense</h2><p>Not every JOIN deserves elimination. Denormalize when all three conditions are true:</p><p><strong>Read-heavy ratio.</strong> The table is read 10-100x more than it&#8217;s written. E-commerce order history, analytics dashboards, reporting tables, user activity feeds. If writes are frequent, the sync cost eats your read gains.</p><p><strong>The JOIN is in the hot path.</strong> Not every slow query matters. Denormalize the queries that users wait on - page loads, API responses, search results. If it&#8217;s a nightly batch job, the JOIN is fine.</p><p><strong>The source data changes rarely.</strong> Customer names change infrequently. Countries almost never change. Product titles change sometimes. Prices change often. Denormalize the stable columns. Leave the volatile ones normalized.</p><div><hr></div><h2>Keeping the data in sync</h2><p>This is where denormalization gets its bad reputation. You&#8217;ve duplicated data. Now you have two sources of truth. If the user changes their name, the <code>users</code> table updates but <code>orders.customer_name</code> doesn&#8217;t - unless you handle it.</p><p>Three patterns, from simplest to most reliable:</p><p><strong>Triggers.</strong> The database itself keeps the copy in sync. Fast, automatic, invisible to your application:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;5fe39de8-e265-4d4c-98ac-3fded98ecb9f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE TRIGGER sync_customer_name
AFTER UPDATE ON users
FOR EACH ROW
  UPDATE orders
  SET customer_name = NEW.name
  WHERE user_id = NEW.id;</code></pre></div><p>The downside: triggers are hard to debug and easy to forget about. They don&#8217;t show up in your application code.</p><p><strong>Application-level sync.</strong> Your service updates both tables in the same transaction. Explicit, visible in code, reviewable in PRs. But you have to remember to do it everywhere.</p><p><strong>Background reconciliation.</strong> A scheduled job compares the denormalized columns with their source tables and fixes drift. This is your safety net, not your primary sync:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;c5d0edaa-ff19-4fb5-8a78-ae2aa3c34766&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">UPDATE orders o
JOIN users u ON o.user_id = u.id
SET o.customer_name = u.name
WHERE o.customer_name != u.name;</code></pre></div><p>Run it hourly or nightly. It catches whatever the triggers or application code missed.</p><p>In practice, most teams use triggers for the primary sync and a background job as a safety net. It&#8217;s belt and suspenders, but stale data in production is worse than a few extra writes.</p><div><hr></div><h2>Measuring the impact</h2><p>Don&#8217;t denormalize on gut feeling. Measure before and after:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;cbe16bbb-d29c-4b3d-91ee-829bfd6e4628&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Before: JOIN query
EXPLAIN ANALYZE
SELECT o.id, o.total, u.name
FROM orders o JOIN users u ON o.user_id = u.id
WHERE o.created_at &gt; '2026-01-01';

-- After: denormalized query
EXPLAIN ANALYZE
SELECT id, total, customer_name
FROM orders
WHERE created_at &gt; '2026-01-01';</code></pre></div><p>You should see the number of rows examined drop, the execution time shrink, and the query plan simplify from a nested loop or hash join to a simple range scan.</p><p>Also check buffer pool pressure - fewer pages loaded per query means more room for other indexes and queries.</p><div><hr></div><h2>When not to denormalize</h2><p><strong>Highly volatile source data.</strong> If the source column changes multiple times per hour per row, the sync cost dominates. You&#8217;re writing more to keep the copy current than you&#8217;re saving on reads.</p><p><strong>Low traffic.</strong> If the query runs 50 times a day, the JOIN overhead is irrelevant. Don&#8217;t add complexity for a problem that doesn&#8217;t exist yet.</p><p><strong>Analytics you can pre-compute.</strong> If you need <code>total_orders</code> per user, a materialized view or a summary table is cleaner than a counter column that you increment on every insert.</p><p><strong>When <a href="https://podostack.com/p/vertical-partitioning-wide-tables">vertical partitioning</a> solves it.</strong> Sometimes the real problem isn&#8217;t JOINs - it&#8217;s that your table is too wide and queries load columns they don&#8217;t need. Split the table first, denormalize second.</p><div><hr></div><h2>The mindset shift</h2><p>Normalization is a design principle. Denormalization is an engineering decision. You normalize when modeling the domain. You denormalize when serving the users.</p><p>The best schemas I&#8217;ve seen do both: normalized source tables that are the system of record, with denormalized read tables or columns that serve the hot path. The source stays clean. The reads stay fast.</p><p>It&#8217;s not about breaking the rules. It&#8217;s about knowing which rules exist for the textbook and which exist for production.</p><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly database patterns, Kubernetes internals, and Cloud Native tools ripe for production.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>Running a dashboard that joins seven tables on every page load? Pick the one JOIN that hurts most, duplicate one column, and measure the difference. Start small.</em></p>]]></content:encoded></item><item><title><![CDATA[Prefix Indexes: Stop Indexing 255 Characters When 16 Is Enough]]></title><description><![CDATA[When VARCHAR(255) is in the WHERE clause but only the first 16 characters matter]]></description><link>https://podostack.com/p/prefix-indexes-long-strings</link><guid isPermaLink="false">https://podostack.com/p/prefix-indexes-long-strings</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 08 Apr 2026 09:01:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Eaxi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your <code>VARCHAR(255)</code> column has an index. That index stores every byte of every value. For 10 million rows of email addresses, that&#8217;s roughly 2.5GB of index data.</p><p>But here&#8217;s the thing - most email lookups match on the first 12-16 characters. The rest of the string is dead weight in the index. You&#8217;re paying storage and memory for bytes the query optimizer never needs.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>Prefix indexes fix this. Instead of indexing the full string, you index only the first N characters. The index shrinks by 5-10x. Queries stay fast. And your InnoDB buffer pool stops wasting RAM on index pages that don&#8217;t earn their keep.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Eaxi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Eaxi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Eaxi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Eaxi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Eaxi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Eaxi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:898618,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191683065?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Eaxi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Eaxi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Eaxi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Eaxi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa197f8f7-5689-4210-bd66-70aa04862428_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>The syntax</h2><p>It&#8217;s one keyword away from a regular index:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;59ce12fd-d867-43d3-8a94-b482ba38c0c0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE INDEX idx_email_prefix ON users (email(16));</code></pre></div><p>That <code>(16)</code> tells MySQL: store only the first 16 characters. For a <code>VARCHAR(255)</code> column, that&#8217;s roughly 6% of the maximum length. The index is dramatically smaller.</p><p>You can do this on <code>CREATE TABLE</code> or as an <code>ALTER TABLE</code>. Works on <code>VARCHAR</code>, <code>CHAR</code>, <code>TEXT</code>, <code>BLOB</code> - any string type.</p><div><hr></div><h2>Choosing the right prefix length</h2><p>Too short and the index isn&#8217;t selective enough - too many rows share the same prefix, so MySQL still scans thousands of entries. Too long and you lose the size benefit.</p><p>The trick is measuring selectivity. You want the prefix that captures most of the uniqueness:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;7a1ced82-86e1-4d0d-b133-781f1f78c7b2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Full column selectivity (baseline)
SELECT COUNT(DISTINCT email) / COUNT(*) AS full_selectivity
FROM users;

-- Test different prefix lengths
SELECT
  COUNT(DISTINCT LEFT(email, 8))  / COUNT(*) AS sel_8,
  COUNT(DISTINCT LEFT(email, 12)) / COUNT(*) AS sel_12,
  COUNT(DISTINCT LEFT(email, 16)) / COUNT(*) AS sel_16,
  COUNT(DISTINCT LEFT(email, 20)) / COUNT(*) AS sel_20
FROM users;</code></pre></div><p>If full selectivity is 0.98 and <code>LEFT(email, 16)</code> gives you 0.95 - that&#8217;s close enough. You&#8217;re keeping 97% of the uniqueness at 6% of the size.</p><p>A rule of thumb: aim for 90-95% of full-column selectivity. Beyond that, you&#8217;re paying more storage for diminishing returns.</p><h2>Where prefix indexes shine</h2><p>The sweet spot is long strings with high uniqueness at the front:</p><ul><li><p><strong>Email addresses</strong> - <code>user@</code> is usually unique enough in the first 16 chars</p></li><li><p><strong>URLs</strong> - <code>https://example.com/path</code> diverges early</p></li><li><p><strong>UUIDs stored as VARCHAR</strong> - the first 8 characters of a UUID v4 give strong selectivity</p></li><li><p><strong>File paths</strong> - <code>/data/region/tenant/...</code> branches early in the tree</p></li><li><p><strong>JSON string fields</strong> - when you&#8217;re filtering on a text column that holds serialized data</p></li></ul><p>For columns like <code>country_code</code> (2 chars) or <code>status</code> (5-10 chars), prefix indexes don&#8217;t make sense - just index the full column.</p><div><hr></div><h2>Composite indexes with prefixes</h2><p>You can mix prefix and full columns in a composite index:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;816b0c3e-ab9a-4a52-ada3-deabce335c46&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE INDEX idx_type_email ON users (account_type, email(16));</code></pre></div><p>MySQL uses <code>account_type</code> fully, then narrows within each type using the email prefix. This is powerful for queries like <code>WHERE account_type = 'business' AND email LIKE 'john%'</code>.</p><div><hr></div><h2>The trade-offs</h2><p><strong>No ORDER BY optimization.</strong> MySQL can&#8217;t use a prefix index for sorting. <code>ORDER BY email</code> won&#8217;t benefit from <code>email(16)</code> - the optimizer doesn&#8217;t know the full order. If you need sorted results, you need the full-column index.</p><p><strong>No covering index.</strong> A prefix index can&#8217;t &#8220;cover&#8221; a query because it doesn&#8217;t store the complete value. If your query is <code>SELECT email FROM users WHERE email LIKE 'john%'</code>, MySQL still has to look up the full row to return the complete email. Pair this with a <a href="https://podostack.com/p/covering-indexes-query-speedup">covering index</a> if you need both.</p><p><strong>No exact UNIQUE constraint.</strong> A prefix index on <code>email(16)</code> can&#8217;t enforce uniqueness on the full email - two different emails might share the same 16-character prefix. If you need uniqueness, keep a full <code>UNIQUE</code> index alongside the prefix index for queries.</p><p><strong>UTF-8 prefix length is in characters, not bytes.</strong> In <code>utf8mb4</code>, one character can take up to 4 bytes. <code>email(16)</code> means 16 characters, which could be up to 64 bytes. This matters for index size calculations if your data has multi-byte characters.</p><div><hr></div><h2>Prefix indexes in PostgreSQL</h2><p>PostgreSQL doesn&#8217;t support prefix indexes directly - there&#8217;s no <code>CREATE INDEX ... (column(N))</code> syntax. But you get the same result with a functional index:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;160d1f40-f52c-40d1-b6f0-e117f50003c5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE INDEX idx_email_prefix ON users (LEFT(email, 16));</code></pre></div><p>Your queries need to use the same expression: <code>WHERE LEFT(email, 16) = LEFT('john@example.com', 16)</code>. It&#8217;s less ergonomic than MySQL&#8217;s approach, but the performance benefit is identical.</p><p>Honestly, for PostgreSQL, <a href="https://podostack.com/p/partial-indexes-mysql-emulation">partial indexes</a> often solve the same underlying problem - a large index where most entries aren&#8217;t useful - through a different mechanism.</p><h2>Quick validation</h2><p>After creating a prefix index, always check two things:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;bb420b3b-84f2-4c40-ac9e-609a887f6fe5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- 1. Verify the optimizer uses it
EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';

-- 2. Check index size vs full index
SELECT
  index_name,
  ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) AS size_mb
FROM mysql.innodb_index_stats
WHERE table_name = 'users'
  AND stat_name = 'size';</code></pre></div><p>If EXPLAIN shows your prefix index and the size is 5-10x smaller than the full-column index - you&#8217;re done. If EXPLAIN ignores the prefix index, your prefix might be too short. Bump it up by 4 characters and test again.</p><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly database patterns, Kubernetes internals, and Cloud Native tools ripe for production.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>Still indexing full VARCHAR(255) columns when only the first 16 characters matter? One ALTER TABLE is all it takes.</em></p>]]></content:encoded></item><item><title><![CDATA[Tools From the Future]]></title><description><![CDATA[Dapr distributed runtime, Kargo GitOps promotion, WasmEdge vs containers, Koordinator scheduling, and OpenFeature]]></description><link>https://podostack.com/p/dapr-kargo-wasmedge-koordinator-openfeature</link><guid isPermaLink="false">https://podostack.com/p/dapr-kargo-wasmedge-koordinator-openfeature</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 07 Apr 2026 14:04:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_Gkf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back to Podo Stack. This week: five tools that feel like they&#8217;re from the future. A runtime that abstracts away infrastructure. A GitOps promotion engine by the Argo CD team. WebAssembly challenging containers. A scheduler that squeezes real money out of idle nodes. And a CNCF standard that makes feature flags vendor-neutral.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>Here&#8217;s what&#8217;s good this week.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Gkf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Gkf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!_Gkf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!_Gkf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!_Gkf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Gkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:973136,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682326?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_Gkf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!_Gkf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!_Gkf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!_Gkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879f093b-fc38-4824-827d-c19b0edca2a8_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128640; Sandbox Watch: Dapr - The Standard Library for Microservices</h2><h3>The problem</h3><p>You&#8217;re building microservices. Every service needs state management, pub/sub, service discovery, secrets, and retries. So you import SDKs - one for Redis, one for Kafka, one for Vault, one for your cloud provider&#8217;s queue. Suddenly, your business logic is buried under infrastructure glue code. And switching from RabbitMQ to Kafka? That&#8217;s a rewrite.</p><h3>The solution</h3><p>Dapr (Distributed Application Runtime) runs as a sidecar next to your app and exposes all those patterns through simple HTTP/gRPC APIs. Need to save state? <code>POST /v1.0/state/mystore</code>. Publish an event? <code>POST /v1.0/publish/mytopic</code>. Invoke another service? <code>POST /v1.0/invoke/service-b/method/checkout</code>.</p><p>Your code doesn&#8217;t know (or care) whether the state store is Redis, PostgreSQL, or CosmosDB. That&#8217;s a YAML config swap, not a code change.</p><p>Started at Microsoft, now a CNCF Incubating project. The building blocks cover the greatest hits of distributed systems: service invocation with mTLS, state management, pub/sub, input/output bindings, actors, and distributed lock.</p><h3>What it&#8217;s NOT</h3><p>Dapr isn&#8217;t a service mesh. Istio and Linkerd handle L7 traffic routing and network policies. Dapr handles application-level concerns - state, events, secrets. They&#8217;re complementary. You can run both. In fact, if you&#8217;re already on <a href="https://podostack.com/p/cilium-ebpf-kube-proxy-identity-hubble">Cilium</a> for networking, Dapr slots right in on top.</p><h3>The catch</h3><p>The sidecar adds latency - a few milliseconds per call. For most microservices, that&#8217;s nothing. For ultra-low-latency trading systems, it&#8217;s a dealbreaker. Also, debugging gets trickier when every call goes through a sidecar. Good observability (Dapr exports OpenTelemetry traces by default) helps, but it&#8217;s still another moving part.</p><h3>Links</h3><ul><li><p><a href="https://github.com/dapr/dapr">GitHub: dapr/dapr</a></p></li><li><p><a href="https://docs.dapr.io/">Dapr Docs</a></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z6bD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z6bD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 424w, https://substackcdn.com/image/fetch/$s_!z6bD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 848w, https://substackcdn.com/image/fetch/$s_!z6bD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!z6bD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z6bD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png" width="1456" height="987" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:987,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:90812,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682326?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z6bD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 424w, https://substackcdn.com/image/fetch/$s_!z6bD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 848w, https://substackcdn.com/image/fetch/$s_!z6bD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!z6bD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5713b5ce-af40-448c-8c06-dff2d9bf34be_1752x1188.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128142; Hidden Gem: Kargo - GitOps Finally Gets Promotion Right</h2><p>GitOps has a dirty secret: nobody agrees on how to promote changes between environments. You&#8217;ve got Argo CD syncing your clusters from Git. Beautiful. But who updates that Git repo when a new image passes staging tests? A janky CI script, that&#8217;s who.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Z6s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Z6s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-Z6s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-Z6s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-Z6s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Z6s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1338515,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682326?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-Z6s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-Z6s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-Z6s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-Z6s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4244a35-2c0f-4e1e-8c07-53f1ef303abe_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Enter Kargo</h3><p>Built by the creators of Argo CD (yes, the same team), Kargo is purpose-built for continuous promotion. It introduces four concepts that make the whole thing declarative:</p><ul><li><p><strong>Warehouse</strong> - watches for new artifacts (Docker tags, Helm chart versions, Git commits)</p></li><li><p><strong>Freight</strong> - a specific bundle of artifacts, like <code>myapp:v1.2.3</code> + <code>chart:0.5.0</code></p></li><li><p><strong>Stage</strong> - represents an environment (dev, staging, prod)</p></li><li><p><strong>Promotion</strong> - the act of moving Freight from one Stage to the next</p></li></ul><p>The flow: Warehouse detects a new image tag. Creates Freight. Auto-promotes to dev. Dev passes verification. Kargo shows a &#8220;Promote to Staging&#8221; button in its UI. QA clicks it. Kargo updates the Git repo. Argo CD syncs. Done.</p><p>No shell scripts. No <code>sed -i</code> in CI pipelines. No &#8220;who changed that image tag in the values.yaml?&#8221;</p><h3>Why it matters</h3><p>If you adopted <a href="https://podostack.com/p/crossplane-infrastructure-api-compositions-claims">Crossplane</a> for infrastructure-as-code and Argo CD for deployment, Kargo closes the last gap. It&#8217;s the missing piece between &#8220;CI built a new image&#8221; and &#8220;that image is running in production.&#8221;</p><h3>Links</h3><ul><li><p><a href="https://github.com/akuity/kargo">GitHub: akuity/kargo</a></p></li><li><p><a href="https://docs.kargo.io/">Kargo Docs</a></p></li></ul><div><hr></div><h2>&#9876;&#65039; The Showdown: WasmEdge vs Containers</h2><p>Solomon Hykes (Docker&#8217;s co-founder) tweeted it back in 2019: &#8220;If WASM+WASI existed in 2008, we wouldn&#8217;t have needed Docker.&#8221; Bold claim. Let&#8217;s see where we are.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-ErF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-ErF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-ErF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-ErF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-ErF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-ErF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1069898,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682326?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-ErF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-ErF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-ErF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-ErF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf98d6b6-7172-4779-82f1-ce55b05b19ec_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>WasmEdge</strong> is a CNCF Incubating runtime for server-side WebAssembly. It compiles Wasm modules ahead-of-time to near-native speed. And the numbers are hard to ignore.</p><p><strong>Containers:</strong></p><ul><li><p>Cold start: seconds</p></li><li><p>Image size: 100s of MB</p></li><li><p>Isolation: Linux namespaces + cgroups</p></li><li><p>Language support: anything that runs on Linux</p></li><li><p>Ecosystem: massive, battle-tested</p></li></ul><p><strong>WasmEdge:</strong></p><ul><li><p>Cold start: milliseconds</p></li><li><p>Module size: single-digit MBs</p></li><li><p>Isolation: Wasm sandbox (no syscalls by default)</p></li><li><p>Language support: Rust, C/C++, Go (TinyGo), JS, Python (growing)</p></li><li><p>Ecosystem: early but expanding fast</p></li></ul><h3>The verdict</h3><p>It&#8217;s not either/or. Wasm shines for edge computing, serverless functions, and plugin systems where you need thousands of isolated instances with instant startup. Containers win for complex apps with deep OS dependencies, mature debugging tools, and the massive Docker Hub ecosystem.</p><p>The practical move today: use containers for your main workloads. Explore WasmEdge for FaaS, edge processing, and SaaS plugin sandboxing. Kubernetes already supports both through OCI-compatible runtimes like <code>crun</code>.</p><h3>Links</h3><ul><li><p><a href="https://github.com/WasmEdge/WasmEdge">GitHub: WasmEdge/WasmEdge</a></p></li><li><p><a href="https://wasmedge.org/docs/">WasmEdge Docs</a></p></li></ul><div><hr></div><h2>&#128300; The Pattern: Koordinator - Make Idle CPUs Pay Rent</h2><h3>The problem</h3><p>Your Kubernetes nodes are 15-20% utilized. You&#8217;re paying for 100% of them. The scheduler sees requested resources, not actual usage. So those 80% &#8220;reserved but idle&#8221; CPUs just sit there, burning money.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rSYx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rSYx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!rSYx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!rSYx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!rSYx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rSYx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1461600,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682326?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rSYx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!rSYx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!rSYx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!rSYx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4b42671-e6f3-4b45-9d77-59569c6409c4_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>The fix</h3><p>Koordinator, from Alibaba and now a CNCF project, introduces colocation - running best-effort (BE) batch jobs on the resources your latency-sensitive (LS) services reserved but aren&#8217;t using.</p><p>The key: when the LS service actually needs those resources back, Koordinator&#8217;s node agent (Koordlet) instantly throttles or evicts the BE workloads. No performance degradation for your production services. It monitors real-time metrics - CPU cycles per instruction, cache contention, memory bandwidth - and reacts in milliseconds.</p><p>The isolation goes deeper than standard cgroups. Koordinator manages LLC (Last Level Cache) allocation and memory bandwidth to prevent &#8220;noisy neighbor&#8221; problems at the hardware level.</p><h3>Real numbers</h3><p>Alibaba reports going from ~15% to 50%+ cluster utilization in production. That&#8217;s not a rounding error. On a 1000-node cluster, that&#8217;s potentially hundreds of nodes you don&#8217;t need to buy.</p><h3>The catch</h3><p>This isn&#8217;t plug-and-play. You need a solid understanding of Linux kernel scheduling, NUMA topology, and your workload patterns. It&#8217;s a power tool for platform teams that have already outgrown basic Kubernetes resource management.</p><h3>Links</h3><ul><li><p><a href="https://github.com/koordinator-sh/koordinator">GitHub: koordinator-sh/koordinator</a></p></li><li><p><a href="https://koordinator.sh/docs/">Koordinator Docs</a></p></li></ul><div><hr></div><h2>&#128736;&#65039; One-Liner: OpenFeature - Feature Flags Without Vendor Lock-in</h2><p>Feature flags are everywhere. But every provider (LaunchDarkly, Flagsmith, Split, PostHog) has its own SDK. Switch providers, rewrite your integration code. Sound familiar?</p><p>OpenFeature is a CNCF standard that does for feature flags what OpenTelemetry did for observability: one API, swap the backend anytime.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;go&quot;,&quot;nodeId&quot;:&quot;e314572f-4b8b-4fff-9c74-1caecfb3a99e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-go">client := openfeature.NewClient("my-app")
enabled, _ := client.BooleanValue(ctx, "new-checkout", false, evalCtx)</code></pre></div><p>That code works with LaunchDarkly today and Flagsmith tomorrow. You change one line - the provider initialization - not the hundreds of flag checks scattered through your codebase. SDKs exist for Go, Java, Python, TypeScript, .NET, PHP, and more.</p><h3>Links</h3><ul><li><p><a href="https://openfeature.dev/">OpenFeature.dev</a></p></li><li><p><a href="https://github.com/open-feature">GitHub: open-feature</a></p></li></ul><div><hr></div><h2>&#128293; The Hot Take: Cloud-Native Is Accelerating, Not Stabilizing</h2><p>Look at this week&#8217;s tools. Dapr abstracts distributed patterns into HTTP calls. Kargo makes GitOps promotion declarative. WasmEdge challenges the container model itself. Koordinator squeezes 3x more value from existing hardware. OpenFeature standardizes yet another fragmented space.</p><p>Five years ago, we were still arguing about whether Kubernetes was production-ready. Now we&#8217;re building abstraction layers on top of abstraction layers - and they&#8217;re actually good. The CNCF landscape isn&#8217;t just growing, it&#8217;s maturing into composable building blocks.</p><p>My take: the teams that win in 2026 aren&#8217;t the ones running the most tools. They&#8217;re the ones picking the right abstractions early. One good choice (like adopting OpenTelemetry in 2022) saves years of migration pain later.</p><p>What&#8217;s your bet? Reply and tell me which tool here you&#8217;d adopt first.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Questions? Feedback? Reply to this email. I read every one.</p><div><hr></div>]]></content:encoded></item><item><title><![CDATA[Redis INT vs RAW Encoding: Why Your Counters Use 10x Less Memory]]></title><description><![CDATA[Inside Redis string encoding - how INT, EMBSTR, and RAW work, and what happens when you accidentally break them]]></description><link>https://podostack.com/p/redis-int-vs-raw-encoding-why-your</link><guid isPermaLink="false">https://podostack.com/p/redis-int-vs-raw-encoding-why-your</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Fri, 03 Apr 2026 09:01:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-7sd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Run this in your Redis CLI:</p><pre><code><code>SET counter 42
OBJECT ENCODING counter</code></code></pre><p>You&#8217;ll get <code>"int"</code>. Now do this:</p><pre><code><code>SET counter "hello"
OBJECT ENCODING counter</code></code></pre><p>That returns <code>"embstr"</code>. And if you set a string longer than 44 bytes, you&#8217;ll get <code>"raw"</code>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>Same command, same key, three different internal representations. Redis picks the most efficient encoding based on what you&#8217;re actually storing. And if you don&#8217;t understand how this works, you might be using 5-10x more memory than you need to.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-7sd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-7sd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-7sd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-7sd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-7sd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-7sd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1213299,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-7sd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-7sd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-7sd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-7sd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d5329e7-df08-4334-bccf-8ae3265269bf_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The three encodings</h2><p>Every Redis string value uses one of three internal encodings:</p><p><strong>INT</strong> - for values that fit in a 64-bit signed integer (-9223372036854775808 to 9223372036854775807). Redis doesn&#8217;t store the string &#8220;42&#8221; - it stores the actual number 42 as an 8-byte integer. No string overhead, no length tracking, no null terminator. Just the number.</p><p><strong>EMBSTR</strong> - for strings up to 44 bytes. Redis allocates a single contiguous memory block for both the object header and the string data. One <code>malloc()</code> call, one cache line. Fast to allocate, fast to access.</p><p><strong>RAW</strong> - for strings longer than 44 bytes. Redis uses SDS (Simple Dynamic String) - a custom string implementation that tracks length, capacity, and supports binary data. The object and SDS buffer are allocated separately - two <code>malloc()</code> calls, two memory regions.</p><h2>Why 44 bytes?</h2><p>That&#8217;s not arbitrary. A Redis object header takes 16 bytes. An SDS header for small strings takes 3 bytes. A null terminator takes 1 byte. A <code>jemalloc</code> allocation bucket is 64 bytes. So: 64 - 16 - 3 - 1 = 44. That&#8217;s the maximum string length that fits in a single 64-byte allocation alongside the object metadata.</p><p>At 45 bytes, Redis needs a second allocation. Two allocations mean more memory overhead, more fragmentation, and an extra pointer dereference on every access.</p><h2>The shared integer pool</h2><p>Here&#8217;s where it gets interesting. For integers from 0 to 9999, Redis doesn&#8217;t even allocate new objects. It maintains a pre-allocated pool of 10,000 integer objects at startup. Every key that stores the value <code>42</code> points to the same object in memory.</p><pre><code><code>SET user:1:login_count 42
SET user:2:login_count 42
SET metrics:errors 42</code></code></pre><p>All three keys reference the exact same Redis object. Zero additional memory per key (beyond the key entry itself). This is why counters and small numeric IDs are absurdly cheap in Redis.</p><p>You can verify this with <code>DEBUG OBJECT</code>:</p><pre><code><code>DEBUG OBJECT user:1:login_count</code></code></pre><p>Look for <code>refcount</code> in the output. If it&#8217;s greater than 1, you&#8217;re sharing an object from the pool. For integers 0-9999, the refcount will typically be very high because Redis itself uses these objects internally too.</p><h2>The encoding switch trap</h2><p>This is the part that bites people. Redis automatically converts between encodings - and it&#8217;s a one-way street for certain operations.</p><pre><code><code>SET counter 100
OBJECT ENCODING counter     -- "int"

APPEND counter " requests"
OBJECT ENCODING counter     -- "raw"

-- counter is now "100 requests" and there's no going back</code></code></pre><p>Once Redis converts from INT to RAW, it stays RAW even if you later set it back to a pure number. Well, actually - a fresh <code>SET</code> will re-evaluate and pick INT again. But <code>APPEND</code>, <code>SETRANGE</code>, and other mutation operations force a permanent encoding change.</p><p>The same thing happens with EMBSTR. Any write operation on an EMBSTR string promotes it to RAW, because EMBSTR is immutable by design - Redis can&#8217;t resize a single-allocation block without potentially moving it.</p><pre><code><code>SET name "alice"
OBJECT ENCODING name        -- "embstr"

APPEND name " smith"
OBJECT ENCODING name        -- "raw"</code></code></pre><p>The value is only 11 bytes, well under the 44-byte limit. But APPEND forced the conversion. If you&#8217;d done <code>SET name "alice smith"</code> instead, it would&#8217;ve been EMBSTR.</p><h2>Practical memory optimization</h2><p><strong>Use INCR/DECR for counters, not SET.</strong> <code>INCR mykey</code> initializes to 0 and increments atomically. The value stays INT-encoded. Don&#8217;t do <code>SET mykey "1"</code> followed by string manipulation - let Redis treat it as a number.</p><p><strong>Keep numeric IDs as numbers.</strong> <code>SET session:abc user_id 12345</code> keeps INT encoding. <code>SET session:abc user_id "user_12345"</code> forces RAW. Over millions of keys, that adds up fast.</p><p><strong>Avoid mutating short strings.</strong> If you&#8217;re building a string incrementally with APPEND, consider building it in your application and doing a single SET. That way Redis picks the optimal encoding based on the final value, instead of promoting through encodings as you append.</p><p><strong>Audit your encodings.</strong> Run <code>OBJECT ENCODING</code> on a sample of your keys. If counters show up as RAW instead of INT, something in your code is contaminating them with string operations.</p><h2>How much does it actually save?</h2><p>Ballpark for 1 million keys:</p><ul><li><p>INT (0-9999, shared pool): practically zero extra memory beyond key entries</p></li><li><p>INT (outside pool): ~16 bytes/value = ~16 MB</p></li><li><p>EMBSTR (20-byte strings): ~64 bytes/value = ~64 MB</p></li><li><p>RAW (100-byte strings): ~160+ bytes/value = ~160 MB</p></li></ul><p>The jump from INT to RAW for a simple counter is roughly 10x. Across millions of keys, that&#8217;s gigabytes of waste.</p><h2>The rule of thumb</h2><p>If it&#8217;s a number, keep it a number. If it&#8217;s a short string, write it once (don&#8217;t append). If it&#8217;s a long string, there&#8217;s nothing to optimize - RAW is the only option, and that&#8217;s fine. The wins come from not accidentally promoting cheap encodings into expensive ones.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly database internals, Kubernetes patterns, and Cloud Native tools ripe for production.</em></p><p><em>Curious what encoding your Redis keys are actually using? Run </em><code>OBJECT ENCODING</code><em> on a few of your busiest keys - you might be surprised.</em></p>]]></content:encoded></item><item><title><![CDATA[Partial Indexes in MySQL: The Trick PostgreSQL Doesn't Want You to Know]]></title><description><![CDATA[Emulating filtered indexes with generated columns and functional key parts - no migration required]]></description><link>https://podostack.com/p/partial-indexes-in-mysql-the-trick</link><guid isPermaLink="false">https://podostack.com/p/partial-indexes-in-mysql-the-trick</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 01 Apr 2026 09:01:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!u6nW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PostgreSQL has this nice trick - <code>CREATE INDEX ... WHERE status = 'pending'</code>. The index only stores rows that match the condition. If you have 50 million orders and only 200,000 are pending, your index is tiny. Scans are fast. Disk usage is minimal.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>MySQL doesn&#8217;t have that syntax. There&#8217;s no <code>WHERE</code> clause in <code>CREATE INDEX</code>. And if you google &#8220;partial index MySQL,&#8221; you&#8217;ll mostly find people saying &#8220;just switch to PostgreSQL.&#8221;</p><p>But here&#8217;s the thing - since MySQL 8.0, you can get almost the same result. It takes a different path, but the destination is the same: a small index that only contains the rows you care about.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u6nW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u6nW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!u6nW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!u6nW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!u6nW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u6nW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:928533,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191682708?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!u6nW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!u6nW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!u6nW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!u6nW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dda0ce4-6afb-46f9-84e2-f27159931c21_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The core idea</h2><p>Instead of telling the index which rows to include, you tell it what value to store. For rows you don&#8217;t want - you store NULL.</p><p>MySQL indexes do include NULL entries (unlike PostgreSQL&#8217;s partial indexes that skip them entirely), so you won&#8217;t get identical disk savings. But the optimizer still benefits massively because the non-NULL keys are clustered together, and your queries only scan that small portion.</p><p>The trick works through two mechanisms: functional key parts and generated stored columns.</p><h2>Approach 1: functional index</h2><p>Say you have an <code>orders</code> table with 50M rows. Only 0.4% are pending. You want to quickly find pending orders by their ID.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;1622f847-2162-4363-8869-f28f8bdad6dc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE INDEX idx_pending
    ON orders ((IF(status = 'pending', order_id, NULL)));</code></pre></div><p>The <code>IF()</code> expression returns the <code>order_id</code> for pending rows and NULL for everything else. The index stores all 50M entries, but only 200K have meaningful (non-NULL) values.</p><p>The catch? Your query has to use the exact same expression:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;c31d1c60-3e0d-41dc-b0ce-b37db552c793&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT *
FROM orders
WHERE IF(status = 'pending', order_id, NULL) = 12345;</code></pre></div><p>That&#8217;s ugly. It works, but it leaks implementation details into your application code. Your ORM won&#8217;t like it. Your teammates won&#8217;t like it either.</p><h2>Approach 2: generated column (the better way)</h2><p>This is what I&#8217;d actually recommend for production:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;aac9fb76-be97-4fd8-8022-81cb651f8f4a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE orders
  ADD COLUMN pending_id BIGINT
    AS (IF(status = 'pending', order_id, NULL)) STORED,
  ADD INDEX idx_pending_id (pending_id);</code></pre></div><p>Now you have a real column - <code>pending_id</code> - that&#8217;s automatically maintained by MySQL. It&#8217;s <code>order_id</code> for pending rows, NULL for everything else. Your queries look normal:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;7973f9e6-96e6-4c86-9e58-b868b4fe7eca&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT *
FROM orders
WHERE pending_id IS NOT NULL;

-- Or find a specific pending order:
SELECT *
FROM orders
WHERE pending_id = 12345;</code></pre></div><p>The optimizer picks up <code>idx_pending_id</code> automatically. No query hints needed. No ugly <code>IF()</code> in your WHERE clause. Run <code>EXPLAIN</code> and you&#8217;ll see the index scan hitting only the pending rows.</p><h2>Approach 3: boolean flag + composite</h2><p>When you need to fetch all pending orders sorted by date:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;b5c73ac6-1911-457a-b1ba-d3b7723a0ce7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">ALTER TABLE orders
  ADD COLUMN is_pending TINYINT(1)
    AS (status = 'pending') STORED,
  ADD INDEX idx_pending_date (is_pending, created_at);</code></pre></div><p>Now <code>SELECT * FROM orders WHERE is_pending = 1 ORDER BY created_at DESC</code> uses an index range scan. The index is compact because most rows have <code>is_pending = 0</code>, and the optimizer skips those efficiently.</p><h2>The gotchas</h2><p><strong>NULLs are still indexed.</strong> This is the biggest difference from PostgreSQL. A true partial index in Postgres with <code>WHERE status = 'pending'</code> physically excludes non-matching rows. MySQL&#8217;s approach stores NULL keys, so the index is larger. For a 50M row table with 200K pending rows, PostgreSQL&#8217;s partial index stores 200K entries. MySQL&#8217;s stores 50M entries (most of them NULL). Still way faster than a full index on <code>status</code>, but not as compact.</p><p><strong>Exact expression match matters for functional indexes.</strong> If your index uses <code>IF(status = 'pending', order_id, NULL)</code> but your query uses <code>WHERE status = 'pending'</code>, the optimizer won&#8217;t connect the dots. That&#8217;s why the generated column approach is better - it gives you a clean column name to query against.</p><p><strong>STORED, not VIRTUAL.</strong> Generated columns can be VIRTUAL (computed on read) or STORED (physically saved). Only STORED columns can be indexed. The <code>STORED</code> keyword means MySQL maintains the value on every INSERT and UPDATE, so there&#8217;s a small write overhead.</p><p><strong>Run ANALYZE TABLE after.</strong> The optimizer needs fresh statistics to know about your new index. Always <code>ANALYZE TABLE orders;</code> after adding the index.</p><h2>When this pays off</h2><p>The sweet spot is clear: a column with low cardinality where one value is rare and you query for it frequently. Some classic examples:</p><ul><li><p><code>status = 'pending'</code> on an orders table (0.5% of rows)</p></li><li><p><code>is_deleted = 1</code> on a soft-delete table (0.1% of rows)</p></li><li><p><code>needs_review = true</code> on a content moderation queue</p></li><li><p><code>retry_count &gt; 0</code> on a job queue</p></li></ul><p>If the rare value is more than 15-20% of the table, a regular index works fine. The partial approach shines when you&#8217;re indexing the needle, not the haystack.</p><h2>Quick validation</h2><p>After creating the index, always verify:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;c2665171-a0d9-4a10-a499-971d0131dcd9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">EXPLAIN ANALYZE
SELECT * FROM orders WHERE pending_id = 12345;</code></pre></div><p>You should see your <code>idx_pending_id</code> in the output, with a low row estimate. If EXPLAIN shows a full table scan, check that you ran <code>ANALYZE TABLE</code> and that your WHERE clause matches the generated column - not the original <code>status</code> column.</p><p>If you want to take this further, combine it with <a href="https://podostack.com/p/covering-indexes-query-speedup">covering indexes</a> - add the columns you SELECT to the index so MySQL never touches the table at all.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly database patterns, Kubernetes internals, and Cloud Native tools ripe for production.</em></p><p><em>Still running full indexes on columns where 99% of rows don&#8217;t matter? Try the generated column approach - it takes one ALTER TABLE and the improvement shows up immediately in EXPLAIN.</em></p>]]></content:encoded></item><item><title><![CDATA[eBPF Beyond Networking]]></title><description><![CDATA[Tetragon runtime enforcement, Parca continuous profiling, Falco vs Tetragon showdown, Sloth SLO automation, and Grafana Alloy]]></description><link>https://podostack.com/p/ebpf-tetragon-parca-falco-sloth-alloy</link><guid isPermaLink="false">https://podostack.com/p/ebpf-tetragon-parca-falco-sloth-alloy</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 31 Mar 2026 14:03:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sJT_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back to Podo Stack. In <a href="https://podostack.com/p/cilium-ebpf-kube-proxy-identity-hubble">Issue #9</a> we covered Cilium - eBPF rewriting the network layer. But networking is just one tentacle. This week: five tools that push eBPF into security, profiling, and reliability.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>Here&#8217;s what&#8217;s good this week.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sJT_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sJT_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!sJT_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!sJT_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!sJT_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sJT_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sJT_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!sJT_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!sJT_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!sJT_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1627384-3c96-48a4-9b8a-44e202cf9329_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128640; Sandbox Watch: Tetragon - Kill It at the Kernel</h2><h3>The problem</h3><p>Your container just spawned a shell. You know about it 30 seconds later because Falco sent an alert to Slack. By then, the attacker already read <code>/etc/shadow</code> and opened a reverse shell. Detection is nice. Prevention is better.</p><h3>The solution</h3><p>Tetragon doesn&#8217;t just watch syscalls - it blocks them. Right there in the kernel. Before the call even completes.</p><p>Built by the Cilium team (Isovalent, now Cisco), Tetragon hooks into the Linux kernel via eBPF and enforces security policies at the deepest level possible. When a process violates a rule, Tetragon sends a SIGKILL before the syscall returns. No round-trip to userspace. No window for TOCTOU attacks.</p><p>And because it runs in the kernel, the overhead is below 1%. No sidecar, no agent eating your CPU.</p><h3>What makes it special</h3><p>Tetragon is Kubernetes-aware by default. It maps low-level kernel events to pods, namespaces, and service accounts. You write policies like &#8220;block writes to <code>/etc</code> for pods labeled <code>app: frontend</code>&#8220; - as CRDs, not Lua scripts.</p><p>Recent additions make it even more serious: persistent enforcement continues working even if the Tetragon agent restarts. And redaction filters strip passwords and tokens from event logs right in the kernel.</p><h3>The catch</h3><p>Tetragon requires Linux 5.x+ with BTF support. Most modern distros have this, but old AMIs or custom kernels might not. Check before you deploy.</p><h3>Links</h3><ul><li><p><a href="https://github.com/cilium/tetragon">GitHub: cilium/tetragon</a></p></li><li><p><a href="https://tetragon.io/docs/">Tetragon Docs</a></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fC2r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fC2r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 424w, https://substackcdn.com/image/fetch/$s_!fC2r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 848w, https://substackcdn.com/image/fetch/$s_!fC2r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 1272w, https://substackcdn.com/image/fetch/$s_!fC2r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fC2r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png" width="1456" height="801" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:801,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:80285,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191680961?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fC2r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 424w, https://substackcdn.com/image/fetch/$s_!fC2r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 848w, https://substackcdn.com/image/fetch/$s_!fC2r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 1272w, https://substackcdn.com/image/fetch/$s_!fC2r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9aaa4c2-5758-4d4f-a3e8-d17271a04372_1752x964.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128142; Hidden Gem: Parca - Profile Everything, All the Time</h2><p>You profile your app when something&#8217;s slow. But by the time you attach a profiler, the problem is gone. Parca flips this: it profiles your entire cluster, continuously, with less than 1% overhead.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IgAY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IgAY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!IgAY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!IgAY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!IgAY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IgAY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IgAY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!IgAY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!IgAY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!IgAY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2411193-173c-4a68-a973-0f176fafdd8d_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>How it works</h3><p>Parca uses eBPF to sample stack traces at the kernel level. No code changes. No library imports. No restarts. It works across C, C++, Rust, Go, Python, Java, and Node.js - anything that runs on Linux.</p><p>Collected profiles go into FrostDB, a columnar database purpose-built for profile data. Then you get flame graphs, icicle charts, and - the killer feature - <strong>differential profiling</strong>. Compare Tuesday&#8217;s deployment to Monday&#8217;s. See exactly which function started eating more CPU. No guessing.</p><h3>Why this matters</h3><p>Traditional profiling costs 10-30% overhead. You&#8217;d never run it in production. Parca costs less than 1%, so it runs everywhere, always. When an incident hits at 3 AM, the profile data is already there.</p><p>Three practical wins: catch hotspots that waste cloud spend, find memory leaks in real time, and get instant post-mortems without &#8220;we should&#8217;ve had a profiler running.&#8221;</p><h3>Parca vs Pyroscope</h3><p>Pyroscope joined Grafana Labs and focuses on SDK-based profiling with broader language support. Parca stays pure eBPF - zero instrumentation, Kubernetes-native CRDs, CNCF sandbox project. If you&#8217;re already in the Cilium/eBPF world, Parca fits naturally.</p><h3>Links</h3><ul><li><p><a href="https://github.com/parca-dev/parca">GitHub: parca-dev/parca</a></p></li><li><p><a href="https://www.parca.dev/docs/overview">Parca Docs</a></p></li></ul><div><hr></div><h2>&#9876;&#65039; The Showdown: Falco vs Tetragon</h2><p>Both watch syscalls. Both protect your cluster. But they disagree on what to do about it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1QZU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1QZU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!1QZU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!1QZU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!1QZU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1QZU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1QZU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!1QZU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!1QZU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!1QZU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3352e63e-64e7-49cd-9c54-d9a2347085df_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Falco</strong> is the CNCF Graduated standard for runtime threat detection. It intercepts syscalls via eBPF (or a kernel module), enriches events with Kubernetes metadata, and evaluates them against YAML rules. When something looks bad - shell in a container, unexpected network connection, read on <code>/etc/shadow</code> - Falco raises an alert. It&#8217;s a fire alarm.</p><p><strong>Tetragon</strong> is a fire suppressor. Same data source (eBPF, syscalls), but instead of alerting, it kills the process in the kernel before the call finishes. No alert fatigue because the threat never completes.</p><p><strong>Falco:</strong><br>- Philosophy: Detect and alert<br>- Engine: Userspace rule evaluation<br>- Response: Alerts (Slack, PagerDuty, SIEM)<br>- Auto-remediation: Via Falco Talon (separate tool)<br>- Maturity: CNCF Graduated, 50+ output integrations</p><p><strong>Tetragon:</strong><br>- Philosophy: Enforce and prevent<br>- Engine: In-kernel eBPF programs<br>- Response: SIGKILL in kernel<br>- Auto-remediation: Built-in, kernel-level<br>- Maturity: CNCF project, Cilium ecosystem</p><h3>The verdict</h3><p>They&#8217;re complementary. Falco gives you the audit trail and the 50+ integrations for compliance. Tetragon gives you the kill switch. Run Falco for detection and forensics. Add Tetragon for the critical paths where &#8220;alert and hope someone notices&#8221; isn&#8217;t enough.</p><div><hr></div><h2>&#128300; The Pattern: SLO from YAML with Sloth</h2><h3>The problem</h3><p>Everyone agrees on SLOs. Nobody measures them. Why? Because writing Prometheus recording rules for SLO tracking is painful. You need multi-window burn rate calculations, error budget tracking, and alerting rules that predict budget exhaustion. That&#8217;s 50+ lines of PromQL per SLO. Per service.</p><h3>The fix</h3><p>Sloth takes a YAML file and generates all of it.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;f3490de2-f7a3-45bf-9298-2c8c0d7b174b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">version: "prometheus/v1"
service: "my-api"
slos:
  - name: "availability"
    objective: 99.9
    sli:
      events:
        error_query: sum(rate(http_requests_total{status=~"5.."}[{{.window}}]))
        total_query: sum(rate(http_requests_total[{{.window}}]))</code></pre></div><p>Run <code>sloth generate</code>, apply the output, and you get:</p><ul><li><p>Recording rules for burn rates across multiple time windows (5min, 30min, 1h, 2d, 6h)</p></li><li><p>Multi-burn-rate alerts that fire <strong>before</strong> your budget runs out</p></li><li><p>Grafana dashboards showing remaining error budget</p></li></ul><p>99.9% availability sounds impressive until Sloth shows you that&#8217;s 43 minutes of allowed downtime per month. And your budget is burning 3x faster than expected after last Friday&#8217;s deploy.</p><h3>Links</h3><ul><li><p><a href="https://github.com/slok/sloth">GitHub: slok/sloth</a></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ITbd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ITbd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 424w, https://substackcdn.com/image/fetch/$s_!ITbd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 848w, https://substackcdn.com/image/fetch/$s_!ITbd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!ITbd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ITbd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png" width="1456" height="987" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:987,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:97060,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/191680961?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ITbd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 424w, https://substackcdn.com/image/fetch/$s_!ITbd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 848w, https://substackcdn.com/image/fetch/$s_!ITbd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!ITbd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa953d6b6-aa7d-4f5f-b737-af1f9becea05_1752x1188.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>&#128736;&#65039; The One-Liner: Grafana Alloy</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;665000de-bda9-45c3-af38-fd0545d24f22&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">kubectl get pods -n monitoring -l app.kubernetes.io/name=alloy \
  -o custom-columns='NAME:.metadata.name,READY:.status.conditions[?(@.type=="Ready")].status'</code></pre></div><p>If you&#8217;re still running Prometheus + Promtail + OTel Collector as three separate things, Grafana Alloy replaces all of them. One agent. Metrics, logs, traces, profiles.</p><p>Alloy is a Grafana distribution of the OpenTelemetry Collector with a programmable config language (think HCL, not YAML). It natively scrapes Prometheus targets, collects logs, receives OTLP traces, and - the standout feature - supports built-in clustering. Multiple Alloy instances automatically distribute scrape targets across the cluster.</p><p>It&#8217;s vendor-neutral despite the Grafana branding. Send data to Prometheus, Loki, Tempo, Mimir, or anything OTLP-compatible.</p><h3>Links</h3><ul><li><p><a href="https://grafana.com/docs/alloy/latest/">Grafana Alloy Docs</a></p></li></ul><div><hr></div><h2>&#128293; The Hot Take: The Three Pillars Are Now Four</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dDz4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dDz4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!dDz4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!dDz4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!dDz4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dDz4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dDz4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!dDz4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!dDz4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!dDz4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea49fd45-5f1e-4657-ae7d-74635cb2d340_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We spent years building the &#8220;three pillars of observability&#8221; - metrics, logs, traces. And then we realized we still can&#8217;t answer &#8220;why is this function slow?&#8221;</p><p>Continuous profiling is the fourth pillar. Parca, Pyroscope, and even Grafana Cloud are betting on it. The difference: profiles show you <em>where</em> in the code the problem is. Not which service, not which endpoint - which function, which line.</p><p>Combine Prometheus (what&#8217;s broken), Grafana Tempo (where the request went), Loki (what happened around it), and Parca (why it&#8217;s slow at the code level). That&#8217;s full observability.</p><p>Agree? Reply and tell me if you&#8217;re already profiling in production.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Questions? Feedback? Reply to this email. I read every one.</p><div><hr></div><p>&#127815; <strong>Podo Stack</strong> - Ripe for Prod.</p>]]></content:encoded></item><item><title><![CDATA[Vertical Partitioning: Split Your Wide Table Before It Splits Your Performance]]></title><description><![CDATA[How to make narrow tables fast by moving cold columns out of the hot path]]></description><link>https://podostack.com/p/vertical-partitioning-wide-tables</link><guid isPermaLink="false">https://podostack.com/p/vertical-partitioning-wide-tables</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Fri, 27 Mar 2026 07:02:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!MAzQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your users table has 45 columns. Your queries touch 5. Every time the database reads a row, it loads all 45 columns from disk into memory - the user&#8217;s login, email, and last_seen that you actually need, plus the 2KB biography, the profile picture URL, notification preferences, OAuth tokens, and 37 other columns you didn&#8217;t ask for. All of it pulled into the buffer pool, competing for cache space, slowing everything down.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>I hit this wall on a SaaS product with 2 million users. The user profile page loaded in 200ms. The login check - which only needed <code>email</code>, <code>password_hash</code>, and <code>is_active</code> - took 45ms. For a login flow that runs thousands of times per minute, 45ms felt wrong. The table had grown to 38 columns over three years of feature additions. Someone had added a <code>bio</code> TEXT column, a <code>settings</code> JSONB column, and an <code>avatar_blob</code> for users who uploaded directly instead of using URLs.</p><p>I split the table in two. Login dropped to 8ms. The profile page didn&#8217;t change - it was already doing a full load. But the hot path got 5x faster because the database was reading 200-byte rows instead of 2KB rows.</p><p>That&#8217;s vertical partitioning.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MAzQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MAzQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!MAzQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!MAzQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!MAzQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MAzQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1797716,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920892?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MAzQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!MAzQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!MAzQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!MAzQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36faf361-41ad-4f71-9db3-6ad2818255ca_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>What vertical partitioning actually does</h2><p>Horizontal partitioning splits a table into chunks by rows - users 1 through 1 million in one partition, 1 million to 2 million in another. Same columns, different rows.</p><p>Vertical partitioning splits a table by columns. Hot columns that you query constantly go into one table. Cold columns that you rarely need go into another. Same rows (same primary key), different columns.</p><pre><code><code>BEFORE: one wide table
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474; users (45 columns, ~2KB per row)                        &#9474;
&#9474; id | email | password_hash | last_seen | bio | avatar | &#9474;
&#9474;    | settings | oauth_tokens | preferences | ...        &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;

AFTER: two narrow tables
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474; users (6 columns, ~200B per row) &#9474;  &#9474; user_profiles (12 columns)  &#9474;
&#9474; id | email | password_hash       &#9474;  &#9474; user_id (FK) | bio          &#9474;
&#9474;    | is_active | last_seen       &#9474;  &#9474; avatar | settings           &#9474;
&#9474;    | created_at                  &#9474;  &#9474; oauth_tokens | preferences  &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</code></code></pre><h2>Why narrower rows are faster</h2><p>Databases read data in pages - typically 8KB in PostgreSQL, 16KB in MySQL InnoDB. When you execute a query, the database doesn&#8217;t read individual rows. It reads entire pages into memory.</p><p>A wide row (2KB) fits about 4 rows per 8KB page. A narrow row (200 bytes) fits about 40 rows per page. That&#8217;s 10x more rows per I/O operation.</p><p>This has cascading effects:</p><p><strong>Buffer pool efficiency.</strong> Your database caches pages in memory. If each page holds 40 rows instead of 4, the same amount of RAM caches 10x more rows. Cache hit rates go up. Disk reads go down.<strong>Sequential scan speed.</strong> When PostgreSQL does a sequential scan on a narrow table, it reads fewer pages to cover the same number of rows. A full scan of 2 million narrow rows might read 50K pages. The same scan on the wide table reads 500K pages.</p><p><strong>Index performance.</strong> In PostgreSQL, the heap (table data) is separate from indexes. An index lookup finds the row location, then fetches the row from the heap. With narrower rows, the heap is smaller, so even random heap fetches are more likely to hit cached pages.</p><h2>Implementation: step by step</h2><p>Let&#8217;s walk through splitting a real users table.</p><h3>1. Identify hot and cold columns</h3><p>Look at your actual queries. Not what you think runs, but what really runs.</p><pre><code><code>-- PostgreSQL: find which columns are actually used
-- Check pg_stat_statements or your query logs
-- Look for patterns like:
SELECT email, password_hash, is_active FROM users WHERE email = $1;  -- login: HOT
SELECT last_seen FROM users WHERE id = $1;                           -- presence: HOT
SELECT bio, avatar, settings FROM users WHERE id = $1;               -- profile page: COLD</code></code></pre><p>Hot columns are touched by high-frequency queries - authentication, authorization, listings, search results. Cold columns are touched by low-frequency queries - profile detail pages, settings screens, admin views.</p><h3>2. Create the cold table</h3><pre><code><code>-- Create the details table with the same primary key
CREATE TABLE user_profiles (
    user_id     BIGINT PRIMARY KEY REFERENCES users(id),
    bio         TEXT,
    avatar      BYTEA,
    settings    JSONB DEFAULT '{}',
    oauth_tokens JSONB,
    preferences JSONB DEFAULT '{}',
    company     VARCHAR(200),
    location    VARCHAR(200),
    website     VARCHAR(500),
    twitter     VARCHAR(100),
    github      VARCHAR(100),
    linkedin    VARCHAR(100)
);</code></code></pre><h3>3. Migrate the data</h3><pre><code><code>-- Copy cold columns to the new table
INSERT INTO user_profiles (user_id, bio, avatar, settings, oauth_tokens,
    preferences, company, location, website, twitter, github, linkedin)
SELECT id, bio, avatar, settings, oauth_tokens,
    preferences, company, location, website, twitter, github, linkedin
FROM users;

-- Verify row counts match
SELECT COUNT(*) FROM users;          -- 2,000,000
SELECT COUNT(*) FROM user_profiles;  -- 2,000,000</code></code></pre><h3>4. Drop cold columns from the main table</h3><pre><code><code>-- Remove cold columns from the hot table
ALTER TABLE users
    DROP COLUMN bio,
    DROP COLUMN avatar,
    DROP COLUMN settings,
    DROP COLUMN oauth_tokens,
    DROP COLUMN preferences,
    DROP COLUMN company,
    DROP COLUMN location,
    DROP COLUMN website,
    DROP COLUMN twitter,
    DROP COLUMN github,
    DROP COLUMN linkedin;</code></code></pre><p>In PostgreSQL, <code>DROP COLUMN</code> doesn&#8217;t physically remove the data immediately - it marks the column as dropped. Run <code>VACUUM FULL users</code> to reclaim the space (this locks the table, so schedule it during maintenance).5. Create a view for backward compatibility</p><pre><code><code>-- Applications that SELECT * still work
CREATE VIEW users_full AS
SELECT u.*, p.bio, p.avatar, p.settings, p.oauth_tokens,
    p.preferences, p.company, p.location, p.website,
    p.twitter, p.github, p.linkedin
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id;</code></code></pre><p>The <code>LEFT JOIN</code> ensures users without a profile row still appear. Existing queries that reference the old wide table can switch to the view without code changes - at least as a migration bridge.</p><h2>Handling writes</h2><p>This is where vertical partitioning gets tricky. Before the split, inserting a new user was one statement. Now it&#8217;s two, and they need to be atomic.</p><pre><code><code>-- Must be in a single transaction
BEGIN;

INSERT INTO users (email, password_hash, is_active, created_at)
VALUES ('new@example.com', '$2b$12$...', true, NOW())
RETURNING id;

-- Use the returned id
INSERT INTO user_profiles (user_id, bio, settings)
VALUES (currval('users_id_seq'), '', '{}');

COMMIT;</code></code></pre><p>If your application uses an ORM, you&#8217;ll need to update the model layer. In most ORMs, this means creating a separate <code>UserProfile</code> model with a one-to-one relationship. It&#8217;s more code, but it&#8217;s straightforward.</p><p>For updates, the same rule applies - if you&#8217;re updating columns in both tables, wrap it in a transaction:</p><pre><code><code>BEGIN;
UPDATE users SET last_seen = NOW() WHERE id = 42;
UPDATE user_profiles SET bio = 'Updated bio' WHERE user_id = 42;
COMMIT;</code></code></pre><p>In practice, most updates only touch one table. The login flow updates <code>last_seen</code> in <code>users</code>. The profile edit updates <code>bio</code> in <code>user_profiles</code>. Cross-table updates are the exception, not the rule.</p><h2>Common use cases</h2><p><strong>User profiles.</strong> The example above. Authentication data (hot) vs display data (cold). Almost every application with user accounts benefits from this.</p><p><strong>Product catalogs.</strong> Core product data - <code>id</code>, <code>name</code>, <code>price</code>, <code>category</code>, <code>is_available</code> - queried on every search result and listing page. Extended data - <code>full_description</code> TEXT, <code>specifications</code> JSONB, <code>manufacturer_details</code> - only loaded on the product detail page.</p><p><strong>Sensitive data isolation.</strong> PII columns (SSN, date of birth, bank account) in a separate table with stricter access controls, encryption at rest, and audit logging. The main table doesn&#8217;t even contain the sensitive data, so a breach of the main table leaks less.</p><p><strong>Audit columns.</strong> <code>created_by</code>, <code>updated_by</code>, <code>approved_by</code>, <code>audit_notes</code> - columns that exist for compliance but aren&#8217;t used in normal application queries. Moving them out keeps the hot table lean.</p><h2>When NOT to do this</h2><p><strong>Small tables.</strong> If your table has 10,000 rows and fits entirely in memory regardless of width, vertical partitioning adds complexity for zero performance gain. Don&#8217;t optimize what isn&#8217;t slow.</p><p><strong>All columns are hot.</strong> If your queries genuinely use most columns most of the time, splitting the table just adds JOINs. Vertical partitioning only helps when there&#8217;s a clear hot/cold divide.</p><p><strong>Write-heavy workloads with cross-column updates.</strong> If every write touches columns in both tables, you&#8217;re doubling your write I/O and adding transaction coordination overhead. The read savings might not offset the write cost.</p><h2>Measuring the impact</h2><p>Before and after, check these:</p><pre><code><code>-- PostgreSQL: table size before and after
SELECT pg_size_pretty(pg_total_relation_size('users'));

-- Average row width
SELECT avg(pg_column_size(t.*)) FROM users t LIMIT 10000;

-- Buffer cache hit ratio
SELECT
    sum(heap_blks_hit) AS cache_hits,
    sum(heap_blks_read) AS disk_reads,
    round(sum(heap_blks_hit)::numeric /
        (sum(heap_blks_hit) + sum(heap_blks_read)), 4) AS hit_ratio
FROM pg_statio_user_tables
WHERE relname = 'users';</code></code></pre><p>If your cache hit ratio goes from 85% to 98% after the split, you&#8217;ve freed up buffer pool space that every query on the system benefits from. The impact often extends beyond the table you partitioned.</p><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly database patterns and Cloud Native tools ripe for production.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>Have you split a wide table in production? What was the before/after difference? I&#8217;d love to hear your numbers - reply to this email or leave a comment.</em></p>]]></content:encoded></item><item><title><![CDATA[Change Data Capture: Stop Copying 50M Rows to Move 5K Changes]]></title><description><![CDATA[Three CDC methods compared - timestamps, triggers, and log-based - with trade-offs and real examples]]></description><link>https://podostack.com/p/change-data-capture-cdc-intro</link><guid isPermaLink="false">https://podostack.com/p/change-data-capture-cdc-intro</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Wed, 25 Mar 2026 07:01:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DCRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your ETL copies 50 million rows every night. Only 5,000 changed. The other 49,995,000 rows are identical to yesterday. You&#8217;re burning compute, network, and storage to move data that hasn&#8217;t moved.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>I&#8217;ve seen this pattern at three different companies. The nightly batch job runs for 4 hours, the data warehouse is &#8220;fresh&#8221; by 6 AM, and everyone accepts this as normal. It isn&#8217;t. Change Data Capture (CDC) exists specifically to solve this - track only what changed, move only the deltas. The difference between copying a full table and streaming 5K changes is the difference between hauling the entire library and borrowing one book. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DCRk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DCRk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!DCRk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!DCRk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!DCRk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DCRk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1869605,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920788?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DCRk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!DCRk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!DCRk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!DCRk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fad3375-98c4-48cb-90f6-358753c1e115_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>What CDC actually does</h2><p>CDC captures INSERT, UPDATE, and DELETE events as they happen (or shortly after) and delivers them downstream. Instead of asking &#8220;what does the table look like right now?&#8221; you ask &#8220;what changed since last time I checked?&#8221;</p><p>The result is the same - your target stays in sync with the source. But the cost drops dramatically. Less data transferred, less compute on both sides, and the target can be minutes behind the source instead of hours.</p><p>There are three main approaches, each with different trade-offs.</p><h2>Method 1: Timestamp-based</h2><p>The simplest form. Add <code>created_at</code> and <code>updated_at</code> columns to your table, then query for rows modified since the last sync.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;922d28f2-08fb-47d2-9c62-1dd5d2d76482&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Source table
CREATE TABLE orders (
    id          BIGINT PRIMARY KEY,
    customer_id INT,
    amount      DECIMAL(10,2),
    status      VARCHAR(20),
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- CDC query: get everything that changed since last run
SELECT *
FROM orders
WHERE updated_at &gt; '2026-03-07 00:00:00';</code></pre></div><p>Your ETL runs this query every 15 minutes, processes the results, and records the latest <code>updated_at</code> as the watermark for the next run.</p><p><strong>Pros:</strong> Dead simple. No special infrastructure. Works with any database. You can implement it in an afternoon.</p><p><strong>Cons:</strong> It misses hard deletes. If someone runs <code>DELETE FROM orders WHERE id = 42</code>, there&#8217;s no row left to have an <code>updated_at</code>. The row just disappears, and your target never finds out. You&#8217;d need a soft-delete pattern (<code>is_deleted</code> flag) or periodic full reconciliation to catch these.</p><p>It also struggles with bulk updates that don&#8217;t touch the timestamp column. If someone does <code>ALTER TABLE orders ADD COLUMN priority INT DEFAULT 0</code>, every row now has the same value but <code>updated_at</code> didn&#8217;t change. Schema changes are invisible.</p><p>Good for: simple sync scenarios where deletes are rare and you control the source schema.</p><h2>Method 2: Trigger-based</h2><p>Database triggers fire on every INSERT, UPDATE, and DELETE and write the change to a shadow table.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;1e029934-b009-4ffa-a075-d345ed1baa3c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">-- Shadow table to capture changes
CREATE TABLE orders_changes (
    change_id   BIGSERIAL PRIMARY KEY,
    operation   CHAR(1),  -- 'I', 'U', 'D'
    changed_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    order_id    BIGINT,
    customer_id INT,
    amount      DECIMAL(10,2),
    status      VARCHAR(20)
);

-- Trigger function (PostgreSQL)
CREATE OR REPLACE FUNCTION capture_order_changes()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'DELETE' THEN
        INSERT INTO orders_changes (operation, order_id, customer_id, amount, status)
        VALUES ('D', OLD.id, OLD.customer_id, OLD.amount, OLD.status);
        RETURN OLD;
    ELSE
        INSERT INTO orders_changes (operation, order_id, customer_id, amount, status)
        VALUES (
            CASE WHEN TG_OP = 'INSERT' THEN 'I' ELSE 'U' END,
            NEW.id, NEW.customer_id, NEW.amount, NEW.status
        );
        RETURN NEW;
    END IF;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER orders_cdc_trigger
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW EXECUTE FUNCTION capture_order_changes();</code></pre></div><p>Your ETL reads from <code>orders_changes</code>, processes the deltas, and deletes or marks the consumed rows.</p><p><strong>Pros:</strong> Catches everything - inserts, updates, and deletes. No row is missed. You get the before and after state if you capture both <code>OLD</code> and <code>NEW</code>.</p><p><strong>Cons:</strong> Triggers are synchronous. Every <code>INSERT INTO orders</code> now also does an <code>INSERT INTO orders_changes</code> within the same transaction. On a table with 10,000 writes per second, that&#8217;s 10,000 extra inserts. Your OLTP workload just got heavier.</p><p>Triggers are also database-specific. The PostgreSQL trigger above won&#8217;t work on MySQL or SQL Server. You&#8217;re writing and maintaining CDC logic per database engine. And triggers can be fragile - they silently break when someone alters the table structure without updating the trigger.</p><p>Good for: moderate-write workloads where you need to catch deletes and don&#8217;t want external tooling.</p><h2>Method 3: Log-based (the gold standard)</h2><p>Every transactional database maintains a write-ahead log (WAL in PostgreSQL, binlog in MySQL, redo log in Oracle, transaction log in SQL Server). This log records every change before it hits the actual data files. It&#8217;s how databases survive crashes - replay the log, recover the state.</p><p>Log-based CDC reads this log directly. No queries against the table, no triggers, no impact on OLTP performance. The log is already being written - CDC just tails it.</p><pre><code><code>PostgreSQL WAL &#8594; Debezium &#8594; Kafka &#8594; your data warehouse
MySQL binlog   &#8594; Debezium &#8594; Kafka &#8594; your data warehouse
SQL Server CDC &#8594; Fivetran  &#8594; Snowflake</code></code></pre><p>Debezium is the most popular open-source tool for this. It connects to the database&#8217;s replication slot (PostgreSQL) or binlog (MySQL), reads every committed transaction, and produces change events as JSON:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;dfbc2e79-9c40-4d24-bbf9-73a934be1487&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{
  "op": "u",
  "before": {"id": 42, "status": "pending", "amount": 99.99},
  "after":  {"id": 42, "status": "shipped", "amount": 99.99},
  "source": {
    "db": "production",
    "table": "orders",
    "ts_ms": 1709827200000
  }
}</code></pre></div><p>You get the operation type (<code>c</code> = create, <code>u</code> = update, <code>d</code> = delete, <code>r</code> = read/snapshot), the before state, the after state, and metadata about where the change came from. Everything you need to replay the change downstream.</p><p><strong>Pros:</strong> Asynchronous - zero impact on OLTP performance. Catches everything including DDL changes. Near real-time (seconds of latency, not hours). Works at any scale because you&#8217;re reading a sequential log, not scanning a table.</p><p><strong>Cons:</strong> More infrastructure to run. Debezium needs Kafka (or Kafka Connect), you need to manage connectors, handle schema evolution, monitor lag. It&#8217;s not an afternoon project.</p><p>The database also needs to be configured for logical replication (PostgreSQL) or row-based binlog (MySQL). In managed services like RDS or Cloud SQL, this is usually a checkbox. On-premise, it might mean changing database parameters and restarting.</p><p>Good for: anything in production. If you&#8217;re serious about keeping a data warehouse or downstream service in sync, log-based CDC is the answer.</p><h2>Comparing the three</h2><p><strong>Timestamp-based:</strong> Doesn&#8217;t catch deletes. Adds query load to OLTP. Minutes of latency. Low setup complexity. Can&#8217;t catch DDL changes. Medium scale ceiling. Tools: any SQL client.</p><p><strong>Trigger-based:</strong> Catches deletes. Adds write overhead. Seconds of latency. Medium complexity. Can&#8217;t catch DDL. Medium scale ceiling. Tools: DB-native triggers.</p><p><strong>Log-based:</strong> Catches deletes. Zero OLTP impact. Seconds of latency. High setup complexity. Catches DDL changes. High scale ceiling. Tools: Debezium, Fivetran, Striim.</p><h2>CDC and Slowly Changing Dimensions</h2><p>If you&#8217;re building a data warehouse, CDC pairs naturally with <a href="https://podostack.com/p/slowly-changing-dimensions-explained">Slowly Changing Dimensions</a>. CDC tells you <em>what changed</em>. SCD defines <em>how to store</em> that change in your dimension tables.</p><p>A Type 2 SCD workflow with log-based CDC looks like this:</p><ol><li><p>Debezium captures an update: customer moved from New York to Austin</p></li><li><p>Your ETL receives the change event</p></li><li><p>It closes the current dimension row (<code>valid_to = today</code>, <code>is_current = false</code>)</p></li><li><p>It inserts a new row for the Austin version (<code>valid_from = today</code>, <code>is_current = true</code>)</p></li></ol><p>Without CDC, you&#8217;d scan the entire customer table every night to detect this one address change. With CDC, the change arrives within seconds and your dimension is always current.</p><h2>Getting started</h2><p>If you&#8217;re running on timestamp-based sync and it works - don&#8217;t fix what isn&#8217;t broken. Add it to your migration backlog and prioritize based on pain.</p><p>If you&#8217;re feeling the pain - stale data, missed deletes, long batch windows - here&#8217;s the progression:</p><ol><li><p><strong>Start with timestamp-based</strong> if you just need to prototype. Add <code>updated_at</code> columns, write a simple sync script, validate the approach.</p></li><li><p><strong>Move to log-based</strong> when you need reliability. Set up Debezium with Kafka Connect, point it at your database, and let it stream changes. Skip the trigger phase - it adds complexity without the benefits of log-based CDC.</p></li><li><p><strong>Monitor lag.</strong> CDC is only useful if the stream keeps up with the source. Track consumer lag in Kafka, alert on it, and scale consumers if needed.</p></li></ol><p>The tools are mature. Debezium has been in production at thousands of companies for years. Fivetran and Striim handle the infrastructure for you if you don&#8217;t want to run Kafka yourself. The hard part isn&#8217;t the tooling - it&#8217;s deciding to stop copying 50 million rows when you only need 5,000.</p><div><hr></div><p><em>Found this useful? Subscribe to <a href="https://podostack.com">Podo Stack</a> for weekly data engineering patterns and Cloud Native tools ripe for production.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>Already using CDC in production? Still on nightly batch ETL? I&#8217;d love to hear what your setup looks like - reply to this email or leave a comment.</em></p>]]></content:encoded></item><item><title><![CDATA[Your Infrastructure Has an API Now, and It's Not Terraform]]></title><description><![CDATA[Crossplane control plane, Composition Functions, Claims vs Terraform, Kyverno guardrails, and debugging the XR chain]]></description><link>https://podostack.com/p/crossplane-infrastructure-api-compositions-claims</link><guid isPermaLink="false">https://podostack.com/p/crossplane-infrastructure-api-compositions-claims</guid><dc:creator><![CDATA[Ilia Gusev]]></dc:creator><pubDate>Tue, 24 Mar 2026 07:02:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!R7zA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a href="https://podostack.com/p/golden-paths-guardrails-catalog">Issue #4</a>, we talked about Platform Engineering and briefly mentioned Crossplane as a Terraform alternative. Time to go deep. What if your infrastructure had a real API - not a CLI tool you run from a CI pipeline, but a Kubernetes controller that never stops reconciling?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><p>That&#8217;s exactly what Crossplane does. And if you&#8217;re still wrapping Terraform in CI jobs, you might be solving 2025&#8217;s problems with 2016&#8217;s architecture.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R7zA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R7zA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!R7zA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!R7zA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!R7zA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R7zA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1701241,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920446?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R7zA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!R7zA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!R7zA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!R7zA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F239dd43a-c197-4fc0-a9b5-ec854a1f82fb_2752x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>The Pattern: Crossplane as Cloud Control Plane</h2><h3>Your infrastructure is a Kubernetes resource now.</h3><p>Crossplane extends the K8s API with CRDs for cloud resources. RDS instances, S3 buckets, VPCs, IAM roles - all of them become objects in your cluster. You <code>kubectl apply</code> them just like you would a Deployment or a ConfigMap.</p><p>This sounds like Terraform with extra steps. It&#8217;s not. The difference is fundamental. Terraform runs as a CLI - you execute <code>terraform apply</code>, it creates resources, writes state to a file, and walks away. If someone modifies the resource manually in the console? Terraform doesn&#8217;t know until you run <code>terraform plan</code> again. That&#8217;s drift, and it&#8217;s your problem.</p><p>Crossplane runs reconciliation controllers. Continuously. Every 10 minutes (configurable), it checks actual cloud state against desired state and fixes any drift. No human intervention. No CI job. It just works - exactly like how a Deployment controller makes sure your pods are always running.</p><p>The architecture has four layers. A <strong>Provider</strong> is a controller that talks to a specific cloud API (AWS, GCP, Azure). An <strong>XRD</strong> (Composite Resource Definition) is the schema for your custom abstraction. A <strong>Composition</strong> is the implementation - it maps your abstraction to actual cloud resources. And a <strong>Claim</strong> is what developers interact with.</p><p>Here&#8217;s the workflow. A developer runs <code>kubectl apply -f postgres-claim.yaml</code>. Crossplane creates an RDS instance, attaches security groups, configures parameter groups, and writes the connection credentials into a Kubernetes Secret in the developer&#8217;s namespace. One YAML file. No Terraform, no tickets, no waiting.</p><p>Crossplane graduated in the CNCF in 2024. It&#8217;s not an experiment anymore.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UwBK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UwBK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 424w, https://substackcdn.com/image/fetch/$s_!UwBK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 848w, https://substackcdn.com/image/fetch/$s_!UwBK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 1272w, https://substackcdn.com/image/fetch/$s_!UwBK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UwBK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png" width="1456" height="951" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:951,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:96634,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920446?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UwBK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 424w, https://substackcdn.com/image/fetch/$s_!UwBK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 848w, https://substackcdn.com/image/fetch/$s_!UwBK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 1272w, https://substackcdn.com/image/fetch/$s_!UwBK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f4c56c-7bca-4cc8-a117-a6a3b70bc03e_1752x1144.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Links</h3><ul><li><p><a href="https://docs.crossplane.io">Crossplane Docs</a></p></li><li><p><a href="https://www.cncf.io/projects/crossplane/">CNCF Graduation Announcement</a></p></li></ul><div><hr></div><h2>Hidden Gem: Composition Functions</h2><h3>When YAML patching isn&#8217;t enough, bring code.</h3><p>Standard Crossplane Compositions use patch-and-transform - you map fields from the Claim to the managed resources. It works, but it&#8217;s limited. No loops. No conditionals. No &#8220;create this resource only if the environment is production.&#8221;</p><p>Composition Functions change everything. Instead of static YAML patching, your Composition runs a pipeline of gRPC functions. Each function is a container - written in Go, Python, whatever you like - that receives the composite resource and desired state, then returns modified desired state.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;9e2edab8-3885-49af-8855-75523bc800d7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">apiVersion: apiextensions.crossplane.io/v1
kind: Composition
spec:
  mode: Pipeline
  pipeline:
    - step: create-resources
      functionRef:
        name: function-go-templating
    - step: add-tags
      functionRef:
        name: function-auto-tagger
    - step: validate
      functionRef:
        name: function-compliance-check</code></pre></div><p>This unlocks things that were impossible before. Dynamic naming based on environment. Conditional resource creation. External API calls during rendering - like fetching the latest AMI ID or checking a CMDB. You&#8217;re writing real logic, not fighting YAML indentation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kh0Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 424w, https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 848w, https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 1272w, https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png" width="1456" height="1408" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1408,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105347,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920446?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 424w, https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 848w, https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 1272w, https://substackcdn.com/image/fetch/$s_!Kh0Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c7c33ba-db66-4bba-b082-11edb965b2e5_1692x1636.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Links</h3><ul><li><p><a href="https://docs.crossplane.io/latest/concepts/composition-functions/">Composition Functions</a></p></li><li><p><a href="https://github.com/crossplane-contrib/function-go-templating">function-go-templating</a></p></li></ul><div><hr></div><h2>The Showdown: Crossplane vs Terraform</h2><p><strong>Terraform:</strong> CLI-driven (<code>terraform apply</code>). State in .tfstate file - drift possible. Manual drift detection (<code>terraform plan</code>). Self-service requires CI/CD wrapper. Abstraction via modules. Secrets live inside .tfstate.</p><p><strong>Crossplane:</strong> Continuous reconciliation loop. State in K8s etcd - auto-healing. Automatic drift detection via controller. Native self-service through K8s RBAC + Claims. Abstraction via XRD + Compositions. Secrets in K8s Secrets + Vault integration.</p><p>The verdict: Terraform isn&#8217;t going away. It&#8217;s still great for bootstrapping clusters, managing DNS zones, and one-off infrastructure. But for day-2 operations - the &#8220;developers need databases and don&#8217;t want to file tickets&#8221; problem - Crossplane fits the Kubernetes-native model better. Continuous reconciliation beats run-and-pray.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!H8-C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!H8-C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 424w, https://substackcdn.com/image/fetch/$s_!H8-C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 848w, https://substackcdn.com/image/fetch/$s_!H8-C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 1272w, https://substackcdn.com/image/fetch/$s_!H8-C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!H8-C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png" width="1456" height="951" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:951,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86188,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920446?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!H8-C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 424w, https://substackcdn.com/image/fetch/$s_!H8-C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 848w, https://substackcdn.com/image/fetch/$s_!H8-C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 1272w, https://substackcdn.com/image/fetch/$s_!H8-C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63a4e59f-3064-48c0-8ec7-4c145495d793_1752x1144.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Deep Dive: Crossplane + Kyverno = Safe Self-Service</h2><h3>Claims are K8s resources. Kyverno can validate them.</h3><p>Here&#8217;s where it gets interesting. A Claim is just a Kubernetes custom resource. That means every policy tool that works with K8s admission control - Kyverno, OPA Gatekeeper, Kubewarden - works with Claims out of the box.</p><p>Want to limit dev namespaces to <code>db.t3.small</code> instances with max 100GB storage? Kyverno ClusterPolicy. Want to auto-add billing tags and cost-center labels to every Claim? Kyverno mutation rule. Want to block <code>db.r6g.16xlarge</code> in anything that isn&#8217;t the <code>production</code> namespace? Validation policy.</p><p>The beauty is layered enforcement. Validate at the Claim level - that&#8217;s the fastest feedback loop, developers see errors immediately on <code>kubectl apply</code>. Then validate again at the MR (Managed Resource) level as a safety net. Two layers, same tool.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;5afcf873-ce35-4c7c-8b63-8f05161d7bd0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: limit-rds-size
spec:
  rules:
    - name: restrict-instance-class
      match:
        resources:
          kinds:
            - PostgreSQLInstanceClaim
      validate:
        message: "Dev namespaces limited to db.t3.small"
        pattern:
          spec:
            parameters:
              instanceClass: "db.t3.small"</code></pre></div><p>Self-service without guardrails is just chaos with a UI. Crossplane + Kyverno gives you both.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DXVq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DXVq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 424w, https://substackcdn.com/image/fetch/$s_!DXVq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 848w, https://substackcdn.com/image/fetch/$s_!DXVq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 1272w, https://substackcdn.com/image/fetch/$s_!DXVq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DXVq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png" width="1456" height="1471" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1471,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:129280,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920446?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DXVq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 424w, https://substackcdn.com/image/fetch/$s_!DXVq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 848w, https://substackcdn.com/image/fetch/$s_!DXVq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 1272w, https://substackcdn.com/image/fetch/$s_!DXVq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18881baf-2f2c-4dab-b492-ea08440d232b_1752x1770.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Links</h3><ul><li><p><a href="https://kyverno.io/docs/">Kyverno Docs</a></p></li></ul><div><hr></div><h2>The One-Liner: Secret Management Chain</h2><pre><code><code>kubectl get secret my-app-db-conn -n my-app -o jsonpath='{.data.endpoint}' | base64 -d</code></code></pre><p>Where did that secret come from? Nobody created it manually. Here&#8217;s the chain.</p><p>When you define a Claim with <code>writeConnectionSecretToRef</code>, Crossplane creates the cloud resource (say, an RDS instance), reads the generated credentials from the cloud API, and writes them into a Kubernetes Secret in the developer&#8217;s namespace. The developer&#8217;s app mounts that secret - no copy-pasting passwords, no Slack DMs with credentials.</p><p>The chain goes deeper. The Managed Resource (MR) writes its secret. The Composite Resource (XR) aggregates secrets from multiple MRs. The Claim exposes the final secret to the developer&#8217;s namespace. Three levels, fully automated.</p><p>For production, you don&#8217;t want raw K8s Secrets. Crossplane&#8217;s <code>StoreConfig</code> integrates with HashiCorp Vault, AWS Secrets Manager, or any external secret store. The developer&#8217;s workflow doesn&#8217;t change - they still reference a secret name in their Claim. Where it&#8217;s stored is an infrastructure concern, not a developer concern.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fHq4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fHq4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 424w, https://substackcdn.com/image/fetch/$s_!fHq4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 848w, https://substackcdn.com/image/fetch/$s_!fHq4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!fHq4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fHq4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png" width="1456" height="1062" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1062,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:100759,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://podostack.com/i/190920446?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fHq4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 424w, https://substackcdn.com/image/fetch/$s_!fHq4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 848w, https://substackcdn.com/image/fetch/$s_!fHq4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!fHq4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ddbcf41-7292-4cd8-9815-0cf6052ac83c_1752x1278.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Links</h3><ul><li><p><a href="https://docs.crossplane.io/latest/concepts/connection-details/">Connection Details</a></p></li></ul><div><hr></div><p>Questions? Feedback? Reply to this email. I read every one.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://podostack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://podostack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Podo Stack - Ripe for Prod.</p><p></p>]]></content:encoded></item></channel></rss>