<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kubernetes on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/</link><description>Recent content in Kubernetes on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 16 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/index.xml" rel="self" type="application/rss+xml"/><item><title>Gardener on Cleura</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/gardener/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/gardener/</guid><description>&lt;p&gt;&lt;a class="link" href="https://gardener.cloud/" target="_blank" rel="noopener"
 &gt;Gardener&lt;/a&gt; is a Kubernetes-as-a-Service framework that runs on Kubernetes and manages the lifecycle of other clusters declaratively. Rather than managing control planes by hand, Gardener treats clusters as a resource — defined, created, upgraded, and deleted via the Gardener API.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="concepts"&gt;Concepts
&lt;/h2&gt;&lt;p&gt;Gardener uses three layers:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Layer&lt;/th&gt;
 &lt;th&gt;What it is&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Garden cluster&lt;/td&gt;
 &lt;td&gt;Runs Gardener itself — the management control plane&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Seed cluster&lt;/td&gt;
 &lt;td&gt;Hosts the control planes of shoot clusters (as pods)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Shoot cluster&lt;/td&gt;
 &lt;td&gt;The cluster you actually use — nodes run on the target cloud&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The shoot cluster&amp;rsquo;s API server does not run on the shoot nodes. It runs as a pod inside the seed cluster. From the outside it behaves like any other Kubernetes cluster; internally the control plane is isolated from the data plane.&lt;/p&gt;
&lt;p&gt;Shoot clusters are defined as &lt;code&gt;Shoot&lt;/code&gt; resources applied to the garden cluster:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;core.gardener.cloud/v1beta1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Shoot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-cluster&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;garden-my-project&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cloudProfileName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;openstack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;region&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sto2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;provider&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;openstack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;workers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;worker-pool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;machine&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;l2.c2r4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;minimum&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;maximum&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kubernetes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1.30&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;networking&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;calico&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pods&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;100.128.0.0&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;/11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;nodes&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10.250.0.0&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;/16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;services&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;100.112.0.0&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;/13&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="shoot-cluster-on-cleura"&gt;Shoot cluster on Cleura
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://cleura.com/" target="_blank" rel="noopener"
 &gt;Cleura&lt;/a&gt; is a European OpenStack provider. Gardener provisions shoot nodes as OpenStack VMs via the OpenStack machine controller.&lt;/p&gt;
&lt;p&gt;Key integrations:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Component&lt;/th&gt;
 &lt;th&gt;Implementation&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Node provisioning&lt;/td&gt;
 &lt;td&gt;OpenStack VMs via Gardener machine controller&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Load balancers&lt;/td&gt;
 &lt;td&gt;Octavia via cloud-controller-manager&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Block storage&lt;/td&gt;
 &lt;td&gt;Cinder via CSI driver&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DNS&lt;/td&gt;
 &lt;td&gt;Manual or external-dns&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CNI&lt;/td&gt;
 &lt;td&gt;Calico (default) or configurable&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Gardener on Cleura does not provide an ingress controller or API gateway — these are brought in separately.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="networking"&gt;Networking
&lt;/h2&gt;&lt;p&gt;Gardener manages the cluster network configuration as part of the shoot spec. Pod, node, and service CIDRs are defined at cluster creation and must not overlap with the OpenStack network.&lt;/p&gt;
&lt;p&gt;On Cleura, nodes get OpenStack floating IPs for egress. Pod-to-pod traffic stays within the cluster overlay network (Calico by default). Traffic entering from outside the cluster goes through a &lt;code&gt;LoadBalancer&lt;/code&gt; service — either directly for raw TCP, or via a gateway controller for HTTP.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="ingress--classic-vs-gateway-api"&gt;Ingress — classic vs Gateway API
&lt;/h2&gt;&lt;p&gt;The classic Kubernetes &lt;code&gt;Ingress&lt;/code&gt; resource is HTTP-only, has no TCP support, and its feature set varies across implementations via non-standard annotations. The NGINX Ingress Controller — the most widely used implementation — is deprecated; NGINX now focuses on their &lt;a class="link" href="https://github.com/nginxinc/nginx-gateway-fabric" target="_blank" rel="noopener"
 &gt;Gateway API implementation&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;The &lt;a class="link" href="https://gateway-api.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Kubernetes Gateway API&lt;/a&gt; is the forward path — a set of CRDs (&lt;code&gt;Gateway&lt;/code&gt;, &lt;code&gt;HTTPRoute&lt;/code&gt;, &lt;code&gt;TCPRoute&lt;/code&gt;, &lt;code&gt;TLSRoute&lt;/code&gt;) with a standardized spec and first-class support for both HTTP and TCP.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Resource&lt;/th&gt;
 &lt;th&gt;Protocol&lt;/th&gt;
 &lt;th&gt;API&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Ingress&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;HTTP only&lt;/td&gt;
 &lt;td&gt;Kubernetes&lt;/td&gt;
 &lt;td&gt;Stable, legacy&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HTTPRoute&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;HTTP/HTTPS&lt;/td&gt;
 &lt;td&gt;Gateway API&lt;/td&gt;
 &lt;td&gt;Stable&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;TCPRoute&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Raw TCP&lt;/td&gt;
 &lt;td&gt;Gateway API&lt;/td&gt;
 &lt;td&gt;Experimental&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;TLSRoute&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;TLS passthrough&lt;/td&gt;
 &lt;td&gt;Gateway API&lt;/td&gt;
 &lt;td&gt;Experimental&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="envoy-gateway"&gt;Envoy Gateway
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://gateway.envoyproxy.io/" target="_blank" rel="noopener"
 &gt;Envoy Gateway&lt;/a&gt; is the CNCF implementation of the Kubernetes Gateway API using &lt;a class="link" href="https://www.envoyproxy.io/" target="_blank" rel="noopener"
 &gt;Envoy&lt;/a&gt; as the data plane. It supports &lt;code&gt;HTTPRoute&lt;/code&gt;, &lt;code&gt;TCPRoute&lt;/code&gt;, and &lt;code&gt;TLSRoute&lt;/code&gt; through a single &lt;code&gt;Gateway&lt;/code&gt; resource — one entry point, both protocols.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Octavia LB ← one LoadBalancer service per Gateway listener
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Envoy Gateway pod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;+------------------+------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTPRoute → ClusterIP pods TCPRoute → ClusterIP pods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Envoy Gateway is deployed into the shoot cluster and exposes a &lt;code&gt;LoadBalancer&lt;/code&gt; service via Octavia, the same as any other service. The Gateway API resources then declare what routes through it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tcproute--declaring-tcp-services"&gt;TCPRoute — declaring TCP services
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;TCPRoute&lt;/code&gt; attaches to a &lt;code&gt;Gateway&lt;/code&gt; listener and routes raw TCP traffic to a backend service. This is how a non-HTTP workload (e.g. a game server, a database proxy, a custom protocol service) gets exposed through the Gateway API rather than a standalone &lt;code&gt;LoadBalancer&lt;/code&gt; service.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gateway.networking.k8s.io/v1alpha2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;TCPRoute&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-tcp-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;parentRefs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;sectionName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;tcp-listener&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;backendRefs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The corresponding &lt;code&gt;Gateway&lt;/code&gt; listener:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gateway.networking.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;gatewayClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy-gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listeners&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;tcp-listener&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;protocol&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;TCP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;http-listener&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;protocol&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;HTTP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One Gateway, both protocols declared explicitly. The &lt;code&gt;TCPRoute&lt;/code&gt; API is in the experimental channel and requires opting in when installing Envoy Gateway.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="httproute--http-services"&gt;HTTPRoute — HTTP services
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;HTTPRoute&lt;/code&gt; handles HTTP and HTTPS traffic with routing by hostname, path, header, or method — without annotations.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;gateway.networking.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;HTTPRoute&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-http-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;parentRefs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-gateway&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;sectionName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;http-listener&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hostnames&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;my-app.example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;matches&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;path&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;PathPrefix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;backendRefs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="loadbalancer--direct-tcp-via-octavia"&gt;LoadBalancer — direct TCP via Octavia
&lt;/h2&gt;&lt;p&gt;For cases where a &lt;code&gt;TCPRoute&lt;/code&gt; is not appropriate (or the Gateway API experimental channel is not enabled), a &lt;code&gt;LoadBalancer&lt;/code&gt; service provisions an Octavia LB directly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-tcp-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;LoadBalancer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;port&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;targetPort&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;protocol&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;TCP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Annotations control Octavia behaviour — timeouts, health check parameters, internal vs external. These are provider-specific and not standardised across OpenStack deployments.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="storage"&gt;Storage
&lt;/h2&gt;&lt;p&gt;Cinder block volumes are available via the CSI driver. A &lt;code&gt;PersistentVolumeClaim&lt;/code&gt; provisions a Cinder volume automatically using the cluster&amp;rsquo;s default storage class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my-data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;accessModes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20Gi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cinder volumes are &lt;code&gt;ReadWriteOnce&lt;/code&gt; — they attach to a single node. For stateful workloads, use &lt;code&gt;StatefulSet&lt;/code&gt; rather than &lt;code&gt;Deployment&lt;/code&gt; to get stable volume binding across pod restarts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="provisioning-a-shoot-cluster-on-cleura"&gt;Provisioning a shoot cluster on Cleura
&lt;/h2&gt;&lt;p&gt;Cleura wraps Gardener behind their own REST API at &lt;code&gt;rest.cleura.cloud&lt;/code&gt;. The garden cluster kubeconfig is not exposed — &lt;code&gt;gardenctl&lt;/code&gt; does not work directly. Cluster lifecycle is managed through HTTP calls.&lt;/p&gt;
&lt;h3 id="authentication"&gt;Authentication
&lt;/h3&gt;&lt;p&gt;Every call requires a token obtained once per session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s -X POST https://rest.cleura.cloud/auth/v1/tokens &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;auth&amp;#34;: {&amp;#34;login&amp;#34;: &amp;#34;you@example.com&amp;#34;, &amp;#34;password&amp;#34;: &amp;#34;yourpass&amp;#34;}}&amp;#39;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | jq &lt;span style="color:#e6db74"&gt;&amp;#39;{token: .token}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pass &lt;code&gt;X-AUTH-LOGIN&lt;/code&gt; and &lt;code&gt;X-AUTH-TOKEN&lt;/code&gt; headers on all subsequent calls.&lt;/p&gt;
&lt;h3 id="bootstrap-once-per-projectregion"&gt;Bootstrap (once per project/region)
&lt;/h3&gt;&lt;p&gt;Before creating any clusters, the project must be bootstrapped — this wires up the OpenStack credentials that Gardener uses to provision nodes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; https://rest.cleura.cloud/gardener/v1/public/secret/kna1/&lt;span style="color:#f92672"&gt;{&lt;/span&gt;projectId&lt;span style="color:#f92672"&gt;}&lt;/span&gt;/bootstrap &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-LOGIN: ...&amp;#34;&lt;/span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-TOKEN: ...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Safe to call repeatedly; idempotent.&lt;/p&gt;
&lt;h3 id="create-a-shoot-cluster"&gt;Create a shoot cluster
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; https://rest.cleura.cloud/gardener/v1/public/shoot/kna1/&lt;span style="color:#f92672"&gt;{&lt;/span&gt;projectId&lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-LOGIN: ...&amp;#34;&lt;/span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-TOKEN: ...&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;shoot&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;name&amp;#34;: &amp;#34;my-cluster&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;kubernetes&amp;#34;: {&amp;#34;version&amp;#34;: &amp;#34;1.31.0&amp;#34;},
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;provider&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;infrastructureConfig&amp;#34;: {&amp;#34;floatingPoolName&amp;#34;: &amp;#34;ext-net&amp;#34;},
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;workers&amp;#34;: [{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;name&amp;#34;: &amp;#34;default&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;machine&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;type&amp;#34;: &amp;#34;4C-8GB-50GB&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;image&amp;#34;: {&amp;#34;name&amp;#34;: &amp;#34;ubuntu&amp;#34;, &amp;#34;version&amp;#34;: &amp;#34;22.4.20230301&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;minimum&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;maximum&amp;#34;: 3,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;volume&amp;#34;: {&amp;#34;size&amp;#34;: &amp;#34;50Gi&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="poll-until-ready"&gt;Poll until ready
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl https://rest.cleura.cloud/gardener/v1/public/shoot/kna1/&lt;span style="color:#f92672"&gt;{&lt;/span&gt;projectId&lt;span style="color:#f92672"&gt;}&lt;/span&gt;/my-cluster &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-LOGIN: ...&amp;#34;&lt;/span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-TOKEN: ...&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | jq &lt;span style="color:#e6db74"&gt;&amp;#39;.lastOperation | {state, description, progress}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Poll until &lt;code&gt;lastOperation.state == &amp;quot;Succeeded&amp;quot;&lt;/code&gt;. Takes roughly 10–15 minutes on first provision.&lt;/p&gt;
&lt;h3 id="fetch-kubeconfig"&gt;Fetch kubeconfig
&lt;/h3&gt;&lt;p&gt;The Cleura docs reference two kubeconfig paths — &lt;code&gt;GET /kubeconfig&lt;/code&gt; (lowercase) and &lt;code&gt;POST /Kubeconfig&lt;/code&gt; (uppercase, different casing). Neither worked reliably in practice. The endpoint that actually returns a kubeconfig is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s -X POST &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; https://rest.cleura.cloud/gardener/v1/public/shoot/kna1/&lt;span style="color:#f92672"&gt;{&lt;/span&gt;projectId&lt;span style="color:#f92672"&gt;}&lt;/span&gt;/my-cluster/adminkubeconfig &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-LOGIN: ...&amp;#34;&lt;/span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-AUTH-TOKEN: ...&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;config&amp;#34;: {&amp;#34;expirationSeconds&amp;#34;: 3600}}&amp;#39;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | jq -r &amp;gt; my-cluster-kubeconfig.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;expirationSeconds&lt;/code&gt; field controls credential lifetime. A bug report has been filed with Cleura about the endpoint inconsistency — the &lt;code&gt;adminkubeconfig&lt;/code&gt; path is not documented.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Path&lt;/th&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;Documented&lt;/th&gt;
 &lt;th&gt;Works&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/kubeconfig&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;GET&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;unclear&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/Kubeconfig&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;td&gt;unclear&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/adminkubeconfig&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;POST&lt;/td&gt;
 &lt;td&gt;no&lt;/td&gt;
 &lt;td&gt;yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;→ &lt;a class="link" href="https://github.com/cleura/docs/issues/534" target="_blank" rel="noopener"
 &gt;Cleura docs issue #534 — kubeconfig endpoint inconsistencies in Gardener REST API&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="script"&gt;Script
&lt;/h3&gt;&lt;p&gt;A bash script wrapping the full workflow (list, create, wait, kubeconfig, delete) is available: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/scripts/cleura-shoot.sh" &gt;cleura-shoot.sh&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export CLEURA_LOGIN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;you@example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export CLEURA_PASSWORD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;yourpass&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./cleura-shoot.sh list
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./cleura-shoot.sh create my-cluster
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./cleura-shoot.sh wait my-cluster
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./cleura-shoot.sh kubeconfig my-cluster
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./cleura-shoot.sh delete my-cluster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="iac-options"&gt;IaC options
&lt;/h3&gt;&lt;p&gt;No native Terraform provider exists for Cleura&amp;rsquo;s Gardener REST API. The Gardener Terraform provider (&lt;code&gt;registry.terraform.io/providers/gardener/gardener&lt;/code&gt;) requires the garden cluster kubeconfig, which Cleura does not expose. Options:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Approach&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Bash + curl&lt;/td&gt;
 &lt;td&gt;Minimal deps — just &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Crossplane &lt;code&gt;provider-http&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Declarative, Kubernetes-native, reconciliation loop&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Custom Terraform provider&lt;/td&gt;
 &lt;td&gt;Full &lt;code&gt;plan&lt;/code&gt;/&lt;code&gt;apply&lt;/code&gt; semantics — requires Go provider development&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Pulumi custom dynamic provider&lt;/td&gt;
 &lt;td&gt;Python/TypeScript, similar effort to custom Terraform provider&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Rook</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/rook/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/rook/</guid><description>&lt;p&gt;Rook is a Kubernetes operator that deploys and manages storage systems — primarily &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cloud-infrastructure/ceph/" &gt;Ceph&lt;/a&gt; — as native Kubernetes resources. The distinction: Ceph is the storage system; Rook is the Kubernetes wiring around it.&lt;/p&gt;
&lt;p&gt;Without Rook you would run Ceph manually (or via &lt;code&gt;cephadm&lt;/code&gt;) and then configure the Kubernetes CSI driver separately. Rook collapses that into CRDs and handles the full lifecycle: deployment, configuration, expansion, upgrades, and failure recovery.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-it-works"&gt;How it works
&lt;/h2&gt;&lt;p&gt;Rook introduces several CRDs:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CephCluster&lt;/strong&gt; — declares the cluster: which nodes, which disks to use as OSDs, replication settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CephBlockPool&lt;/strong&gt; — defines a Ceph pool (replication factor, failure domain). Maps to an RBD pool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;StorageClass&lt;/strong&gt; — references a CephBlockPool and enables dynamic PVC provisioning. Kubernetes workloads request storage; Rook/Ceph fulfils it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CephFilesystem&lt;/strong&gt; — deploys CephFS + MDS for POSIX shared filesystem access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CephObjectStore&lt;/strong&gt; — deploys the Ceph RGW S3-compatible object storage gateway.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="typical-install-sequence"&gt;Typical install sequence
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/rook/rook/refs/tags/v1.17.9/deploy/examples/crds.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/rook/rook/refs/tags/v1.17.9/deploy/examples/common.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/rook/rook/refs/tags/v1.17.9/deploy/examples/operator.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then apply a &lt;code&gt;CephCluster&lt;/code&gt; manifest declaring your storage topology, followed by &lt;code&gt;CephBlockPool&lt;/code&gt; and &lt;code&gt;StorageClass&lt;/code&gt; for PVC support.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="single-node-considerations"&gt;Single-node considerations
&lt;/h2&gt;&lt;p&gt;A single-node setup requires &lt;code&gt;allowMultiplePerNode: true&lt;/code&gt; in the &lt;code&gt;CephCluster&lt;/code&gt; spec (MONs, MGR, and OSDs all land on the same node). Replication &lt;code&gt;size&lt;/code&gt; must be set to &lt;code&gt;1&lt;/code&gt; — there is nowhere else to replicate. This works for experimentation; it is not a production configuration. See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cloud-infrastructure/ceph/" &gt;Ceph&lt;/a&gt; for details on the replication model.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://rook.io/docs/rook/latest/" target="_blank" rel="noopener"
 &gt;Rook documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cloud-infrastructure/ceph/" &gt;Ceph&lt;/a&gt; — the underlying storage system&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rook-ceph/" &gt;Rook + Ceph in the homelab&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Talos Linux + Omni</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/talos/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/talos/</guid><description>&lt;p&gt;Talos Linux is an immutable, minimal operating system designed specifically for running Kubernetes. There is no shell, no SSH, no package manager. The entire OS is read-only and managed via a gRPC API (&lt;code&gt;talosctl&lt;/code&gt;). Node configuration is declarative YAML applied over the API; changes that require a reboot take effect on the next boot.&lt;/p&gt;
&lt;p&gt;The tradeoff is rigidity for operational simplicity. You cannot log into a Talos node and fix something by hand. In return, nodes are deterministic, reproducible, and there is no configuration drift.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="comparison-to-other-installs"&gt;Comparison to other installs
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Method&lt;/th&gt;
 &lt;th&gt;OS&lt;/th&gt;
 &lt;th&gt;Config&lt;/th&gt;
 &lt;th&gt;Mutable&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;kubeadm&lt;/td&gt;
 &lt;td&gt;Ubuntu / RHEL / etc&lt;/td&gt;
 &lt;td&gt;Manual + scripts&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;k3s&lt;/td&gt;
 &lt;td&gt;Any Linux&lt;/td&gt;
 &lt;td&gt;Minimal&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Talos&lt;/td&gt;
 &lt;td&gt;Talos Linux&lt;/td&gt;
 &lt;td&gt;Declarative API&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;k3s and kubeadm give you more flexibility and a familiar Linux environment. Talos is the right choice when you want the cluster nodes to behave like appliances — provisioned, never touched.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="omni"&gt;Omni
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://omni.siderolabs.com" target="_blank" rel="noopener"
 &gt;Omni&lt;/a&gt; is a cluster management platform by Sidero Labs built on top of Talos. It handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node registration (nodes boot and phone home to the Omni API)&lt;/li&gt;
&lt;li&gt;Cluster creation and machine assignment&lt;/li&gt;
&lt;li&gt;Kubernetes upgrades (one action in the UI)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;talosctl&lt;/code&gt; and &lt;code&gt;kubeconfig&lt;/code&gt; access via the Omni CLI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nodes register via a join token embedded in the kernel command line at PXE boot time. The cluster runs on your hardware; Omni only manages the control plane.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hobby tier&lt;/strong&gt;: 10 nodes, non-commercial use, free. Sidero Labs also offers a self-hosted version.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="image-factory"&gt;Image Factory
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://factory.talos.dev" target="_blank" rel="noopener"
 &gt;factory.talos.dev&lt;/a&gt; generates custom Talos images with hardware extensions included. Notable extensions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;siderolabs/bnx2&lt;/code&gt; — Broadcom NetXtreme II (BCM5708/BCM5709) NIC firmware, required on some enterprise hardware (IBM x3550 M3, HP Gen 6/7 blades)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;siderolabs/intel-ucode&lt;/code&gt; — Intel microcode updates&lt;/li&gt;
&lt;li&gt;&lt;code&gt;siderolabs/nvidia-*&lt;/code&gt; — NVIDIA GPU support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The factory produces both ISO and PXE artifacts (kernel + initramfs). See the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/hardware/hardware-provisioning/ipxe-opnsense/" &gt;OPNSense + iPXE reference&lt;/a&gt; for how to serve these over TFTP.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="supporting-sidero-labs"&gt;Supporting Sidero Labs
&lt;/h2&gt;&lt;p&gt;Talos and Omni are built by &lt;a class="link" href="https://github.com/siderolabs" target="_blank" rel="noopener"
 &gt;Sidero Labs&lt;/a&gt; — good people doing good work. I sponsor them via &lt;a class="link" href="https://github.com/sponsors/siderolabs" target="_blank" rel="noopener"
 &gt;GitHub Sponsors&lt;/a&gt; at the fanboi tier.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="relevant-links"&gt;Relevant links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.talos.dev/latest/" target="_blank" rel="noopener"
 &gt;Talos Linux docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://omni.siderolabs.com/docs" target="_blank" rel="noopener"
 &gt;Omni docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://factory.talos.dev" target="_blank" rel="noopener"
 &gt;Image factory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/siderolabs" target="_blank" rel="noopener"
 &gt;Sidero Labs GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/sponsors/siderolabs" target="_blank" rel="noopener"
 &gt;Sponsor Sidero Labs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>etcd</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/etcd/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/etcd/</guid><description>&lt;p&gt;etcd is the distributed key-value store that backs Kubernetes. Every Kubernetes object — pods, services, deployments, configmaps, secrets — is stored in etcd. The API server is the only component that reads and writes it directly; everything else in the cluster reads from the API server&amp;rsquo;s cache. etcd&amp;rsquo;s reliability is the cluster&amp;rsquo;s reliability: if etcd loses quorum, the Kubernetes control plane stops functioning.&lt;/p&gt;
&lt;h2 id="raft-consensus"&gt;Raft consensus
&lt;/h2&gt;&lt;p&gt;etcd uses the Raft consensus algorithm. The cluster elects a leader; all writes go through the leader, which replicates them to followers before acknowledging the write. The cluster tolerates &lt;code&gt;(n-1)/2&lt;/code&gt; node failures — a three-node cluster survives one failure, a five-node cluster survives two. This is why control plane node counts are always odd. Three nodes is standard for production; five for clusters where control plane availability is critical.&lt;/p&gt;
&lt;h2 id="watches-and-revisions"&gt;Watches and revisions
&lt;/h2&gt;&lt;p&gt;Every write increments a global revision counter. Clients can watch a key or key prefix and receive every change since a given revision. This is how the Kubernetes controller manager and scheduler work — they hold long-lived watch connections and react to changes in specific resource types without polling.&lt;/p&gt;
&lt;h2 id="operations"&gt;Operations
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Snapshot backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl snapshot save /backup/etcd-snapshot.db &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --endpoints&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --cacert&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --cert&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/healthcheck-client.crt &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --key&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/healthcheck-client.key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Restore from snapshot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl snapshot restore /backup/etcd-snapshot.db --data-dir&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/var/lib/etcd-restore
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Check cluster health&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl endpoint health --cluster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Backing up etcd regularly is the most critical operational task for a Kubernetes cluster. The snapshot is the only path to full recovery if cluster state is lost.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://etcd.io/docs/" target="_blank" rel="noopener"
 &gt;etcd documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/" target="_blank" rel="noopener"
 &gt;Kubernetes etcd administration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Istio</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/istio/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/istio/</guid><description>&lt;p&gt;Istio is a service mesh for Kubernetes. It injects a sidecar proxy (Envoy) into every pod, and all traffic between pods flows through these proxies rather than directly between containers. This gives the mesh control over traffic routing, security, and observability without any changes to application code.&lt;/p&gt;
&lt;h2 id="what-it-solves"&gt;What it solves
&lt;/h2&gt;&lt;p&gt;In a large microservice deployment, every service needs to handle retries, timeouts, circuit breaking, mutual TLS, and metrics collection — or skip them and accept the risk. Without a mesh, each team implements this differently, or not at all. Istio moves these concerns out of the application and into the infrastructure layer, where they are configured once and applied uniformly.&lt;/p&gt;
&lt;h2 id="traffic-management"&gt;Traffic management
&lt;/h2&gt;&lt;p&gt;Istio&amp;rsquo;s &lt;code&gt;VirtualService&lt;/code&gt; and &lt;code&gt;DestinationRule&lt;/code&gt; CRDs give fine-grained control over how traffic is routed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;networking.istio.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;VirtualService&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;reviews&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;reviews&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;http&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;headers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;end-user&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;exact&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;test-user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;route&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;destination&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;reviews&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;subset&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;route&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;destination&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;reviews&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;subset&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This routes a specific user to &lt;code&gt;v2&lt;/code&gt; of a service while everyone else gets &lt;code&gt;v1&lt;/code&gt; — canary testing without a load balancer rule or code change.&lt;/p&gt;
&lt;h2 id="mtls"&gt;mTLS
&lt;/h2&gt;&lt;p&gt;Istio issues and rotates certificates for every workload and enforces mutual TLS between services automatically. Services authenticate each other&amp;rsquo;s identity, not just encrypt the connection. A &lt;code&gt;PeerAuthentication&lt;/code&gt; policy can enforce strict mTLS across a namespace, ensuring no plaintext traffic is accepted.&lt;/p&gt;
&lt;h2 id="observability"&gt;Observability
&lt;/h2&gt;&lt;p&gt;Because all traffic flows through Envoy sidecars, Istio generates L7 metrics (request rate, error rate, latency percentiles), distributed traces, and access logs for every service-to-service call — without instrumentation in the services themselves. This integrates with &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/observability/prometheus/" &gt;Prometheus&lt;/a&gt;, &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/observability/grafana/" &gt;Grafana&lt;/a&gt;, and &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/observability/jaeger/" &gt;Jaeger&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="cost"&gt;Cost
&lt;/h2&gt;&lt;p&gt;Istio adds latency (two extra proxy hops per call) and resource overhead (a sidecar per pod). For clusters with tens of services, the operational benefit is clear. For small clusters or teams early in a microservices journey, the complexity may outweigh the gains.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://istio.io/latest/docs/" target="_blank" rel="noopener"
 &gt;Istio documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://istio.io/latest/docs/concepts/" target="_blank" rel="noopener"
 &gt;Istio concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Kubernetes</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/</guid><description>&lt;p&gt;Kubernetes (K8s) is the de facto standard for container orchestration and the second largest open source project after the Linux kernel. It has well and truly reached the plateau of productivity — the ecosystem is mature and it genuinely delivers.&lt;/p&gt;
&lt;p&gt;That said, the honest take: &lt;strong&gt;K8s is ridiculously hard to deploy and manage&lt;/strong&gt; (day 2 operations especially). Docker Swarm is equally ridiculously easy to get started with. For raw scale, Mesos/DC/OS wins — clusters of 80k+ nodes have been documented in the wild, versus K8s master&amp;rsquo;s practical ceiling of around 5k nodes.&lt;/p&gt;
&lt;p&gt;So the real question is whether the ecosystem justifies the complexity for your situation. For most teams doing cloud-native work, it does.&lt;/p&gt;
&lt;h2 id="core-concepts"&gt;Core concepts
&lt;/h2&gt;&lt;p&gt;The main building blocks:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pods&lt;/strong&gt; — smallest deployable unit, wrapping one or more containers that share network and storage.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pods" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/pods.png" width="800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployments&lt;/strong&gt; — declare desired state; K8s handles rolling updates and self-healing.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Deployments" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/deployments.png" width="800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Secrets&lt;/strong&gt; — store sensitive data (passwords, tokens, keys) separately from application config.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Secrets" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/secrets.png" width="800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DaemonSets&lt;/strong&gt; — run a pod on every node. Typical use: log collectors, monitoring agents.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DaemonSets" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/daemonsets.png" width="800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ReplicaSets&lt;/strong&gt; — ensure N copies of a pod are running at any given time.&lt;/p&gt;
&lt;p&gt;&lt;img alt="ReplicaSets" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/replicasets.png" width="800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ingress&lt;/strong&gt; — HTTP/S routing rules at layer 7. Your load balancer config, declarative.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Ingress" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="1920" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/ingress.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/ingress_hu_b0bb8a58e86a1fe2.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/ingress_hu_34af71a3280d9a15.png 1600w, https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/ingress.png 2400w" width="2400"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CronJobs&lt;/strong&gt; — scheduled jobs, K8s-native.&lt;/p&gt;
&lt;p&gt;&lt;img alt="CronJobs" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/cronjobs.png" width="800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom Resource Definitions (CRDs)&lt;/strong&gt; — extend the K8s API with your own resource types. The foundation of most K8s operators.&lt;/p&gt;
&lt;p&gt;&lt;img alt="CRDs" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/crd.png" width="800"&gt;&lt;/p&gt;
&lt;h2 id="architecture"&gt;Architecture
&lt;/h2&gt;&lt;p&gt;How the pieces fit together internally:&lt;/p&gt;
&lt;p&gt;&lt;img alt="K8s internal architecture" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="1280" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/inner.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/inner_hu_ca8f0543462c084c.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/inner.png 1600w" width="1600"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="K8s component overview" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="640" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/zoo.png" width="800"&gt;&lt;/p&gt;
&lt;h2 id="containers-vs-virtual-machines"&gt;Containers vs virtual machines
&lt;/h2&gt;&lt;p&gt;Not an either/or — they solve different problems and are frequently combined.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Separate: containers alongside VMs" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="1280" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/seperate.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/seperate_hu_557182bcbb415ab4.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/seperate.png 1600w" width="1600"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Combined: containers running on top of VMs" class="gallery-image" data-flex-basis="300px" data-flex-grow="125" height="1280" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/combined.png" srcset="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/combined_hu_9f383172dd98181c.png 800w, https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubernetes/combined.png 1600w" width="1600"&gt;&lt;/p&gt;
&lt;h2 id="local-clusters-for-development"&gt;Local clusters for development
&lt;/h2&gt;&lt;p&gt;When you need K8s without a full cluster:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Tool&lt;/th&gt;
 &lt;th&gt;Best for&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://microk8s.io/" target="_blank" rel="noopener"
 &gt;MicroK8s&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Ubuntu, snap-based, batteries included&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://minikube.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Minikube&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;The classic, broad driver support&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://kind.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Kind&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;K8s in Docker, great for CI pipelines&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://k3d.io/" target="_blank" rel="noopener"
 &gt;K3D&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;K3s in Docker, fast startup&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://k3s.io/" target="_blank" rel="noopener"
 &gt;K3S&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Lightweight K8s, edge and IoT use cases&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/" target="_blank" rel="noopener"
 &gt;kubernetes.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://landscape.cncf.io/" target="_blank" rel="noopener"
 &gt;CNCF Landscape&lt;/a&gt; — map of the cloud-native ecosystem&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=PH-2FfFD2PU" target="_blank" rel="noopener"
 &gt;TGI Kubernetes intro (YouTube)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://igy.cx/posts/setup-microk8s-rbac-storage/" target="_blank" rel="noopener"
 &gt;Setting up MicroK8s with RBAC and Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Kubernetes Autoscaling</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/k8s-autoscaling/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/k8s-autoscaling/</guid><description>&lt;p&gt;Kubernetes has built-in autoscaling at two levels: the Horizontal Pod Autoscaler scales the number of pod replicas based on CPU or memory, and the Cluster Autoscaler adds or removes nodes when pods can&amp;rsquo;t be scheduled. KEDA and Karpenter extend these primitives — KEDA pushing workload scaling further, Karpenter replacing the node provisioner entirely.&lt;/p&gt;
&lt;h2 id="keda"&gt;KEDA
&lt;/h2&gt;&lt;p&gt;Kubernetes Event-Driven Autoscaling. KEDA extends the HPA to scale workloads based on external event sources — Kafka consumer lag, queue depth in SQS or RabbitMQ, HTTP request rate, database query results, cron schedules. The built-in HPA only knows about CPU and memory; KEDA adds a long list of scalers for external systems. The important capability it adds is scale-to-zero: a consumer that has no messages to process can scale down to zero pods and scale back up when work arrives. This makes it well-suited for event-driven workloads and batch processing where idle replicas waste resources.&lt;/p&gt;
&lt;h2 id="karpenter"&gt;Karpenter
&lt;/h2&gt;&lt;p&gt;A node provisioner that replaces the Cluster Autoscaler, originally from AWS and now a CNCF project with support for other clouds. Where the Cluster Autoscaler works by adjusting existing Auto Scaling Groups, Karpenter provisions EC2 instances (or equivalent) directly based on the actual resource requirements of pending pods — choosing the right instance type, size, and purchase option (on-demand vs spot) in real time. This makes provisioning significantly faster and more cost-efficient: the cluster gets exactly the nodes the pending workload needs, not the nearest pre-configured node group. Karpenter also handles consolidation — continuously evaluating whether running workloads could be packed onto fewer nodes and replacing over-provisioned nodes accordingly.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://keda.sh/docs/" target="_blank" rel="noopener"
 &gt;KEDA documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://keda.sh/docs/scalers/" target="_blank" rel="noopener"
 &gt;KEDA scalers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://karpenter.sh/docs/" target="_blank" rel="noopener"
 &gt;Karpenter documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://karpenter.sh/docs/concepts/" target="_blank" rel="noopener"
 &gt;Karpenter concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>KubeVirt</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubevirt/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kubevirt/</guid><description>&lt;p&gt;See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/virtualization/" &gt;Virtualization — KVM and KubeVirt&lt;/a&gt; for full coverage of both KVM and KubeVirt.&lt;/p&gt;</description></item><item><title>Kyverno</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kyverno/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kyverno/</guid><description>&lt;p&gt;Kyverno is a policy engine for Kubernetes. It runs as an admission controller and intercepts every resource creation or update, applying rules that validate, mutate, or generate resources. Policies are written as Kubernetes CRDs in YAML — no Rego, no separate language to learn. If you can write a Kubernetes manifest, you can write a Kyverno policy.&lt;/p&gt;
&lt;h2 id="three-rule-types"&gt;Three rule types
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Validate&lt;/strong&gt; — reject resources that don&amp;rsquo;t meet requirements:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kyverno.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;require-labels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;check-team-label&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;any&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kinds&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;Deployment]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;message&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Deployments must have a &amp;#39;team&amp;#39; label.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pattern&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;team&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;?*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Mutate&lt;/strong&gt; — automatically add or modify fields on admission:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;add-default-resources&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;any&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kinds&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;Pod]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mutate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;patchStrategicMerge&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;containers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;(name)&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;+(memory)&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;64Mi&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;+(cpu)&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;250m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Generate&lt;/strong&gt; — create related resources automatically. A common use: generate a &lt;code&gt;NetworkPolicy&lt;/code&gt; every time a new namespace is created.&lt;/p&gt;
&lt;h2 id="enforcement-vs-audit"&gt;Enforcement vs audit
&lt;/h2&gt;&lt;p&gt;Policies run in &lt;code&gt;enforce&lt;/code&gt; mode (block non-compliant resources) or &lt;code&gt;audit&lt;/code&gt; mode (allow but report violations). Audit mode is the right starting point — understand your existing state before enforcing.&lt;/p&gt;
&lt;h2 id="common-policies"&gt;Common policies
&lt;/h2&gt;&lt;p&gt;The &lt;a class="link" href="https://kyverno.io/policies/" target="_blank" rel="noopener"
 &gt;Kyverno policy library&lt;/a&gt; has ready-made policies for common requirements: disallow privileged containers, require image tags to not be &lt;code&gt;latest&lt;/code&gt;, enforce resource limits, restrict hostPath mounts. Most teams start from the library and customise.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://kyverno.io/docs/" target="_blank" rel="noopener"
 &gt;Kyverno documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kyverno.io/policies/" target="_blank" rel="noopener"
 &gt;Kyverno policy library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Local Kubernetes</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/local-kubernetes/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/local-kubernetes/</guid><description>&lt;p&gt;Running Kubernetes locally is useful for development, testing, and CI — a real cluster without the cloud bill. The options differ mainly in weight, startup speed, and whether they target local dev, CI pipelines, or lightweight production use.&lt;/p&gt;
&lt;h2 id="minikube"&gt;MiniKube
&lt;/h2&gt;&lt;p&gt;The original local Kubernetes, maintained by the Kubernetes project itself. Runs a single-node cluster inside a VM (VirtualBox, HyperKit) or a Docker container. The reference implementation — if something works in Kubernetes, it works in MiniKube. Slower to start than the container-based options, heavier on resources, but the most faithful representation of a real cluster. Good for getting started and for testing things that need VM-level isolation.&lt;/p&gt;
&lt;h2 id="kind"&gt;Kind
&lt;/h2&gt;&lt;p&gt;Kubernetes IN Docker — each cluster node runs as a Docker container, no VM required. Fast startup (seconds), low overhead, and multi-node clusters are easy to spin up. The standard choice for running Kubernetes in CI pipelines: create a cluster, run tests, tear it down. The Kubernetes project itself uses Kind for conformance testing. Not designed for running workloads long-term, but excellent for ephemeral test environments.&lt;/p&gt;
&lt;h2 id="k3s"&gt;K3S
&lt;/h2&gt;&lt;p&gt;Lightweight Kubernetes from Rancher (now SUSE), packaged as a single binary under 100MB. It strips out cloud-provider integrations, in-tree storage drivers, and alpha features — the result is a fully conformant Kubernetes that runs on hardware where full K8s won&amp;rsquo;t. Used in production for edge deployments, IoT, and resource-constrained environments. Also a good choice when you want a real persistent cluster locally without the overhead of MiniKube.&lt;/p&gt;
&lt;h2 id="k3d"&gt;K3D
&lt;/h2&gt;&lt;p&gt;K3S running inside Docker containers — the same relationship Kind has to standard Kubernetes. Fast, lightweight, multi-node clusters in Docker. The advantage over Kind is that K3S starts faster and uses less memory per node. Good choice for local dev and CI when you want the lightweight K3S runtime rather than full upstream Kubernetes.&lt;/p&gt;
&lt;h2 id="microk8s"&gt;MicroK8S
&lt;/h2&gt;&lt;p&gt;Canonical&amp;rsquo;s take on local Kubernetes, distributed as a snap package on Ubuntu. Single-command install, add-ons (DNS, storage, ingress, observability) enabled with &lt;code&gt;microk8s enable &amp;lt;addon&amp;gt;&lt;/code&gt;. Opinionated and tightly integrated with the Ubuntu/Canonical ecosystem. The right choice if you&amp;rsquo;re on Ubuntu and want a low-friction local cluster with batteries included — less so outside that ecosystem.&lt;/p&gt;
&lt;h2 id="which-to-use"&gt;Which to use
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Best for&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;MiniKube&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Getting started, testing with VM isolation&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Kind&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;CI pipelines, ephemeral test clusters&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;K3S&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Persistent local cluster, edge/IoT production&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;K3D&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Fast local dev and CI with K3S runtime&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;MicroK8S&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Ubuntu users wanting a managed local cluster&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://minikube.sigs.k8s.io/docs/" target="_blank" rel="noopener"
 &gt;MiniKube documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kind.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Kind documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.k3s.io/" target="_blank" rel="noopener"
 &gt;K3S documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://k3d.io/" target="_blank" rel="noopener"
 &gt;K3D documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://microk8s.io/docs" target="_blank" rel="noopener"
 &gt;MicroK8S documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Managing Secrets in Kubernetes</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/k8s-secrets/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/k8s-secrets/</guid><description>&lt;p&gt;Kubernetes has a built-in &lt;code&gt;Secret&lt;/code&gt; resource, but it is not a secrets management solution — it is base64-encoded storage with no encryption at rest by default and no access audit trail. How you actually manage secrets in a Kubernetes cluster depends on how far you need to go beyond the default.&lt;/p&gt;
&lt;h2 id="native-kubernetes-secrets"&gt;Native Kubernetes Secrets
&lt;/h2&gt;&lt;p&gt;The baseline. A &lt;code&gt;Secret&lt;/code&gt; is a key-value store mounted into pods as environment variables or files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;db-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Opaque&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;data&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;username&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;YWRtaW4= &lt;/span&gt; &lt;span style="color:#75715e"&gt;# base64(&amp;#34;admin&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cGFzc3dvcmQ=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The problems: base64 is encoding, not encryption. Secrets are stored in etcd — enabling etcd encryption at rest is a cluster configuration step that is easy to skip. Secrets are visible to anyone with &lt;code&gt;kubectl get secret&lt;/code&gt; in that namespace. For anything beyond a local dev cluster or a low-sensitivity workload, you need something more.&lt;/p&gt;
&lt;h2 id="sealed-secrets"&gt;Sealed Secrets
&lt;/h2&gt;&lt;p&gt;A Kubernetes controller from Bitnami. &lt;code&gt;SealedSecret&lt;/code&gt; resources contain secrets encrypted with the cluster&amp;rsquo;s public key — only the controller running in that cluster can decrypt them. The encrypted form is safe to commit to Git, which makes GitOps workflows possible without a separate secrets store. Simple to operate, no external dependency. The tradeoff: secrets are tied to a specific cluster&amp;rsquo;s key, cross-cluster sharing requires re-encryption, and there is no centralised audit trail.&lt;/p&gt;
&lt;h2 id="external-secrets-operator"&gt;External Secrets Operator
&lt;/h2&gt;&lt;p&gt;ESO reads secrets from an external store (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, Azure Key Vault, 1Password) and syncs them into native Kubernetes Secrets. Your source of truth stays in the external system; the K8s Secret is a read-only projection of it, refreshed on a configurable interval:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;external-secrets.io/v1beta1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ExternalSecret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;db-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;refreshInterval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;secretStoreRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;aws-secrets-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ClusterSecretStore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;target&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;db-credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;data&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;secretKey&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;remoteRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;prod/db/password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ESO is the right choice when you already have a secrets store and want Kubernetes workloads to consume from it without changing how secrets are managed elsewhere.&lt;/p&gt;
&lt;h2 id="secrets-store-csi-driver"&gt;Secrets Store CSI Driver
&lt;/h2&gt;&lt;p&gt;An alternative to ESO for the same problem: mount secrets from an external store directly as files in a pod, without creating a Kubernetes Secret at all. The secret materialises only in the pod&amp;rsquo;s filesystem, is not stored in etcd, and disappears when the pod terminates. Supported by AWS, Azure, GCP, and Vault providers. Used in combination with a &lt;code&gt;SecretProviderClass&lt;/code&gt; to define what to fetch and where to mount it.&lt;/p&gt;
&lt;h2 id="hashicorp-vault"&gt;HashiCorp Vault
&lt;/h2&gt;&lt;p&gt;A dedicated secrets management platform. Vault stores arbitrary secrets, issues dynamic credentials (database passwords that expire, AWS IAM credentials valid for an hour), manages PKI, and provides a full audit log of every read and write. Kubernetes workloads authenticate to Vault via the Kubernetes auth method (using the pod&amp;rsquo;s service account token) and receive a Vault token scoped to the secrets their service account is allowed to read. More to operate than the other options, but the right answer for organisations that need dynamic credentials, fine-grained access control, and audit logs.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Approach&lt;/th&gt;
 &lt;th&gt;Good for&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Native Secrets&lt;/td&gt;
 &lt;td&gt;Local dev, low-sensitivity workloads&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Sealed Secrets&lt;/td&gt;
 &lt;td&gt;GitOps, single-cluster, no external dependency&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;External Secrets Operator&lt;/td&gt;
 &lt;td&gt;Syncing from existing external stores&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Secrets Store CSI&lt;/td&gt;
 &lt;td&gt;Avoiding etcd entirely, file-based secret injection&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;HashiCorp Vault&lt;/td&gt;
 &lt;td&gt;Dynamic credentials, audit logs, enterprise requirements&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bitnami-labs/sealed-secrets" target="_blank" rel="noopener"
 &gt;Sealed Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://external-secrets.io/" target="_blank" rel="noopener"
 &gt;External Secrets Operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://secrets-store-csi-driver.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Secrets Store CSI Driver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.hashicorp.com/vault/docs/platform/k8s" target="_blank" rel="noopener"
 &gt;HashiCorp Vault on Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>OpenShift Data Foundation</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/odf/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/odf/</guid><description>&lt;p&gt;OpenShift Data Foundation (ODF) is Red Hat&amp;rsquo;s enterprise Kubernetes storage platform, built on &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cloud-infrastructure/ceph/" &gt;Ceph&lt;/a&gt; orchestrated by &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/rook/" &gt;Rook&lt;/a&gt;. Where Rook-Ceph is the open source upstream, ODF packages it with an operator, a validated configuration, enterprise support, and integration with the OpenShift console. It provides block (RBD), file (CephFS), and object (S3-compatible via Ceph RGW) storage as Kubernetes StorageClasses on the same hardware.&lt;/p&gt;
&lt;h2 id="what-it-provides"&gt;What it provides
&lt;/h2&gt;&lt;p&gt;Three storage modes from one cluster:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Mode&lt;/th&gt;
 &lt;th&gt;StorageClass&lt;/th&gt;
 &lt;th&gt;Use case&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Block (RBD)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ocs-storagecluster-ceph-rbd&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Databases, stateful apps needing a single-writer disk&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;File (CephFS)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ocs-storagecluster-cephfs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Shared filesystems, multiple pods reading/writing the same volume&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Object&lt;/td&gt;
 &lt;td&gt;S3-compatible endpoint&lt;/td&gt;
 &lt;td&gt;Buckets via &lt;code&gt;ObjectBucketClaim&lt;/code&gt;, backup targets, artifact storage&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="installation"&gt;Installation
&lt;/h2&gt;&lt;p&gt;ODF installs via the ODF operator from OperatorHub. The operator creates a &lt;code&gt;StorageCluster&lt;/code&gt; CR that drives the Ceph deployment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ocs.openshift.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;StorageCluster&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ocs-storagecluster&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;openshift-storage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storageDeviceSets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ocs-deviceset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;count&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;replica&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;dataPVCTemplate&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storageClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;local-storage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumeMode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Block&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1Ti&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Requires at minimum three nodes with dedicated block devices. The operator handles Ceph cluster formation, monitors, MGRs, and OSDs.&lt;/p&gt;
&lt;h2 id="vs-rook-ceph"&gt;vs Rook-Ceph
&lt;/h2&gt;&lt;p&gt;ODF IS Rook-Ceph under the hood. The difference is packaging and support: ODF is tested and supported on OpenShift, includes the NooBaa multi-cloud gateway for object storage federation, and integrates with the OpenShift UI. For self-managed Kubernetes outside OpenShift, raw Rook-Ceph is the equivalent path.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://access.redhat.com/documentation/en-us/red_hat_openshift_data_foundation/" target="_blank" rel="noopener"
 &gt;ODF documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://rook.io/docs/rook/latest/" target="_blank" rel="noopener"
 &gt;Rook documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Velero</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/velero/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/velero/</guid><description>&lt;p&gt;Velero backs up and restores Kubernetes clusters. It captures both Kubernetes resource definitions (deployments, services, configmaps, secrets, CRDs) and persistent volume data, stores them in object storage (S3, GCS, Azure Blob), and can restore them to the same cluster or a different one. The primary use cases are disaster recovery, cluster migration, and namespace cloning.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it works
&lt;/h2&gt;&lt;p&gt;Velero runs as a controller in the cluster. A &lt;code&gt;Backup&lt;/code&gt; CR triggers a snapshot of selected resources:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;velero.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;daily-backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;velero&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;includedNamespaces&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storageLocation&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ttl&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;720h &lt;/span&gt; &lt;span style="color:#75715e"&gt;# 30 days&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Persistent volume data is handled via storage provider snapshots (CSI snapshots, AWS EBS snapshots) or a file-system-level backup using the node-agent daemonset (formerly Restic). CSI snapshot integration is the preferred modern approach.&lt;/p&gt;
&lt;p&gt;Scheduled backups run via a &lt;code&gt;Schedule&lt;/code&gt; CR:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;velero.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Schedule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;daily&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;velero&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;schedule&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;0 2 * * *&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;template&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;includedNamespaces&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ttl&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;720h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="restore"&gt;Restore
&lt;/h2&gt;&lt;p&gt;Restoring is a &lt;code&gt;Restore&lt;/code&gt; CR pointing at a backup:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;velero restore create --from-backup daily-backup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Velero recreates the Kubernetes objects and restores volume data. Namespaces can be remapped on restore — useful for cloning production to staging.&lt;/p&gt;
&lt;h2 id="cluster-migration"&gt;Cluster migration
&lt;/h2&gt;&lt;p&gt;The standard migration pattern: back up from the source cluster, configure the destination cluster to point at the same object storage bucket, restore. Velero handles the resource recreation; DNS cutover is a separate step.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://velero.io/docs/" target="_blank" rel="noopener"
 &gt;Velero documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://velero.io/docs/main/csi/" target="_blank" rel="noopener"
 &gt;CSI snapshot support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>