<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>mrpbennett.dev Blog</title>
        <link>https://mrpbennett.dev/blog</link>
        <description>mrpbennett.dev Blog</description>
        <lastBuildDate>Wed, 25 Mar 2026 13:31:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[My current AI workflow]]></title>
            <link>https://mrpbennett.dev/blog/2026/03/ai-workflow</link>
            <guid>https://mrpbennett.dev/blog/2026/03/ai-workflow</guid>
            <pubDate>Wed, 25 Mar 2026 13:31:33 GMT</pubDate>
            <description><![CDATA[AI agents are getting better at an absurd pace, and if I am honest, it can feel impossible to keep up. One week there is a new model, the next week there is a new coding tool, and then suddenly everyone has a strong opinion about how you should be working.]]></description>
            <content:encoded><![CDATA[<p>AI agents are getting better at an absurd pace, and if I am honest, it can feel impossible to keep up. One week there is a new model, the next week there is a new coding tool, and then suddenly everyone has a strong opinion about how you should be working.</p>
<p>That said, this is also what makes the space fun. It still feels like one big ongoing experiment, and I am very much learning in public with the rest of it. So rather than pretend I have found the one true setup, I thought it would be more useful to walk through the workflow I am using right now, what each tool does well, and where I think each one fits.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tooling">Tooling<a href="https://mrpbennett.dev/blog/2026/03/ai-workflow#tooling" class="hash-link" aria-label="Direct link to Tooling" title="Direct link to Tooling" translate="no">​</a></h2>
<p>Here are the main tools I have been reaching for lately to get consistently better results.</p>
<p><strong><a href="https://github.com/superpowers-sh/superpowers" target="_blank" rel="noopener noreferrer" class="">superpowers</a></strong></p>
<p>This is a skill and context system for Claude Code that has become the backbone of how I work with agents. The core problem it solves is context rot — the slow degradation in output quality that happens as a conversation gets longer, muddier, and further from the original intent.</p>
<p>What I like about superpowers is that it gives the agent a library of specialised skills to draw on. Rather than relying on a general system prompt and hoping the model figures out the right approach, you invoke a skill that already knows what good looks like for that kind of task. Planning a feature, debugging a tricky issue, doing a thorough code review — there is a skill for each, and they wire in the right structure automatically.</p>
<p>For example, if I want an agent to add a feature, it no longer just starts generating code. It invokes a brainstorming skill first, works out the approach, surfaces assumptions, and only then moves to implementation. That one shift alone gives me noticeably better output than jumping straight into code generation.</p>
<p><strong><a href="https://github.com/colbymchenry/codegraph" target="_blank" rel="noopener noreferrer" class="">codegraph</a></strong></p>
<p>This one surprised me more than anything else I have added to the workflow recently. Codegraph builds a pre-indexed knowledge graph of your codebase — locally, using tree-sitter to parse the ASTs — and exposes it to the agent via MCP. Instead of burning a chunk of context watching the agent grep and read files trying to figure out how things connect, it can just ask the graph directly.</p>
<p>The benchmark numbers are genuinely striking: 58% fewer tool calls, 22% faster, and file reads dropped to near zero. In practice that means the agent spends less time on discovery and more time on the actual problem. It also auto-syncs when the code changes, so it stays accurate without you having to think about it.</p>
<p>The thing I appreciate most is that it is completely local. No sending code to an external service, no setup beyond running <code>codegraph init</code>. It just works.</p>
<p><strong><a href="https://github.com/DietrichGebert/ponytail" target="_blank" rel="noopener noreferrer" class="">ponytail</a></strong></p>
<p>Ponytail is one of those tools that makes you wonder how you tolerated the alternative. It makes your agent think like the laziest senior developer in the room — and I mean that as a genuine compliment.</p>
<p>The idea is a decision ladder the agent runs before writing any code. Does this need to exist at all? Is it already in the codebase? Does the stdlib do it? Can it be one line? Only if none of those rungs hold does it write something new. The result is that you stop getting over-engineered solutions to simple problems, stop accumulating unnecessary abstractions, and stop having agents add layers of scaffolding for use cases that will never arrive.</p>
<p>The motto is "the best code is the code you never wrote", and once you have worked with an agent that actually operates that way, it is hard to go back. It is not about being careless — validation, security, and error handling at real boundaries are never on the chopping block. It is about defaulting to simplicity everywhere else.</p>
<p><strong><a href="https://github.com/msitarzewski/agency-agents" target="_blank" rel="noopener noreferrer" class="">agency-agents</a></strong></p>
<p>As the repo is described:</p>
<blockquote>
<p>A complete AI agency at your fingertips - From frontend wizards to Reddit community ninjas, from whimsy injectors to reality checkers. Each agent is a specialized expert with personality, processes, and proven deliverables.</p>
</blockquote>
<p>It really does live up to that description. I have found some of the agents to be ridiculously good when used for the right job. The <a href="https://github.com/msitarzewski/agency-agents/blob/main/marketing/marketing-content-creator.md" target="_blank" rel="noopener noreferrer" class="">Content Creator</a>, for example, is excellent at turning rough project notes into a polished <code>README.md</code> that actually sounds like it was written by someone who cares.</p>
<p>This is where I think specialised agents shine. A general-purpose coding assistant can do a lot, but a purpose-built agent with a clear role often gives you a much stronger first draft. If I am writing docs, I would rather hand that off to something tuned for documentation. If I am reviewing product copy, a marketing-focused agent often has better instincts than a raw coding model.</p>
<p>A simple example: say I have hacked together a project over a weekend and the code works, but the docs are a mess. Instead of asking my main coding assistant to context-switch into "documentation mode", I can hand the repo summary to a content-focused agent and let it produce a cleaner first pass. It is a small change, but it keeps each tool doing the thing it is best at.</p>
<p><strong><a href="https://worktrunk.dev/" target="_blank" rel="noopener noreferrer" class="">worktrunk</a></strong></p>
<p>Worktrunk has a bunch of quality-of-life features that make working with multiple parallel changes much less painful. If you have ever used git worktrees directly from the CLI, you will know they are powerful but not always especially pleasant. Personally, I found the raw workflow a bit cumbersome.</p>
<p>Worktrunk smooths that out. It makes creating, jumping between, and cleaning up worktrees feel much more ergonomic, which matters a lot once you start using AI agents in parallel.</p>
<p>One really practical use case is when I want to explore multiple ideas at once. I might have one worktree for a feature branch, another for a risky refactor, and another where I let an agent experiment with a different approach entirely. That separation is brilliant because it stops one bad experiment from polluting the rest of the work.</p>
<p>This is probably the biggest shift in my workflow overall: I no longer think of AI as a single assistant living in one terminal window. I think of it more like a team of temporary contributors, and worktrees give each of them their own safe little sandbox.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-it-all-fits-together">How It All Fits Together<a href="https://mrpbennett.dev/blog/2026/03/ai-workflow#how-it-all-fits-together" class="hash-link" aria-label="Direct link to How It All Fits Together" title="Direct link to How It All Fits Together" translate="no">​</a></h2>
<p>The rough flow for me usually looks like this:</p>
<ol>
<li class="">Start with a rough idea or problem I want to solve.</li>
<li class="">Let <code>superpowers</code> guide the agent into the right structure for the task — planning, spec, or review depending on what it is.</li>
<li class="">Let <code>codegraph</code> handle codebase orientation so the agent is not burning context on discovery before it even starts.</li>
<li class="">Trust <code>ponytail</code> to keep the output tight — no unnecessary abstractions, no scaffolding for hypothetical future requirements.</li>
<li class="">Spin up a dedicated agent, or a more specialised one from <code>agency-agents</code>, depending on what the work actually calls for.</li>
<li class="">Do everything in an isolated worktree using <code>worktrunk</code> so I can compare, throw away, or merge changes cleanly.</li>
</ol>
<p>That combination has made AI feel much more useful to me. Not because any one tool is magic, but because the workflow has a bit more structure. The better I get at setting context, isolating work, and keeping output quality high, the better the results tend to be.</p>
<p>I am sure this setup will change again in a few months, because the whole space moves too fast for anything to stay still. But right now this is the workflow that feels the most practical for how I like to work: a bit opinionated, a bit experimental, and very focused on keeping the chaos under control.</p>]]></content:encoded>
            <category>ai</category>
        </item>
        <item>
            <title><![CDATA[My list of my favourite CLI tools]]></title>
            <link>https://mrpbennett.dev/blog/2026/03/clitools</link>
            <guid>https://mrpbennett.dev/blog/2026/03/clitools</guid>
            <pubDate>Sat, 07 Mar 2026 10:03:27 GMT</pubDate>
            <description><![CDATA[Since finding the site terminaltrove I have realised that I have accumulated a lot of CLI tools. Tools that help me in my workflow day to day or tools that are just awesome to use.]]></description>
            <content:encoded><![CDATA[<p>Since finding the site <a href="https://terminaltrove.com/" target="_blank" rel="noopener noreferrer" class="">terminaltrove</a> I have realised that I have accumulated a lot of CLI tools. Tools that help me in my workflow day to day or tools that are just awesome to use.</p>
<p>Below are a list of ones that I find <em>useful</em></p>
<p>[Note]
Any thing missing, let me know. I am always on the look for new CLI / TUI tools.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-must-haves">The Must Haves<a href="https://mrpbennett.dev/blog/2026/03/clitools#the-must-haves" class="hash-link" aria-label="Direct link to The Must Haves" title="Direct link to The Must Haves" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="utilities">Utilities<a href="https://mrpbennett.dev/blog/2026/03/clitools#utilities" class="hash-link" aria-label="Direct link to Utilities" title="Direct link to Utilities" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/junegunn/fzf" target="_blank" rel="noopener noreferrer" class="">fzf</a> - Command-line fuzzy finding written in Go</li>
<li class=""><a href="https://github.com/sharkdp/bat" target="_blank" rel="noopener noreferrer" class="">bat</a> - Clone of cat(1) with syntax highlighting and Git integration</li>
<li class=""><a href="https://github.com/atuinsh/atuin" target="_blank" rel="noopener noreferrer" class="">atuin</a> - Improved shell history for zsh, bash, fish and nushell</li>
<li class=""><a href="https://github.com/ajeetdsouza/zoxide" target="_blank" rel="noopener noreferrer" class="">zoxide</a> - shell extension to navigate your file-system faster</li>
<li class=""><a href="https://github.com/ajeetdsouza/yazi" target="_blank" rel="noopener noreferrer" class="">yazi</a> - Blazing fast terminal file manager written in Rust, based on async I/O</li>
<li class=""><a href="https://github.com/eza-community/eza" target="_blank" rel="noopener noreferrer" class="">eza</a> - Modern, maintained replacement for ls</li>
<li class=""><a href="https://github.com/BurntSushi/ripgrep" target="_blank" rel="noopener noreferrer" class="">ripgrep</a> - Fast search tool</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="git-and-github">Git and GitHub<a href="https://mrpbennett.dev/blog/2026/03/clitools#git-and-github" class="hash-link" aria-label="Direct link to Git and GitHub" title="Direct link to Git and GitHub" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/jesseduffield/lazygit" target="_blank" rel="noopener noreferrer" class="">lazygit</a> - A simple terminal UI for git commands, written in Go</li>
<li class=""><a href="https://github.com/cli/cli" target="_blank" rel="noopener noreferrer" class="">gh</a> - GitHub command-line tool</li>
<li class=""><a href="https://github.com/dlvhdr/gh-dash" target="_blank" rel="noopener noreferrer" class="">gh-dash</a> - A rich terminal UI for GitHub PRs that doesn't break your flow.</li>
<li class=""><a href="https://github.com/modem-dev/hunk" target="_blank" rel="noopener noreferrer" class="">hunk</a> - Review-first terminal diff viewer</li>
<li class=""><a href="https://github.com/max-sixty/worktrunk" target="_blank" rel="noopener noreferrer" class="">worktrunk</a> - Git worktree management, designed for parallel AI agent workflows</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="process-management">Process Management<a href="https://mrpbennett.dev/blog/2026/03/clitools#process-management" class="hash-link" aria-label="Direct link to Process Management" title="Direct link to Process Management" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/zellij-org/zellij" target="_blank" rel="noopener noreferrer" class="">zellij</a> - terminal multiplexer</li>
<li class=""><a href="https://github.com/jesseduffield/lazydocker" target="_blank" rel="noopener noreferrer" class="">lazydocker</a> - Lazier way to manage everything docker</li>
<li class=""><a href="https://github.com/derailed/k9s" target="_blank" rel="noopener noreferrer" class="">k9s</a> - kubernetes CLI to manage your clusters in style!</li>
<li class=""><a href="https://github.com/aristocratos/bpytop" target="_blank" rel="noopener noreferrer" class="">bpytop</a> - Linux/OSX/FreeBSD resource monitor</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-rest">The Rest<a href="https://mrpbennett.dev/blog/2026/03/clitools#the-rest" class="hash-link" aria-label="Direct link to The Rest" title="Direct link to The Rest" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://github.com/mrpbennett/bucky" target="_blank" rel="noopener noreferrer" class="">bucky</a> - My <strong>very own</strong> S3/SFTP/GCP TUI</li>
<li class=""><a href="https://github.com/tw93/Mole" target="_blank" rel="noopener noreferrer" class="">mole</a> - CLI for Deep cleaning and optimizing your Mac.</li>
<li class=""><a href="https://github.com/YS-L/csvlens" target="_blank" rel="noopener noreferrer" class="">csvlens</a> - Command line csv viewer</li>
<li class=""><a href="https://github.com/carapace-sh/carapace-bin" target="_blank" rel="noopener noreferrer" class="">carapace</a> - CLI tool for multi-shell completion.</li>
<li class=""><a href="https://github.com/darrenburns/posting" target="_blank" rel="noopener noreferrer" class="">posting</a> - A modern API client that lives in your terminal.</li>
<li class=""><a href="https://github.com/ynqa/jnv" target="_blank" rel="noopener noreferrer" class="">jnv</a> - Interactive JSON filter using jq</li>
</ul>
<p>Please do let me know if I have missed anything, you can find me on X at <a href="https://x.com/mrpbennett" target="_blank" rel="noopener noreferrer" class="">@mrpbennett</a></p>]]></content:encoded>
            <category>terminal</category>
        </item>
        <item>
            <title><![CDATA[Intercepting iOS App Traffic with mitmproxy CLI & Web UI Guide]]></title>
            <link>https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide</link>
            <guid>https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[Intercepting HTTP(S) traffic from a real iOS device is a powerful way to understand what your app (or any third‑party app) is sending and receiving—without Xcode, without re‑signing, and without touching a simulator. This guide provides a streamlined setup process and covers both the command‑line interface (CLI) and the Web UI (mitmweb) for capturing, filtering, and modifying your mobile traffic.]]></description>
            <content:encoded><![CDATA[<p>Intercepting HTTP(S) traffic from a real iOS device is a powerful way to understand what your app (or any third‑party app) is sending and receiving—without Xcode, without re‑signing, and without touching a simulator. This guide provides a streamlined setup process and covers both the <strong>command‑line interface (CLI)</strong> and the <strong>Web UI</strong> (<code>mitmweb</code>) for capturing, filtering, and modifying your mobile traffic.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-prerequisites">📋 Prerequisites<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-prerequisites" class="hash-link" aria-label="Direct link to 📋 Prerequisites" title="Direct link to 📋 Prerequisites" translate="no">​</a></h2>
<ul>
<li class="">A Mac (or Linux) machine on the same Wi‑Fi network as your iOS device</li>
<li class="">Homebrew (macOS) or Python 3 &amp; <code>pip3</code></li>
<li class="">Your iPhone and Mac must be on the <strong>same network</strong></li>
<li class="">Basic familiarity with terminal commands and iOS Settings</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1️⃣-install-mitmproxy-on-macos">1️⃣ Install mitmproxy on macOS<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#1%EF%B8%8F%E2%83%A3-install-mitmproxy-on-macos" class="hash-link" aria-label="Direct link to 1️⃣ Install mitmproxy on macOS" title="Direct link to 1️⃣ Install mitmproxy on macOS" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="homebrew-macos">Homebrew (macOS)<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#homebrew-macos" class="hash-link" aria-label="Direct link to Homebrew (macOS)" title="Direct link to Homebrew (macOS)" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">brew install mitmproxy</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pip3-crossplatform">pip3 (cross‑platform)<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#pip3-crossplatform" class="hash-link" aria-label="Direct link to pip3 (cross‑platform)" title="Direct link to pip3 (cross‑platform)" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip3 install mitmproxy</span><br></span></code></pre></div></div>
<p>This installs three binaries:</p>
<ul>
<li class=""><code>mitmproxy</code> – interactive CLI (text UI)</li>
<li class=""><code>mitmweb</code> – browser‑based Web UI</li>
<li class=""><code>mitmdump</code> – headless, scriptable</li>
</ul>
<blockquote>
<p><strong>Tip:</strong> Always keep mitmproxy updated (<code>brew upgrade mitmproxy</code> or <code>pip3 install --upgrade mitmproxy</code>).</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2️⃣-start-mitmproxy">2️⃣ Start mitmproxy<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#2%EF%B8%8F%E2%83%A3-start-mitmproxy" class="hash-link" aria-label="Direct link to 2️⃣ Start mitmproxy" title="Direct link to 2️⃣ Start mitmproxy" translate="no">​</a></h2>
<p>For CLI interface:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy</span><br></span></code></pre></div></div>
<p>For Web UI (recommended for beginners):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmweb</span><br></span></code></pre></div></div>
<p>By default, both listen on:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">0.0.0.0:8080</span><br></span></code></pre></div></div>
<p>Leave this running. The Web UI will be available at <code>http://127.0.0.1:8081</code>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3️⃣-find-your-macs-local-ip">3️⃣ Find Your Mac's Local IP<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#3%EF%B8%8F%E2%83%A3-find-your-macs-local-ip" class="hash-link" aria-label="Direct link to 3️⃣ Find Your Mac's Local IP" title="Direct link to 3️⃣ Find Your Mac's Local IP" translate="no">​</a></h2>
<p>On macOS:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ipconfig getifaddr en0</span><br></span></code></pre></div></div>
<p>Or check <strong>System Settings → Network</strong>.</p>
<p>Example:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">192.168.1.25</span><br></span></code></pre></div></div>
<p>You'll need this IP address for the next step.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4️⃣-configure-iphone-proxy">4️⃣ Configure iPhone Proxy<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#4%EF%B8%8F%E2%83%A3-configure-iphone-proxy" class="hash-link" aria-label="Direct link to 4️⃣ Configure iPhone Proxy" title="Direct link to 4️⃣ Configure iPhone Proxy" translate="no">​</a></h2>
<p>On your iPhone:</p>
<ol>
<li class=""><strong>Settings → Wi‑Fi</strong></li>
<li class="">Tap the <strong>(ℹ︎)</strong> next to your connected network</li>
<li class="">Scroll down → <strong>Configure Proxy</strong></li>
<li class="">Select <strong>Manual</strong></li>
<li class="">Enter:<!-- -->
<ul>
<li class=""><strong>Server</strong>: <code>192.168.1.25</code> (your Mac's IP)</li>
<li class=""><strong>Port</strong>: <code>8080</code></li>
</ul>
</li>
<li class=""><strong>Save</strong></li>
</ol>
<p>Now all HTTP/S traffic flows through mitmproxy. You should immediately see traffic appear in the terminal or Web UI.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5️⃣-install-mitmproxy-certificate-on-iphone-required-for-https">5️⃣ Install mitmproxy Certificate on iPhone (Required for HTTPS)<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#5%EF%B8%8F%E2%83%A3-install-mitmproxy-certificate-on-iphone-required-for-https" class="hash-link" aria-label="Direct link to 5️⃣ Install mitmproxy Certificate on iPhone (Required for HTTPS)" title="Direct link to 5️⃣ Install mitmproxy Certificate on iPhone (Required for HTTPS)" translate="no">​</a></h2>
<p>Without this certificate, HTTPS traffic will fail with security errors.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-easy-way-recommended">The Easy Way (Recommended)<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#the-easy-way-recommended" class="hash-link" aria-label="Direct link to The Easy Way (Recommended)" title="Direct link to The Easy Way (Recommended)" translate="no">​</a></h3>
<p>On your iPhone:</p>
<ol>
<li class="">
<p>Open <strong>Safari</strong></p>
</li>
<li class="">
<p>Navigate to:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">http://mitm.it</span><br></span></code></pre></div></div>
</li>
<li class="">
<p>Tap <strong>iOS</strong> (or the Apple icon)</p>
</li>
<li class="">
<p>Tap <strong>Allow</strong> to download the profile</p>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="install--trust-the-certificate">Install &amp; Trust the Certificate<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#install--trust-the-certificate" class="hash-link" aria-label="Direct link to Install &amp; Trust the Certificate" title="Direct link to Install &amp; Trust the Certificate" translate="no">​</a></h3>
<ol>
<li class="">
<p><strong>Settings → General → VPN &amp; Device Management</strong></p>
</li>
<li class="">
<p>Tap the <strong>mitmproxy</strong> profile</p>
</li>
<li class="">
<p>Tap <strong>Install</strong> (enter passcode if prompted)</p>
</li>
<li class="">
<p>After installing, go to:</p>
<p><strong>Settings → General → About → Certificate Trust Settings</strong></p>
</li>
<li class="">
<p>Toggle <strong>ON</strong> for the mitmproxy certificate</p>
</li>
</ol>
<p>Now HTTPS traffic will decrypt properly in mitmproxy.</p>
<blockquote>
<p><strong>Pro tip:</strong> If you later rotate mitmproxy's CA, remove the old profile on iOS first to prevent conflicts. After an OS update, you may need to re-enable certificate trust.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-using-the-cli-mitmproxy">🖥️ Using the CLI: <code>mitmproxy</code><a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#%EF%B8%8F-using-the-cli-mitmproxy" class="hash-link" aria-label="Direct link to ️-using-the-cli-mitmproxy" title="Direct link to ️-using-the-cli-mitmproxy" translate="no">​</a></h2>
<p>For quick inspection or scripting, the CLI TUI is lightning fast.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="basic-navigation">Basic Navigation<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#basic-navigation" class="hash-link" aria-label="Direct link to Basic Navigation" title="Direct link to Basic Navigation" translate="no">​</a></h3>
<ul>
<li class=""><strong>↑/↓</strong> – scroll through flows</li>
<li class=""><strong>Enter</strong> – open flow detail (headers &amp; body)</li>
<li class=""><strong>e</strong> – set an <strong>edit</strong> breakpoint on this flow</li>
<li class=""><strong>f</strong> – filter (type filter expression)</li>
<li class=""><strong>r</strong> – replay request</li>
<li class=""><strong>q</strong> – quit</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="filtering-examples">Filtering Examples<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#filtering-examples" class="hash-link" aria-label="Direct link to Filtering Examples" title="Direct link to Filtering Examples" translate="no">​</a></h3>
<p>Show only specific endpoints:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">~u containerBeacon</span><br></span></code></pre></div></div>
<p>Filter by domain:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">~u id5-sync.com</span><br></span></code></pre></div></div>
<p>Combine multiple filters:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">(~u containerBeacon) | (~u id5-sync.com)</span><br></span></code></pre></div></div>
<p>Filter by method:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">~m POST</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="intercept--modify">Intercept &amp; Modify<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#intercept--modify" class="hash-link" aria-label="Direct link to Intercept &amp; Modify" title="Direct link to Intercept &amp; Modify" translate="no">​</a></h3>
<ol>
<li class="">Press <strong>f</strong> to set a filter</li>
<li class="">Press <strong>i</strong> to set an intercept (e.g., <code>~u api.myapp.com</code>)</li>
<li class="">Reproduce the request on your device</li>
<li class="">The flow will pause in mitmproxy</li>
<li class="">Press <strong>Enter</strong> to view it, <strong>e</strong> to edit</li>
<li class="">Press <strong>a</strong> to accept &amp; continue or <strong>d</strong> to drop</li>
</ol>
<blockquote>
<p><strong>CLI Tip:</strong> Pipe mitmproxy logs to a file for offline debugging:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy 2&gt;&amp;1 | tee mitmproxy.log</span><br></span></code></pre></div></div>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-using-the-web-ui-mitmweb">🌐 Using the Web UI: <code>mitmweb</code><a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-using-the-web-ui-mitmweb" class="hash-link" aria-label="Direct link to -using-the-web-ui-mitmweb" title="Direct link to -using-the-web-ui-mitmweb" translate="no">​</a></h2>
<p>The Web UI offers a user‑friendly dashboard in your browser.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="launch">Launch<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#launch" class="hash-link" aria-label="Direct link to Launch" title="Direct link to Launch" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmweb</span><br></span></code></pre></div></div>
<ul>
<li class=""><strong>Proxy</strong> runs on <code>0.0.0.0:8080</code></li>
<li class=""><strong>Web UI</strong> at <code>http://127.0.0.1:8081/</code></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-features">Key Features<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#key-features" class="hash-link" aria-label="Direct link to Key Features" title="Direct link to Key Features" translate="no">​</a></h3>
<ol>
<li class=""><strong>Live Flows List</strong> – real‑time streaming of requests/responses</li>
<li class=""><strong>Filter Box</strong> – same filter syntax as CLI (<code>~u</code>, <code>~h</code>, <code>~q</code>, <code>~m</code>, etc.)</li>
<li class=""><strong>Flow Detail Pane</strong> – click any request to see Request / Response / Timeline</li>
<li class=""><strong>Intercept Toggle</strong> – checkbox to pause flows for inspection</li>
<li class=""><strong>Copy as cURL</strong> – right‑click any flow for easy replay</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="filter-examples">Filter Examples<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#filter-examples" class="hash-link" aria-label="Direct link to Filter Examples" title="Direct link to Filter Examples" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">~u containerBeacon | ~u id5-sync.com | ~u api.myapp.com</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="exporting--auditing">Exporting &amp; Auditing<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#exporting--auditing" class="hash-link" aria-label="Direct link to Exporting &amp; Auditing" title="Direct link to Exporting &amp; Auditing" translate="no">​</a></h3>
<ul>
<li class=""><strong>File → Save</strong> → exports flows in various formats</li>
<li class=""><strong>Export HTTP Archive (HAR)</strong> → choose "Filtered flows" → save <code>.har</code> for team reviews or browser replay</li>
<li class="">Share HARs or screenshots of the Web UI for clear documentation</li>
</ul>
<blockquote>
<p><strong>Web UI Tip:</strong> Use your browser's DevTools to customize the Web UI appearance with larger fonts or dark mode via user CSS extensions like "Stylus".</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-scripting-with-addons">🔄 Scripting with Add‑ons<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-scripting-with-addons" class="hash-link" aria-label="Direct link to 🔄 Scripting with Add‑ons" title="Direct link to 🔄 Scripting with Add‑ons" translate="no">​</a></h2>
<p>Want to automate logging or custom breakpoints? Write a small Python addon.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># log_beacons.py</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> mitmproxy </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">request</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">HTTPFlow</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    url </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pretty_url</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"containerBeacon"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> url </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"id5-sync.com"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"[MITM] </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">flow</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">request</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">method</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> → </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">url</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Launch with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy -s log_beacons.py</span><br></span></code></pre></div></div>
<p>Or with the Web UI:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmweb -s log_beacons.py</span><br></span></code></pre></div></div>
<p>Logs appear in your terminal or Web UI <strong>Event Log</strong> panel.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-certificate-pinning">⚠️ Certificate Pinning<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#%EF%B8%8F-certificate-pinning" class="hash-link" aria-label="Direct link to ⚠️ Certificate Pinning" title="Direct link to ⚠️ Certificate Pinning" translate="no">​</a></h2>
<p>Many production apps implement <strong>certificate pinning</strong> to prevent man‑in‑the‑middle attacks.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="symptoms">Symptoms<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#symptoms" class="hash-link" aria-label="Direct link to Symptoms" title="Direct link to Symptoms" translate="no">​</a></h3>
<ul>
<li class="">Requests fail or time out</li>
<li class="">TLS handshake errors in mitmproxy</li>
<li class="">No traffic appears for that specific app</li>
<li class="">App shows network error messages</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="solutions">Solutions<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#solutions" class="hash-link" aria-label="Direct link to Solutions" title="Direct link to Solutions" translate="no">​</a></h3>
<p><strong>Option A — Use iOS Simulator</strong></p>
<p>The simulator is MUCH easier for development:</p>
<ul>
<li class="">Install mitmproxy certificate directly into macOS system trust</li>
<li class="">Simulator inherits macOS trust settings</li>
<li class="">No extra profile installation steps</li>
<li class="">No certificate pinning issues for apps in development</li>
</ul>
<p><strong>Option B — Use Frida to Bypass Pinning</strong></p>
<p>Common for security research on real devices:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Install Frida tools</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pip3 install frida-tools</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Use SSL pinning bypass scripts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">frida -U -f com.yourapp.bundle -l ssl-pinning-bypass.js</span><br></span></code></pre></div></div>
<p><strong>Option C — Jailbroken Device</strong></p>
<p>You can disable SSL pinning via tweaks like SSL Kill Switch.</p>
<p><strong>Option D — Test Your Own App</strong></p>
<p>If you control the app source code, disable pinning in debug builds or implement a debug flag.</p>
<blockquote>
<p><strong>Note:</strong> For apps you own, the simulator is the cleanest and most reliable route for development and testing.</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-debugging-tips">🔍 Debugging Tips<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-debugging-tips" class="hash-link" aria-label="Direct link to 🔍 Debugging Tips" title="Direct link to 🔍 Debugging Tips" translate="no">​</a></h2>
<p>If traffic isn't showing up:</p>
<ul>
<li class="">
<p><strong>Firewall</strong>: Ensure Mac firewall allows incoming connections on port 8080</p>
</li>
<li class="">
<p><strong>Timing</strong>: Make sure mitmproxy is running <strong>before</strong> enabling the proxy on iPhone</p>
</li>
<li class="">
<p><strong>IP Address</strong>: Verify your Mac's IP didn't change (DHCP can reassign IPs)</p>
</li>
<li class="">
<p><strong>Network</strong>: Confirm iPhone and Mac are on the same subnet</p>
</li>
<li class="">
<p><strong>VPN</strong>: Disable any VPN on your iPhone</p>
</li>
<li class="">
<p><strong>Port conflicts</strong>: If you get <code>address already in use</code>, find and kill the process:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">lsof -iTCP:8080 | grep LISTEN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kill -9 &lt;PID&gt;</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Certificate trust</strong>: Double-check that certificate trust is enabled in iOS settings</p>
</li>
<li class="">
<p><strong>Restart</strong>: Try restarting mitmproxy or toggling the proxy off/on in iOS settings</p>
</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-advanced-tips">🚀 Advanced Tips<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-advanced-tips" class="hash-link" aria-label="Direct link to 🚀 Advanced Tips" title="Direct link to 🚀 Advanced Tips" translate="no">​</a></h2>
<ul>
<li class="">
<p><strong>Custom ports</strong>: Use <code>--listen-port</code> to change the proxy port:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy --listen-port 8082</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Selective bypass</strong>: Exclude domains from proxying:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy --ignore-hosts '^(?!.*myapp\.com)'</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Save flows automatically</strong>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmdump -w flows.mitm</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Replay saved flows</strong>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy -r flows.mitm</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Transparent mode</strong>: Intercept traffic without proxy configuration (requires routing setup):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mitmproxy --mode transparent</span><br></span></code></pre></div></div>
</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-legal--ethical-note">🔐 Legal &amp; Ethical Note<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-legal--ethical-note" class="hash-link" aria-label="Direct link to 🔐 Legal &amp; Ethical Note" title="Direct link to 🔐 Legal &amp; Ethical Note" translate="no">​</a></h2>
<p>Only intercept traffic:</p>
<ul>
<li class="">For apps you own or have permission to test</li>
<li class="">In testing/development environments</li>
<li class="">For authorized security research</li>
<li class="">For educational purposes on your own devices</li>
</ul>
<p>Intercepting traffic without proper authorization can violate laws, terms of service, and ethical guidelines. Always ensure you have the right to inspect the traffic you're capturing.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-conclusion">🎯 Conclusion<a href="https://mrpbennett.dev/blog/intercepting-ios-app-traffic-with-mitmproxy-cli-and-web-ui-guide#-conclusion" class="hash-link" aria-label="Direct link to 🎯 Conclusion" title="Direct link to 🎯 Conclusion" translate="no">​</a></h2>
<p>With <strong>mitmproxy</strong> (CLI or Web UI), you gain complete visibility into real‑device HTTPS traffic—no Xcode entanglements required. This guide equipped you to:</p>
<ul>
<li class="">Install and start mitmproxy with minimal configuration</li>
<li class="">Configure your iOS device proxy quickly</li>
<li class="">Install the CA certificate the easy way via <code>mitm.it</code></li>
<li class="">Capture, filter, and inspect flows via CLI or Web UI</li>
<li class="">Intercept, modify, and log requests on the fly</li>
<li class="">Handle certificate pinning scenarios</li>
<li class="">Export HAR files for audit and collaboration</li>
<li class="">Script custom automation with Python add‑ons</li>
</ul>
<p>Whether you're debugging your own app, auditing network behavior, or learning about mobile security, mitmproxy is an invaluable tool in your toolkit.</p>
<p>Happy sniffing, and may your network requests always behave as expected!</p>]]></content:encoded>
            <category>terminal</category>
        </item>
        <item>
            <title><![CDATA[Switching themes automatically in Neovim]]></title>
            <link>https://mrpbennett.dev/blog/switching-themes-in-neovim</link>
            <guid>https://mrpbennett.dev/blog/switching-themes-in-neovim</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[We're going to be using Catppuccin and this setup is generally for MacOS but this idea will work on Linux too. On macOS, you can run this shell command in Lua to get the current system appearance:]]></description>
            <content:encoded><![CDATA[<p>We're going to be using <a href="https://github.com/catppuccin/nvim" target="_blank" rel="noopener noreferrer" class="">Catppuccin</a> and this setup is generally for MacOS but this idea will work on Linux too. On <strong>macOS</strong>, you can run this shell command in Lua to get the current system appearance:</p>
<p><img decoding="async" loading="lazy" src="https://images.ctfassets.net/53u3hzg2egeu/63LY79GFopUFfYGL5Gj74C/c28f4b8a733eaad3e8a8402d723c3a21/213472445-091e54fb-091f-4448-a631-fa6b2ba7d8a5.png" alt="image" class="img_ev3q"></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">defaults read -g AppleInterfaceStyle 2&gt;/dev/null</span><br></span></code></pre></div></div>
<p>This will output:</p>
<ul>
<li class=""><code>"Dark"</code> if dark mode is enabled.</li>
<li class="">Nothing if in light mode.</li>
</ul>
<p>Now we can adjust our <code>colorscheme.lua</code> config by adding the following before we return the color scheme.</p>
<div class="language-lua codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lua codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">-- Detect system appearance (macOS only)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local handle = io.popen("defaults read -g AppleInterfaceStyle 2&gt;/dev/null")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local result = handle:read("*a")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">handle:close()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local is_dark = result:match("Dark") ~= nil</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- Decide flavour and background based on appearance</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local flavour = is_dark and "macchiato" or "latte"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local background = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  light = "latte",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  dark = "mocha",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>To complete your <code>colorscheme</code> config it should look like the below.</p>
<div class="language-lua codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lua codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">-- Detect system appearance (macOS only)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local handle = io.popen("defaults read -g AppleInterfaceStyle 2&gt;/dev/null")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local result = handle:read("*a")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">handle:close()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local is_dark = result:match("Dark") ~= nil</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- Decide flavour and background based on appearance</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local flavour = is_dark and "macchiato" or "latte"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local background = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  light = "latte",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  dark = "mocha",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">return {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "catppuccin/nvim",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name = "catppuccin",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    lazy = false,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    opts = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      flavour = flavour, -- use detected flavour</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      background = background,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      transparent_background = false,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      no_italic = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      no_bold = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      no_underline = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      term_colors = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      integrations = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        cmp = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        gitsigns = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        neotree = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        treesitter = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        notify = false,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        mini = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          enabled = true,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          indentscope_color = "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "LazyVim/LazyVim",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    opts = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      colorscheme = "catppuccin",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>This is for the <a href="https://github.com/catppuccin/nvim" target="_blank" rel="noopener noreferrer" class="">Catppuccin</a> theme. Which I have every where, you're able to amend the above Catppuccin options by following <a href="https://github.com/catppuccin/nvim?tab=readme-ov-file#configuration" target="_blank" rel="noopener noreferrer" class="">this guide</a></p>]]></content:encoded>
            <category>neovim</category>
        </item>
        <item>
            <title><![CDATA[Connecting to Forticlient in Omarchy with openfortivpn]]></title>
            <link>https://mrpbennett.dev/blog/connecting-to-a-vpn-in-omarchy</link>
            <guid>https://mrpbennett.dev/blog/connecting-to-a-vpn-in-omarchy</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I have found a love for window tile management whether that be Aerospace for MacOs or Hyprland for Arch. They suit my workflow especially on a large 32" screen, I have been playing with Omarchy An opinionated Arch + Hyprland Setup by DHH]]></description>
            <content:encoded><![CDATA[<p>I have found a love for window tile management whether that be <a href="https://nikitabobko.github.io/AeroSpace/guide" target="_blank" rel="noopener noreferrer" class="">Aerospace</a> for MacOs or <a href="https://hypr.land/" target="_blank" rel="noopener noreferrer" class="">Hyprland</a> for Arch. They suit my workflow especially on a large 32" screen, I have been playing with <a href="https://omarchy.org/" target="_blank" rel="noopener noreferrer" class="">Omarchy</a> An opinionated Arch + Hyprland Setup by <a href="https://dhh.dk/" target="_blank" rel="noopener noreferrer" class="">DHH</a></p>
<p>It's still being fleshed out but so far I think DHH and the Omarchy community are doing awesome work. However, for myself to move away from M1 Macbook Pro to Omarchy for work, I needed to use a VPN. Forticlient only has <code>.rep</code> and <code>.deb</code> packages, meaning Arch needed something else.</p>
<p>After some googling, some ChatGPT and being pointed in the right direction from the Omarchy community I came across <a href="https://archlinux.org/packages/extra/x86_64/openfortivpn/" target="_blank" rel="noopener noreferrer" class="">openfortivpn</a> this is how I set things up for myself.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="install-openfortivpn">Install openfortivpn<a href="https://mrpbennett.dev/blog/connecting-to-a-vpn-in-omarchy#install-openfortivpn" class="hash-link" aria-label="Direct link to Install openfortivpn" title="Direct link to Install openfortivpn" translate="no">​</a></h3>
<p>Arch provides <code>openfortivpn</code> in the official repo:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">sudo pacman -S openfortivpn</span><br></span></code></pre></div></div>
<p>Or use the latest dev version from the AUR:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">yay -S openfortivpn-git</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-your-config-file">Setting up your config file<a href="https://mrpbennett.dev/blog/connecting-to-a-vpn-in-omarchy#setting-up-your-config-file" class="hash-link" aria-label="Direct link to Setting up your config file" title="Direct link to Setting up your config file" translate="no">​</a></h3>
<p>Once installed you will need to set up your config file this is generally located at <code>/etc/openfortivpn/config</code></p>
<div class="language-txt codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-txt codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">host = vpn.example.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">port = 443</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">username = john.doe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">password = mySecretPassword   # optional, omit for interactive prompt</span><br></span></code></pre></div></div>
<p>Once you have that, make sure only root can read it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">sudo chmod 600 /etc/openfortivpn/config</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="running-the-vpn">Running the VPN<a href="https://mrpbennett.dev/blog/connecting-to-a-vpn-in-omarchy#running-the-vpn" class="hash-link" aria-label="Direct link to Running the VPN" title="Direct link to Running the VPN" translate="no">​</a></h3>
<p>Once the above steps are done you can do the following:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">sudo openfortivpn</span><br></span></code></pre></div></div>
<p>In my case this then prompted me to accept a token on my phone before allowing me to connect. Once connected the connection will continue to run inside the termainal window. If you were to close the window your connection will be dropped.</p>
<p>To prevent this, so I can have my terminal window back I set up the following alias.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">alias vpnc="nohup sudo openfortivpn -c /etc/openfortivpn/config &amp;"</span><br></span></code></pre></div></div>
<hr>
<p>There we have it...what seemed daunting was actually pretty simple, just needed a few config files.</p>]]></content:encoded>
            <category>omarchy</category>
            <category>linux</category>
        </item>
        <item>
            <title><![CDATA[Attempting to Master Vim Motions]]></title>
            <link>https://mrpbennett.dev/blog/attempt-to-master-vim-motions</link>
            <guid>https://mrpbennett.dev/blog/attempt-to-master-vim-motions</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I am not a professional developer, but I am a hobbyist one, who knows what the future holds. As a Lead Solution Engineer, I do get to tinker with code in my day-to-day. But it's not my main role, more of a side quest. However, outside of my role I do love to tinker with code and homelabbing. Before I became a Solution Engineer, I spent some time in IT, which meant a lot of time in the terminal.]]></description>
            <content:encoded><![CDATA[<p>I am not a professional developer, but I am a hobbyist one, who knows what the future holds. As a Lead Solution Engineer, I do get to tinker with code in my day-to-day. But it's not my main role, more of a side quest. However, outside of my role I do love to tinker with code and homelabbing. Before I became a Solution Engineer, I spent some time in IT, which meant a lot of time in the terminal.</p>
<p>I think looking back on it, this sparked a love for terminal use and cli tools. Although I have spent most of my development work in an IDE (VSC / DataGrip / PyCharm). I have started to slowly transition from those to NeoVim. Before I got started with NVIM, I spent some time in actual Vim and could never exit. Looks like <a href="https://stackoverflow.com/questions/11828270/how-do-i-exit-vim" target="_blank" rel="noopener noreferrer" class="">I was not the only one</a> with StackOverflow having a post that was viewed over 3.2 million times. The infamous <code>:q</code> was only the beginning.</p>
<p>The paragraphs below explain or give some tidbits of info on how I felt like I could use Vim Motions on a day-to-day basis.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="keybindings-i-found-helpful">Keybindings I found helpful<a href="https://mrpbennett.dev/blog/attempt-to-master-vim-motions#keybindings-i-found-helpful" class="hash-link" aria-label="Direct link to Keybindings I found helpful" title="Direct link to Keybindings I found helpful" translate="no">​</a></h2>
<ul>
<li class=""><code>h, j, k, l</code>: These will help you move left, down, up, right</li>
<li class=""><code>a</code>: inserts character in insert mode beginning of line</li>
<li class=""><code>A</code>: enter insert at the end of a line.</li>
<li class=""><code>i</code>: insert character in insert mode after character</li>
<li class=""><code>I</code>: enter insert at the beginning of a line</li>
<li class=""><code>e, E</code>: jump forwards to the end of a word using uppercase E will include punctuation</li>
<li class=""><code>w, W</code>: jump forwards to the start of a word using uppercase W will include punctuation</li>
<li class=""><code>b, B</code>: jump backwards to the start of a word using uppercase B will include punctuation</li>
<li class=""><code>o</code>: start a new line in insert mode below current line</li>
<li class=""><code>O</code>: start a new line in insert mode above the current line</li>
<li class=""><code>r</code>: replace a single character</li>
<li class=""><code>gg</code>: jump to the top of the page</li>
<li class=""><code>G</code>: jump to the bottom of the page</li>
<li class=""><code>ctrl-d</code>: jump down the page</li>
<li class=""><code>ctrl-u</code>: jump up the page</li>
</ul>
<p>In <code>COMMAND</code> mode I found <code>%s/&lt;find-word&gt;/&lt;replace-word&gt;/g</code> very useful to search for words and replace them with something else. Appending <code>/g</code> does this globally and <code>/gc</code> does the same but with confirmation.</p>
<p>I posted something on <a href="https://www.reddit.com/r/vim/comments/1m6lzba/started_the_journey/" target="_blank" rel="noopener noreferrer" class="">Reddit</a> where I found a lot of awesome tips and sites, some of which are listed below. The below consists of videos or reading material I used to get better with Vim, as well as some awesome CLI tools to make your life that bit easier when in the terminal.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="distros">Distros<a href="https://mrpbennett.dev/blog/attempt-to-master-vim-motions#distros" class="hash-link" aria-label="Direct link to Distros" title="Direct link to Distros" translate="no">​</a></h2>
<p>I have been told and also read that I should learn to use vim motions and vim in general before I use a distro or at least learn to set up my own. I don't really have time to set my own up. So I use the amazing <a href="https://www.lazyvim.org/" target="_blank" rel="noopener noreferrer" class="">Lazyvim</a> which is actually fantastic. There are many out there such as, <a href="https://nvchad.com/" target="_blank" rel="noopener noreferrer" class="">NVChad</a> and <a href="https://www.lunarvim.org/" target="_blank" rel="noopener noreferrer" class="">LunarVim</a>, but I find LazyVim to be the best.</p>
<p>Having said that, because I have a homelab I have spent a lot of time just using vim. As I didn't want to faff around with installing distros on a server just to edit a few files. So I felt confident enough to move to a distro.</p>
<p>People say you should own your own config, in case the maintainers one day stop maintaining the distro. This could also happen with your favourite plugins too, it's just the way of open source I guess. However, there is <a href="https://github.com/nvim-lua/kickstart.nvim" target="_blank" rel="noopener noreferrer" class="">kickstart.nvim</a> which isn't a distro but more of a starting point for your own config which is less daunting. I have started to work along side my own config and Lazyvim. You can learn more about the project by watching <a href="https://youtu.be/m8C0Cq9Uv9o?si=hUiZO8vpR6VBeLjv" target="_blank" rel="noopener noreferrer" class="">The Only Video You Need to Get Started with Neovim</a>, which has been put together by one of the core Nvim devs and the maintainer of the project <a href="https://www.youtube.com/@teej_dv" target="_blank" rel="noopener noreferrer" class="">TJ DeVries</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="awesome-info">Awesome Info<a href="https://mrpbennett.dev/blog/attempt-to-master-vim-motions#awesome-info" class="hash-link" aria-label="Direct link to Awesome Info" title="Direct link to Awesome Info" translate="no">​</a></h2>
<p>A lot of this info has been found from various sources mainly Youtube and the awesome creators such as:</p>
<ul>
<li class=""><a href="https://www.youtube.com/@ThePrimeagen" target="_blank" rel="noopener noreferrer" class="">ThePrimeagen</a></li>
<li class=""><a href="https://www.youtube.com/@typecraft_dev" target="_blank" rel="noopener noreferrer" class="">TypeCraft</a></li>
<li class=""><a href="https://www.youtube.com/@devopstoolbox" target="_blank" rel="noopener noreferrer" class="">DevOps Toolbox</a></li>
<li class=""><a href="https://www.youtube.com/@henrymisc" target="_blank" rel="noopener noreferrer" class="">Henry Misc</a></li>
<li class=""><a href="https://www.youtube.com/@joseanmartinez" target="_blank" rel="noopener noreferrer" class="">Josean Martinez</a></li>
<li class=""><a href="https://www.youtube.com/@teej_dv" target="_blank" rel="noopener noreferrer" class="">TJ DeVries</a></li>
</ul>
<p>I am sure there are plenty more like but these are the guys I seem to be going back too.</p>
<ul>
<li class="">
<p><strong>Vim Motion</strong></p>
<ul>
<li class="">YT: <a href="https://www.youtube.com/playlist?list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R" target="_blank" rel="noopener noreferrer" class="">Vim As Your Editor - Series</a> by ThePrimeagen<!-- -->
<ul>
<li class=""><a href="https://www.youtube.com/watch?v=X6AR2RMB5tE&amp;list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R&amp;index=2" target="_blank" rel="noopener noreferrer" class="">Introduction</a></li>
<li class=""><a href="https://www.youtube.com/watch?v=5JGVtttuDQA&amp;list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R&amp;index=3&amp;t=204s" target="_blank" rel="noopener noreferrer" class="">Horizontal Movement</a></li>
<li class=""><a href="https://www.youtube.com/watch?v=KfENDDEpCsI&amp;list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R&amp;index=4" target="_blank" rel="noopener noreferrer" class="">Vertical Movement</a></li>
<li class=""><a href="https://www.youtube.com/watch?v=qZO9A5F6BZs&amp;list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R&amp;index=5" target="_blank" rel="noopener noreferrer" class="">Advance Motion Pt1</a></li>
<li class=""><a href="https://www.youtube.com/watch?v=uL9oOZStezw&amp;list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R&amp;index=6" target="_blank" rel="noopener noreferrer" class="">Advance Motion Pt2</a></li>
<li class=""><a href="https://www.youtube.com/watch?v=FrMRyXtiJkc&amp;list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R&amp;index=8" target="_blank" rel="noopener noreferrer" class="">Tips &amp; Tricks</a></li>
</ul>
</li>
<li class="">YT: <a href="https://youtu.be/z4eA2eC28qg?si=uhj-CWEbE2ipD1Ow" target="_blank" rel="noopener noreferrer" class="">Give Me 20 Minutes and I’ll Make You a Vim Motions Expert</a> by DevOps Toolbox</li>
<li class=""><a href="https://vim.rtorr.com/" target="_blank" rel="noopener noreferrer" class="">Vim Cheat Sheet</a></li>
<li class=""><a href="https://www.vim-hero.com/" target="_blank" rel="noopener noreferrer" class="">Vim Hero</a></li>
<li class=""><a href="https://vim-adventures.com/" target="_blank" rel="noopener noreferrer" class="">Vim Adventures</a></li>
<li class=""><a href="https://m4xshen.dev/posts/vim-command-workflow" target="_blank" rel="noopener noreferrer" class="">Practical Vim command workflow</a>
<ul>
<li class=""><a href="https://github.com/m4xshen/hardtime.nvim" target="_blank" rel="noopener noreferrer" class="">hardtime.nvim</a></li>
</ul>
</li>
<li class=""><a href="https://youtu.be/RdyfT2dbt78?si=tUd6gg7D8W152Yne" target="_blank" rel="noopener noreferrer" class="">Vim Motions &amp; Tricks I Wish I Learned Sooner</a> by Henry Misc</li>
<li class=""><a href="https://youtu.be/wgbvBDR4yxk?si=WMdNzLkC3efmClik" target="_blank" rel="noopener noreferrer" class="">More Vim Tricks That Blew My Mind</a> by Henry Misc</li>
<li class=""><a href="https://youtu.be/sK6HR9lzgU0?si=PPNqZ4brkPhinpZs" target="_blank" rel="noopener noreferrer" class="">Vim's Most Misunderstood Feature: Tabs</a> by Henry Misc</li>
<li class=""><a href="https://youtu.be/bTmEqmtr_6I?si=hbJMig1MwMuwZ0OQ" target="_blank" rel="noopener noreferrer" class="">This VIM trick BLEW MY MIND</a> by Typecraft</li>
<li class=""><a href="https://youtu.be/RSlrxE21l_k?si=qmy_jjT_xenRgrdw" target="_blank" rel="noopener noreferrer" class="">30 Vim commands you NEED TO KNOW (in just 10 minutes)</a> by Typecraft</li>
<li class=""><a href="https://youtu.be/lWTzqPfy1gE?si=PYZnnAaJz4-UIo2J" target="_blank" rel="noopener noreferrer" class="">Vim Motions for absolute beginners!!!</a> by Dispatch</li>
<li class=""><a href="https://youtu.be/nBjEzQlJLHE?si=zeRByx_Pa84-Y62-" target="_blank" rel="noopener noreferrer" class="">Intermediate Vim Motions and Pro Tips!!!</a> by Dispatch</li>
</ul>
</li>
<li class="">
<p><strong>Terminals</strong></p>
<ul>
<li class=""><a href="https://ghostty.org/" target="_blank" rel="noopener noreferrer" class="">Ghostty</a></li>
<li class=""><a href="https://www.warp.dev/terminal" target="_blank" rel="noopener noreferrer" class="">Warp</a></li>
</ul>
</li>
<li class="">
<p><strong>CLI Tools</strong></p>
<ul>
<li class="">YT: <a href="https://youtu.be/eyXxEBZMVQI?si=UAz-yntLzHTDpfo-" target="_blank" rel="noopener noreferrer" class="">7 Amazing Terminal API tools</a> by DevOps Toolbox</li>
<li class="">YT: <a href="https://youtu.be/MvLQor1Ck3M?si=w8-Qkqaccopn_Fgk" target="_blank" rel="noopener noreferrer" class="">fzf</a> by DevOps Toolbox</li>
<li class="">YT: <a href="https://youtu.be/oTNRvnQLLLs?si=Nq7WmOqIfVCZXBT8" target="_blank" rel="noopener noreferrer" class="">fzf</a> by Typecraft</li>
<li class="">YT: <a href="https://www.youtube.com/watch?v=Ihg37znaiBo" target="_blank" rel="noopener noreferrer" class="">Lazygit - The Best Way To Use Git On The Terminal</a> by Josean Martinez</li>
<li class="">YT: <a href="https://www.youtube.com/watch?v=iKb3cHDD9hw" target="_blank" rel="noopener noreferrer" class="">Yazi</a> by Josean Martinez</li>
<li class="">YT: <a href="https://youtu.be/ZPfQS5FHNYQ?si=IFqwO8vvaRWvFZeV" target="_blank" rel="noopener noreferrer" class="">Zellij</a> by Typecraft</li>
</ul>
<p><strong>Github</strong></p>
<ul>
<li class=""><a href="https://github.com/sxyazi/yazi" target="_blank" rel="noopener noreferrer" class="">yazi</a> - File Manager</li>
<li class=""><a href="https://posting.sh/" target="_blank" rel="noopener noreferrer" class="">posting</a> - API client</li>
<li class=""><a href="https://github.com/rs/curlie" target="_blank" rel="noopener noreferrer" class="">curlie</a> - A better <code>curl</code></li>
<li class=""><a href="https://github.com/eza-community/eza" target="_blank" rel="noopener noreferrer" class="">eza</a> - A better <code>ls</code></li>
<li class=""><a href="https://github.com/ajeetdsouza/zoxide" target="_blank" rel="noopener noreferrer" class="">zoxide</a> - Learn your directories</li>
<li class=""><a href="https://github.com/sharkdp/bat" target="_blank" rel="noopener noreferrer" class="">bat</a> - Syntax highlighting</li>
<li class=""><a href="https://github.com/mitmproxy/mitmproxy" target="_blank" rel="noopener noreferrer" class="">mitmproxy</a> - Charles for the terminal</li>
<li class=""><a href="https://github.com/zellij-org/zellij" target="_blank" rel="noopener noreferrer" class="">zellij</a> - terminal multiplexer</li>
</ul>
</li>
<li class="">
<p><strong>LazyVim</strong></p>
<ul>
<li class=""><a href="https://www.lazyvim.org/" target="_blank" rel="noopener noreferrer" class="">lazyvim.org</a></li>
<li class="">YT: <a href="https://www.youtube.com/watch?v=evCmP4hH7ZU&amp;t=391s" target="_blank" rel="noopener noreferrer" class="">LazyVim from Scratch to BeastMode</a> by DevOps Toolbox</li>
</ul>
</li>
<li class="">
<p><strong>Nvim</strong></p>
<ul>
<li class=""><a href="https://youtu.be/KYDG3AHgYEs?si=rcvgxBGs5wpzHXNg" target="_blank" rel="noopener noreferrer" class="">Full Neovim Setup from Scratch in 2025</a> by Henry Misc</li>
<li class=""><a href="https://youtu.be/6pAG3BHurdM?si=skgkAbKGBigyQCpl" target="_blank" rel="noopener noreferrer" class="">How I Setup Neovim To Make It AMAZING in 2024: The Ultimate Guide</a> by Josean Martinez</li>
<li class=""><a href="https://www.youtube.com/watch?v=J9yqSdvAKXY&amp;list=PLsz00TDipIffxsNXSkskknolKShdbcALR" target="_blank" rel="noopener noreferrer" class="">NeoVim setup config playlist</a> by TypeCraft</li>
</ul>
</li>
<li class="">
<p><strong>Other</strong></p>
<ul>
<li class="">Book: <a href="https://www.amazon.co.uk/dp/1680501275?ref=ppx_yo2ov_dt_b_fed_asin_title" target="_blank" rel="noopener noreferrer" class="">Practical Vim 2nd Edition</a></li>
<li class="">Book: <a href="https://lazyvim-ambitious-devs.phillips.codes/" target="_blank" rel="noopener noreferrer" class="">lazyvim-ambitious-devs</a></li>
<li class="">YT: <a href="https://youtu.be/K4PoBfz3WLA?si=-L-cXr2oP_3dgB4t" target="_blank" rel="noopener noreferrer" class="">How To Use Vim/Neovim Macros For Next Level Productivity</a> by Josean Martinez</li>
<li class=""><a href="https://github.com/takac/vim-hardtime" target="_blank" rel="noopener noreferrer" class="">Vim Hardtime</a></li>
<li class=""><a href="https://www.keybr.com/" target="_blank" rel="noopener noreferrer" class="">Touch typing</a></li>
</ul>
</li>
</ul>]]></content:encoded>
            <category>neovim</category>
        </item>
        <item>
            <title><![CDATA[Supercharge your API workflow with Kulala in Neovim]]></title>
            <link>https://mrpbennett.dev/blog/kulala-in-neovim</link>
            <guid>https://mrpbennett.dev/blog/kulala-in-neovim</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[If you're a developer like me who lives in Neovim and frequently works with APIs, you've probably bounced between different tools for testing HTTP requests. Maybe you've used Postman, httpie, or curl commands scattered across your terminal history. But what if I told you there's a better way—one that keeps you in your favorite editor and treats your API requests as code?]]></description>
            <content:encoded><![CDATA[<p>If you're a developer like me who lives in Neovim and frequently works with APIs, you've probably bounced between different tools for testing HTTP requests. Maybe you've used Postman, httpie, or curl commands scattered across your terminal history. But what if I told you there's a better way—one that keeps you in your favorite editor and treats your API requests as code?</p>
<p>Enter <code>.http</code> files and kulala.nvim, a powerful combination that brings REST API testing directly into your Neovim workflow.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-http-files">What Are .http Files?<a href="https://mrpbennett.dev/blog/kulala-in-neovim#what-are-http-files" class="hash-link" aria-label="Direct link to What Are .http Files?" title="Direct link to What Are .http Files?" translate="no">​</a></h3>
<p><code>.http</code> files (also known as HTTP request files) are plain text files that contain HTTP requests in a human-readable format. They originated from JetBrains IDEs but have since been adopted by various editors and tools, including VS Code's REST Client extension.</p>
<p>The beauty of <code>.http</code> files lies in their simplicity. Instead of clicking through a GUI or remembering complex curl syntax, you write your HTTP requests in a declarative format that's easy to read, version control, and share with your team.</p>
<p>Here's what a basic <code>.http</code> file looks like:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">### Get all users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET https://api.example.com/users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Accept: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Create a new user</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST https://api.example.com/users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Content-Type: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "name": "John Doe",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "email": "john@example.com"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-features-of-http-files">Key Features of .http Files<a href="https://mrpbennett.dev/blog/kulala-in-neovim#key-features-of-http-files" class="hash-link" aria-label="Direct link to Key Features of .http Files" title="Direct link to Key Features of .http Files" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-request-separation">1. Request Separation<a href="https://mrpbennett.dev/blog/kulala-in-neovim#1-request-separation" class="hash-link" aria-label="Direct link to 1. Request Separation" title="Direct link to 1. Request Separation" translate="no">​</a></h3>
<p>Use <code>###</code> to separate different requests in the same file. Each request can be executed independently.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-headers">2. Headers<a href="https://mrpbennett.dev/blog/kulala-in-neovim#2-headers" class="hash-link" aria-label="Direct link to 2. Headers" title="Direct link to 2. Headers" translate="no">​</a></h3>
<p>Add headers right after the request line, one per line:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET https://api.example.com/protected</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer your-token-here</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">User-Agent: MyApp/1.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Accept: application/json</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-request-bodies">3. Request Bodies<a href="https://mrpbennett.dev/blog/kulala-in-neovim#3-request-bodies" class="hash-link" aria-label="Direct link to 3. Request Bodies" title="Direct link to 3. Request Bodies" translate="no">​</a></h3>
<p>For POST, PUT, and PATCH requests, add a blank line after headers and include your request body:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">POST https://api.example.com/posts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Content-Type: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "title": "My Post",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "content": "This is the content"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-variables">4. Variables<a href="https://mrpbennett.dev/blog/kulala-in-neovim#4-variables" class="hash-link" aria-label="Direct link to 4. Variables" title="Direct link to 4. Variables" translate="no">​</a></h3>
<p>Define and reuse variables to avoid repetition:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@baseUrl = https://api.example.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@token = your-auth-token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET {{baseUrl}}/users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer {{token}}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-comments">5. Comments<a href="https://mrpbennett.dev/blog/kulala-in-neovim#5-comments" class="hash-link" aria-label="Direct link to 5. Comments" title="Direct link to 5. Comments" translate="no">​</a></h3>
<p>Use <code>#</code> or <code>//</code> for comments to document your requests:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># This endpoint returns all active users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET https://api.example.com/users?status=active</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="enter-kulalanvim">Enter kulala.nvim<a href="https://mrpbennett.dev/blog/kulala-in-neovim#enter-kulalanvim" class="hash-link" aria-label="Direct link to Enter kulala.nvim" title="Direct link to Enter kulala.nvim" translate="no">​</a></h2>
<p>While <code>.http</code> files are useful on their own, they really shine when paired with a good client. For Neovim users, kulala.nvim is the perfect companion. It's a lightweight, fast HTTP client plugin that brings the power of <code>.http</code> files directly into your editor.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-kulalanvim">Why kulala.nvim?<a href="https://mrpbennett.dev/blog/kulala-in-neovim#why-kulalanvim" class="hash-link" aria-label="Direct link to Why kulala.nvim?" title="Direct link to Why kulala.nvim?" translate="no">​</a></h3>
<ol>
<li class=""><strong>Native Neovim Integration</strong>: No need to leave your editor</li>
<li class=""><strong>Lightweight</strong>: Minimal overhead, fast execution</li>
<li class=""><strong>Feature-rich</strong>: Supports all standard HTTP methods, headers, and body types</li>
<li class=""><strong>Variable Support</strong>: Full support for environment variables and file-scoped variables</li>
<li class=""><strong>Response Handling</strong>: View responses in a dedicated buffer with syntax highlighting</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-kulalanvim">Setting Up kulala.nvim<a href="https://mrpbennett.dev/blog/kulala-in-neovim#setting-up-kulalanvim" class="hash-link" aria-label="Direct link to Setting Up kulala.nvim" title="Direct link to Setting Up kulala.nvim" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="installation">Installation<a href="https://mrpbennett.dev/blog/kulala-in-neovim#installation" class="hash-link" aria-label="Direct link to Installation" title="Direct link to Installation" translate="no">​</a></h3>
<p>Using <a href="https://github.com/folke/lazy.nvim" target="_blank" rel="noopener noreferrer" class="">lazy.nvim</a>:</p>
<div class="language-lua codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lua codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  'mistweaverco/kulala.nvim',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config = function()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    require('kulala').setup({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      -- Default request timeout</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      default_timeout = 5000,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      -- Show icons in the request list</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      icons = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        inlay = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          loading = "⏳",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          done = "✅"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        lualine = "🐼"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    })</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="basic-configuration">Basic Configuration<a href="https://mrpbennett.dev/blog/kulala-in-neovim#basic-configuration" class="hash-link" aria-label="Direct link to Basic Configuration" title="Direct link to Basic Configuration" translate="no">​</a></h3>
<p>Add some keybindings to make kulala.nvim easy to use:</p>
<div class="language-lua codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lua codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vim.keymap.set("n", "&lt;leader&gt;rr", require('kulala').run, { desc = "Execute HTTP request" })</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim.keymap.set("n", "&lt;leader&gt;ra", require('kulala').run_all, { desc = "Execute all HTTP requests" })</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim.keymap.set("n", "&lt;leader&gt;ri", require('kulala').inspect, { desc = "Inspect HTTP request" })</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vim.keymap.set("n", "&lt;leader&gt;rt", require('kulala').toggle_view, { desc = "Toggle response view" })</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="practical-examples">Practical Examples<a href="https://mrpbennett.dev/blog/kulala-in-neovim#practical-examples" class="hash-link" aria-label="Direct link to Practical Examples" title="Direct link to Practical Examples" translate="no">​</a></h2>
<p>Let's walk through some practical examples of using <code>.http</code> files with kulala.nvim.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-1-basic-crud-operations">Example 1: Basic CRUD Operations<a href="https://mrpbennett.dev/blog/kulala-in-neovim#example-1-basic-crud-operations" class="hash-link" aria-label="Direct link to Example 1: Basic CRUD Operations" title="Direct link to Example 1: Basic CRUD Operations" translate="no">​</a></h3>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@baseUrl = https://jsonplaceholder.typicode.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Get all posts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET {{baseUrl}}/posts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Get specific post</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET {{baseUrl}}/posts/1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Create new post</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST {{baseUrl}}/posts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Content-Type: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "title": "My New Post",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "body": "This is the content of my post",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "userId": 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Update post</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PUT {{baseUrl}}/posts/1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Content-Type: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "id": 1,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "title": "Updated Post Title",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "body": "Updated content",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "userId": 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Delete post</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DELETE {{baseUrl}}/posts/1</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-2-authentication-flow">Example 2: Authentication Flow<a href="https://mrpbennett.dev/blog/kulala-in-neovim#example-2-authentication-flow" class="hash-link" aria-label="Direct link to Example 2: Authentication Flow" title="Direct link to Example 2: Authentication Flow" translate="no">​</a></h3>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@authUrl = https://api.example.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@username = your-username</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@password = your-password</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Login to get token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST {{authUrl}}/auth/login</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Content-Type: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "username": "{{username}}",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "password": "{{password}}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Use the token (you'll need to copy it from the login response)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@token = your-jwt-token-here</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Get protected resource</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET {{authUrl}}/protected</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer {{token}}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Refresh token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST {{authUrl}}/auth/refresh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer {{token}}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="advanced-features">Advanced Features<a href="https://mrpbennett.dev/blog/kulala-in-neovim#advanced-features" class="hash-link" aria-label="Direct link to Advanced Features" title="Direct link to Advanced Features" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="environment-files">Environment Files<a href="https://mrpbennett.dev/blog/kulala-in-neovim#environment-files" class="hash-link" aria-label="Direct link to Environment Files" title="Direct link to Environment Files" translate="no">​</a></h3>
<p>kulala.nvim supports environment-specific variables through <code>.env</code> files:</p>
<p>Create a <code>http-client.env.json</code> file:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"dev"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"baseUrl"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://api-dev.example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"token"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"dev-token"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"prod"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"baseUrl"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://api.example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"token"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prod-token"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Then use variables in your <code>.http</code> file:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET {{baseUrl}}/users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer {{token}}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="response-testing">Response Testing<a href="https://mrpbennett.dev/blog/kulala-in-neovim#response-testing" class="hash-link" aria-label="Direct link to Response Testing" title="Direct link to Response Testing" translate="no">​</a></h3>
<p>You can even write simple tests for your responses:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">GET https://api.example.com/users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt; {%</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  client.test("Status is 200", function() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    client.assert(response.status === 200, "Expected status 200");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  client.test("Response has users", function() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    client.assert(response.body.length &gt; 0, "Expected users in response");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">%}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="best-practices">Best Practices<a href="https://mrpbennett.dev/blog/kulala-in-neovim#best-practices" class="hash-link" aria-label="Direct link to Best Practices" title="Direct link to Best Practices" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-organize-by-feature">1. Organize by Feature<a href="https://mrpbennett.dev/blog/kulala-in-neovim#1-organize-by-feature" class="hash-link" aria-label="Direct link to 1. Organize by Feature" title="Direct link to 1. Organize by Feature" translate="no">​</a></h3>
<p>Create separate <code>.http</code> files for different API endpoints or features:</p>
<ul>
<li class=""><code>auth.http</code> - Authentication endpoints</li>
<li class=""><code>users.http</code> - User management</li>
<li class=""><code>posts.http</code> - Content operations</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-use-variables-liberally">2. Use Variables Liberally<a href="https://mrpbennett.dev/blog/kulala-in-neovim#2-use-variables-liberally" class="hash-link" aria-label="Direct link to 2. Use Variables Liberally" title="Direct link to 2. Use Variables Liberally" translate="no">​</a></h3>
<p>Define common values as variables to make maintenance easier:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@apiVersion = v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@contentType = application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@baseUrl = https://api.example.com/{{apiVersion}}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-document-your-requests">3. Document Your Requests<a href="https://mrpbennett.dev/blog/kulala-in-neovim#3-document-your-requests" class="hash-link" aria-label="Direct link to 3. Document Your Requests" title="Direct link to 3. Document Your Requests" translate="no">​</a></h3>
<p>Use comments to explain what each request does:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">### Creates a new user account</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">### Requires admin privileges</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">POST {{baseUrl}}/users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer {{adminToken}}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Content-Type: application/json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "name": "New User",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "email": "newuser@example.com",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  "role": "user"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-version-control">4. Version Control<a href="https://mrpbennett.dev/blog/kulala-in-neovim#4-version-control" class="hash-link" aria-label="Direct link to 4. Version Control" title="Direct link to 4. Version Control" translate="no">​</a></h3>
<p>Commit your <code>.http</code> files to version control. They serve as living documentation of your API and make it easy for team members to test endpoints.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-separate-sensitive-data">5. Separate Sensitive Data<a href="https://mrpbennett.dev/blog/kulala-in-neovim#5-separate-sensitive-data" class="hash-link" aria-label="Direct link to 5. Separate Sensitive Data" title="Direct link to 5. Separate Sensitive Data" translate="no">​</a></h3>
<p>Never commit sensitive data like API keys. Use environment files or variables that can be set locally:</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># ❌ Don't do this</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer sk-1234567890abcdef</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># ✅ Do this instead</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Authorization: Bearer {{apiKey}}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workflow-integration">Workflow Integration<a href="https://mrpbennett.dev/blog/kulala-in-neovim#workflow-integration" class="hash-link" aria-label="Direct link to Workflow Integration" title="Direct link to Workflow Integration" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="testing-during-development">Testing During Development<a href="https://mrpbennett.dev/blog/kulala-in-neovim#testing-during-development" class="hash-link" aria-label="Direct link to Testing During Development" title="Direct link to Testing During Development" translate="no">​</a></h3>
<p>Keep a <code>.http</code> file open in a split while you're developing your API. Make a change to your backend code, save, and immediately test it with <code>&lt;leader&gt;rr</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="api-documentation">API Documentation<a href="https://mrpbennett.dev/blog/kulala-in-neovim#api-documentation" class="hash-link" aria-label="Direct link to API Documentation" title="Direct link to API Documentation" translate="no">​</a></h3>
<p>Your <code>.http</code> files serve as executable documentation. New team members can understand your API by looking at and running the requests.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cicd-integration">CI/CD Integration<a href="https://mrpbennett.dev/blog/kulala-in-neovim#cicd-integration" class="hash-link" aria-label="Direct link to CI/CD Integration" title="Direct link to CI/CD Integration" translate="no">​</a></h3>
<p>Some tools can run <code>.http</code> files as part of your CI/CD pipeline, turning them into integration tests.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://mrpbennett.dev/blog/kulala-in-neovim#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p><code>.http</code> files combined with kulala.nvim offer a powerful, lightweight solution for API development and testing. They keep you in your editor, provide version-controllable API documentation, and make testing as simple as pressing a key combination.</p>
<p>If you're tired of context-switching between your editor and external HTTP clients, give this combo a try. Your workflow will thank you.</p>
<p>The best part? Your <code>.http</code> files are portable. If you switch editors or tools, your requests go with you. They're just text files, after all—but text files that can supercharge your API development workflow.</p>
<p>Ready to give it a shot? Start with a simple <code>.http</code> file, install kulala.nvim, and begin exploring your APIs without ever leaving Neovim.</p>]]></content:encoded>
            <category>neovim</category>
        </item>
        <item>
            <title><![CDATA[Why you need CROSS JOIN with an UNNEST in Trino]]></title>
            <link>https://mrpbennett.dev/blog/unnest-in-trino</link>
            <guid>https://mrpbennett.dev/blog/unnest-in-trino</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[If you've worked with arrays or delimited strings in Trino, you've probably encountered the need to "explode" them into separate rows. This is where UNNEST comes in. But there's a catch: unlike some other SQL databases, Trino requires you to use CROSS JOIN UNNEST rather than just UNNEST in your SELECT clause. Let's explore why.]]></description>
            <content:encoded><![CDATA[<p>If you've worked with arrays or delimited strings in Trino, you've probably encountered the need to "explode" them into separate rows. This is where <code>UNNEST</code> comes in. But there's a catch: unlike some other SQL databases, Trino requires you to use <code>CROSS JOIN UNNEST</code> rather than just <code>UNNEST</code> in your <code>SELECT</code> clause. Let's explore why.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-hierarchical-data-in-a-single-column">The Problem: Hierarchical Data in a Single Column<a href="https://mrpbennett.dev/blog/unnest-in-trino#the-problem-hierarchical-data-in-a-single-column" class="hash-link" aria-label="Direct link to The Problem: Hierarchical Data in a Single Column" title="Direct link to The Problem: Hierarchical Data in a Single Column" translate="no">​</a></h2>
<p>Imagine you have data that looks like this:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">| id | categories                    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|----|-------------------------------|</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 1  | C10|C10.228|C10.228.140       |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 2  | E02|E02.642|E02.642.249       |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 3  | C16|C16.131                   |</span><br></span></code></pre></div></div>
<p>Each row contains multiple pipe-separated values that you need to work with individually. Maybe you need to join them to another table, count occurrences, or filter by specific codes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-solution-unnest">The Solution: <code>UNNEST</code><a href="https://mrpbennett.dev/blog/unnest-in-trino#the-solution-unnest" class="hash-link" aria-label="Direct link to the-solution-unnest" title="Direct link to the-solution-unnest" translate="no">​</a></h2>
<p><code>UNNEST</code> is a table function that takes an array and returns a table with one row for each array element. First, we split our delimited string into an array, then unnest it:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Result:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">| id | code        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|----|-------------|</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 1  | C10         |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 1  | C10.228     |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 1  | C10.228.140 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 2  | E02         |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 2  | E02.642     |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 2  | E02.642.249 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 3  | C16         |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 3  | C16.131     |</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-cross-join">Why CROSS JOIN?<a href="https://mrpbennett.dev/blog/unnest-in-trino#why-cross-join" class="hash-link" aria-label="Direct link to Why CROSS JOIN?" title="Direct link to Why CROSS JOIN?" translate="no">​</a></h2>
<p>Here's the key insight: <strong><code>UNNEST</code> is a table function, not a scalar function.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-the-difference">What's the Difference?<a href="https://mrpbennett.dev/blog/unnest-in-trino#whats-the-difference" class="hash-link" aria-label="Direct link to What's the Difference?" title="Direct link to What's the Difference?" translate="no">​</a></h3>
<p><strong>Scalar functions</strong> (like <code>upper()</code>, <code>length()</code>, <code>split()</code>):</p>
<ul>
<li class="">Take one or more values as input</li>
<li class="">Return a single value as output</li>
<li class="">Can be used directly in SELECT clauses</li>
</ul>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- This works fine</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> upper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">description</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><strong>Table functions</strong> (like <code>UNNEST</code>):</p>
<ul>
<li class="">Take input (often an array)</li>
<li class="">Return a <strong>table</strong> (multiple rows)</li>
<li class="">Must be used in the FROM clause</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-you-cant-do-this">Why You Can't Do This<a href="https://mrpbennett.dev/blog/unnest-in-trino#why-you-cant-do-this" class="hash-link" aria-label="Direct link to Why You Can't Do This" title="Direct link to Why You Can't Do This" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- ❌ This doesn't work in Trino</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> unnest</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Think about what this would mean: for each input row, you're trying to put multiple output rows into a single <code>SELECT</code> expression. SQL doesn't know how to handle that because SELECT operates row-by-row.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-cross-join-pattern">The CROSS JOIN Pattern<a href="https://mrpbennett.dev/blog/unnest-in-trino#the-cross-join-pattern" class="hash-link" aria-label="Direct link to The CROSS JOIN Pattern" title="Direct link to The CROSS JOIN Pattern" translate="no">​</a></h3>
<p>A <code>CROSS JOIN</code> creates a Cartesian product: every row from the left table combined with every row from the right table.</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- Regular CROSS JOIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> table1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> table2</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>When you use <code>CROSS JOIN UNNEST</code>, you're doing something clever:</p>
<ol>
<li class="">For each row in your main table</li>
<li class="">Generate a mini-table from the array using <code>UNNEST</code></li>
<li class="">Join that row with its own mini-table</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Here's what happens step by step:</p>
<p><strong>Row 1:</strong> id=1, categories="C10|C10.228"</p>
<ul>
<li class="">Split creates: <code>['C10', 'C10.228']</code></li>
<li class="">UNNEST creates a 2-row table: <code>{C10}, {C10.228}</code></li>
<li class="">CROSS JOIN combines: <code>(1, C10)</code>, <code>(1, C10.228)</code></li>
</ul>
<p><strong>Row 2:</strong> id=2, categories="E02"</p>
<ul>
<li class="">Split creates: <code>['E02']</code></li>
<li class="">UNNEST creates a 1-row table: <code>{E02}</code></li>
<li class="">CROSS JOIN combines: <code>(2, E02)</code></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="practical-examples">Practical Examples<a href="https://mrpbennett.dev/blog/unnest-in-trino#practical-examples" class="hash-link" aria-label="Direct link to Practical Examples" title="Direct link to Practical Examples" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-1-joining-with-another-table">Example 1: Joining with Another Table<a href="https://mrpbennett.dev/blog/unnest-in-trino#example-1-joining-with-another-table" class="hash-link" aria-label="Direct link to Example 1: Joining with Another Table" title="Direct link to Example 1: Joining with Another Table" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">product_name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    c</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">category_description</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> products t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> category_lookup c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">ON</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">category_code</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-2-filtering-for-specific-codes">Example 2: Filtering for Specific Codes<a href="https://mrpbennett.dev/blog/unnest-in-trino#example-2-filtering-for-specific-codes" class="hash-link" aria-label="Direct link to Example 2: Filtering for Specific Codes" title="Direct link to Example 2: Filtering for Specific Codes" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DISTINCT</span><span class="token plain"> id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> product_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> products t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">LIKE</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'C10%'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-3-counting-occurrences">Example 3: Counting Occurrences<a href="https://mrpbennett.dev/blog/unnest-in-trino#example-3-counting-occurrences" class="hash-link" aria-label="Direct link to Example 3: Counting Occurrences" title="Direct link to Example 3: Counting Occurrences" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">count</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> num_products</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> products t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">GROUP</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">ORDER</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> num_products </span><span class="token keyword" style="color:#00009f">DESC</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-4-working-with-multiple-arrays">Example 4: Working with Multiple Arrays<a href="https://mrpbennett.dev/blog/unnest-in-trino#example-4-working-with-multiple-arrays" class="hash-link" aria-label="Direct link to Example 4: Working with Multiple Arrays" title="Direct link to Example 4: Working with Multiple Arrays" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tag</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    category</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> products t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tags</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tag</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> categories</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">category</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="common-pitfalls">Common Pitfalls<a href="https://mrpbennett.dev/blog/unnest-in-trino#common-pitfalls" class="hash-link" aria-label="Direct link to Common Pitfalls" title="Direct link to Common Pitfalls" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pitfall-1-limit-before-vs-after-unnest">Pitfall 1: LIMIT Before vs After UNNEST<a href="https://mrpbennett.dev/blog/unnest-in-trino#pitfall-1-limit-before-vs-after-unnest" class="hash-link" aria-label="Direct link to Pitfall 1: LIMIT Before vs After UNNEST" title="Direct link to Pitfall 1: LIMIT Before vs After UNNEST" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- This limits AFTER unnesting (might get 20 exploded rows from 2 original rows)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">LIMIT</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- This limits BEFORE unnesting (gets 20 original rows, then unnests all)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> categories</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">LIMIT</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pitfall-2-forgetting-the-alias">Pitfall 2: Forgetting the Alias<a href="https://mrpbennett.dev/blog/unnest-in-trino#pitfall-2-forgetting-the-alias" class="hash-link" aria-label="Direct link to Pitfall 2: Forgetting the Alias" title="Direct link to Pitfall 2: Forgetting the Alias" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- ❌ Missing alias</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- ✅ With alias</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CROSS</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pitfall-3-null-or-empty-arrays">Pitfall 3: NULL or Empty Arrays<a href="https://mrpbennett.dev/blog/unnest-in-trino#pitfall-3-null-or-empty-arrays" class="hash-link" aria-label="Direct link to Pitfall 3: NULL or Empty Arrays" title="Direct link to Pitfall 3: NULL or Empty Arrays" translate="no">​</a></h3>
<p>If your column contains NULL or produces an empty array, UNNEST will produce zero rows for that input row (the row disappears). Use a LEFT JOIN if you need to preserve rows:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- Preserves rows even if array is empty</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> my_table t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">LEFT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">JOIN</span><span class="token plain"> UNNEST</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">categories</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'|'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> codes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ON</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-takeaways">Key Takeaways<a href="https://mrpbennett.dev/blog/unnest-in-trino#key-takeaways" class="hash-link" aria-label="Direct link to Key Takeaways" title="Direct link to Key Takeaways" translate="no">​</a></h2>
<ol>
<li class=""><strong>UNNEST is a table function</strong>, not a scalar function - it produces rows, not values</li>
<li class=""><strong>CROSS JOIN is required</strong> because you're joining each row with its own generated table</li>
<li class=""><strong>The pattern is</strong>: <code>CROSS JOIN UNNEST(array_expression) AS alias(column_name)</code></li>
<li class="">This pattern is powerful for working with hierarchical, delimited, or array data in Trino</li>
</ol>
<p>Once you understand why the CROSS JOIN is needed, the pattern becomes intuitive. You're not just selecting values - you're joining your data with a dynamically generated table derived from each row's array!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="further-reading">Further Reading<a href="https://mrpbennett.dev/blog/unnest-in-trino#further-reading" class="hash-link" aria-label="Direct link to Further Reading" title="Direct link to Further Reading" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://trino.io/docs/current/sql/select.html#unnest" target="_blank" rel="noopener noreferrer" class="">Trino UNNEST Documentation</a></li>
<li class=""><a href="https://trino.io/docs/current/functions/array.html" target="_blank" rel="noopener noreferrer" class="">Trino Array Functions</a></li>
<li class=""><a href="https://trino.io/docs/current/sql/select.html#join-clause" target="_blank" rel="noopener noreferrer" class="">Understanding SQL Joins</a></li>
</ul>]]></content:encoded>
            <category>trino</category>
            <category>sql</category>
        </item>
        <item>
            <title><![CDATA[Ah moment when using slices in Go!]]></title>
            <link>https://mrpbennett.dev/blog/ah-with-slices-in-go</link>
            <guid>https://mrpbennett.dev/blog/ah-with-slices-in-go</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[The slices package defines various functions useful with slices of any type. Below are some small examples I came across today. I wanted to see how I could remove a string from a slice. Below I have a slice called names.]]></description>
            <content:encoded><![CDATA[<p>The slices package defines various functions useful with slices of any type. Below are some small examples I came across today. I wanted to see how I could remove a string from a slice. Below I have a slice called <code>names</code>.</p>
<div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">names </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"Paul"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Fiona"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Murphy"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Linda"</span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>I wanted to remove the string <code>Linda</code>. In the block below I used two functions from the slice package these were <a href="https://pkg.go.dev/slices#Index" target="_blank" rel="noopener noreferrer" class=""><code>Index</code></a> and <a href="https://pkg.go.dev/slices#Index" target="_blank" rel="noopener noreferrer" class=""><code>Delete</code></a> these two allowed me to get the index of the string and delete it by its index.</p>
<div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> idx </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> slices</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Index</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">names</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Linda"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> idx </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    slices</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Delete</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">names</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> idx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> idx</span><span class="token operator" style="color:#393A34">+</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>So what is happening here?</p>
<p><code>slices.Index</code>: takes two arguments, the slice in question <code>names</code> and the item I am searching for.</p>
<blockquote>
<p>Index returns the index of the first occurrence of v in s, or -1 if not present.</p>
</blockquote>
<p>In the <code>if</code> statement you can see a <code>;</code> this is used to combine two conditions. Allowing for the statement to read as follows. IF the index of "Linda" does not equal <code>-1</code> then action the code inside the block. Pretty cool as this is different from Python which I am use to.</p>
<p>Next there is <code>slices.Delete</code> this takes three arguments, the slice, the start index and the end index. This would work in the same manner as a slice would work in Python <code>[1:4]</code> for example. Therefore with the block above, I took the index of the string, for this example lets say it was 3 and I only wanted to delete that index, therefore the end index would be 4.</p>
<p>This is why the arguments are <code>Delete(names, idx, idx+1)</code> which in turn would be <code>Delete(names, 3, 3+1)</code></p>
<p>Quite simple but was am ah ha moment for me.</p>
<p>More can be found on the slices package <a href="https://pkg.go.dev/slices#pkg-overview" target="_blank" rel="noopener noreferrer" class="">here</a></p>]]></content:encoded>
            <category>go</category>
        </item>
        <item>
            <title><![CDATA[Quick way to setup Proxmox with Tailscale for remote access]]></title>
            <link>https://mrpbennett.dev/blog/setting-proxmox-with-tailscale</link>
            <guid>https://mrpbennett.dev/blog/setting-proxmox-with-tailscale</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I sometimes work remotely (away from my cosy home office) and I need to access my homelab from any location. Accessing Proxmox using Tailscale is super easy. First you will need to install Tailscale on your Proxmox nodes.]]></description>
            <content:encoded><![CDATA[<p>I sometimes work remotely (away from my cosy home office) and I need to access my homelab from any location. Accessing Proxmox using Tailscale is super easy. First you will need to install Tailscale on your Proxmox nodes.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://tailscale.com/install.sh | sh</span><br></span></code></pre></div></div>
<p>Once installed run <code>tailscale up</code> and authenticate with your account. Easy, Tailscale is installed and you will be able to see it under your account list.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="enable-https-access-to-the-proxmox-web-ui">Enable HTTPS access to the Proxmox Web UI<a href="https://mrpbennett.dev/blog/setting-proxmox-with-tailscale#enable-https-access-to-the-proxmox-web-ui" class="hash-link" aria-label="Direct link to Enable HTTPS access to the Proxmox Web UI" title="Direct link to Enable HTTPS access to the Proxmox Web UI" translate="no">​</a></h2>
<p>The Proxmox Web UI is served over HTTPS by default on port <code>8006</code>. The default certificate is self-signed, so you will need to accept the certificate warning in your browser. Which as we all know is rather a big PIA. So let's change that.</p>
<p>The following script will use Tailscale to generate a certificate for the Proxmox host and install it in the Proxmox certificate store. However it will only generate the cert once, when the cert expires you will have to accept the certificate warning again. Let's change that...</p>
<p>First we need to install <a href="https://jqlang.org/" target="_blank" rel="noopener noreferrer" class=""><code>jq</code></a> without that the script below will fail as we can't populate the <code>$NAME</code> variable. So run: <code>sudo apt-get install jq</code></p>
<p>Once installed, create a file in your <code>/root</code> directory, I called mine <code>tailscale-cert.sh</code> this will need to be run via cron. Therefore, we need to create a cron job. Before you do that though, add the contents below to your <code>tailscale-cert.sh</code> file:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">#!/bin/bash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">NAME="$(tailscale status --json | jq '.Self.DNSName | .[:-1]' -r)"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tailscale cert "${NAME}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pvenode cert set "${NAME}.crt" "${NAME}.key" --force --restart</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-cron">Setting up cron<a href="https://mrpbennett.dev/blog/setting-proxmox-with-tailscale#setting-up-cron" class="hash-link" aria-label="Direct link to Setting up cron" title="Direct link to Setting up cron" translate="no">​</a></h2>
<p>Now let's change the permissions on our script and make it executable</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">chmod +x tailscale-cert.sh</span><br></span></code></pre></div></div>
<p>Now you have your file, let's set up a job to keep that cert up to date. From the terminal, enter edit mode for your user’s crontab using the following command:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">crontab -e</span><br></span></code></pre></div></div>
<p>The first time you run this command, the OS should ask you what editor you would like to use with a little menu like this:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">no crontab for user - using an empty one</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Select an editor.  To change later, run 'select-editor'.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  1. /bin/nano     &lt;---- easiest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  2. /usr/bin/vim.basic</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  3. /usr/bin/vim.tiny</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  4. /bin/ed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Choose 1-4 [1]:</span><br></span></code></pre></div></div>
<p>Being a <a href="https://lazyvim.org/" target="_blank" rel="noopener noreferrer" class="">LazyVim</a> user I of course picked 3. If you're using <code>vim</code> simply press <code>G</code> to get to the bottom of the cron page and enter the following:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">0 3 1 * * /root/tailscale-cert.sh</span><br></span></code></pre></div></div>
<p>This will run the script every month on the 1st at 3AM renewing your certificate.</p>
<p>For reference, here's how the cron syntax works:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">* * * * * command</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ │ │ │ │</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ │ │ │ └─── Day of week (0-7, where both 0 and 7 = Sunday)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ │ │ └───── Month (1-12)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ │ └─────── Day of month (1-31)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ └───────── Hour (0-23)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└─────────── Minute (0-59)</span><br></span></code></pre></div></div>
<p>Then again if you're using <code>vim</code> type the following command <code>:wqa</code> to save and quit all.</p>
<p>There you have it...Proxmox setup with Tailscale including a certificate.</p>]]></content:encoded>
            <category>homelap</category>
        </item>
        <item>
            <title><![CDATA[Exposing Kubernetes Services with Tailscale Funnel]]></title>
            <link>https://mrpbennett.dev/blog/exposing-with-tailscale-funnel-k8s</link>
            <guid>https://mrpbennett.dev/blog/exposing-with-tailscale-funnel-k8s</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I have been learning Go lately and wanted to try and expose a microservice using a Tailscale Funnel. I had a project inmind to build a small Go application that would build and deploy a Docker image to a private registry using a webhook. Therefore, I needed to figure out how to expose the application using Tailscale Funnel.]]></description>
            <content:encoded><![CDATA[<p>I have been learning Go lately and wanted to try and expose a microservice using a Tailscale Funnel. I had a project inmind to build a small Go application that would build and deploy a Docker image to a private registry using a webhook. Therefore, I needed to figure out how to expose the application using Tailscale Funnel.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-application">The application<a href="https://mrpbennett.dev/blog/exposing-with-tailscale-funnel-k8s#the-application" class="hash-link" aria-label="Direct link to The application" title="Direct link to The application" translate="no">​</a></h2>
<p>Although this is not the project in mind, this is the package I created to test. A simple HTTP server that prints "Hello, World!" when accessed.</p>
<div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// main.go</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">package</span><span class="token plain"> main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token string" style="color:#e3116c">"fmt"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token string" style="color:#e3116c">"log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token string" style="color:#e3116c">"net/http"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Starting server on port 8080"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">HandleFunc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">func</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">w http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ResponseWriter</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		fmt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Fprintf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">w</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Hello, World!"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ListenAndServe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">":8080"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Along with the following Docker image. Simply build it using the following Dockerfile then push it to a private registry.</p>
<div class="language-Dockerfile language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Use the official Go image as the base image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM golang:1.25.2-alpine AS builder</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Set the working directory inside the container</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">WORKDIR /app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Copy go mod and sum files</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY go.mod ./</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Download dependencies</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RUN go mod download</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Copy the source code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY main.go ./</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Build the application</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RUN go build -o webserver main.go</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Use a minimal alpine image for the final stage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM alpine:latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Install ca-certificates for HTTPS requests (if needed)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RUN apk --no-cache add ca-certificates</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Set working directory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">WORKDIR /root/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Copy the binary from builder stage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=builder /app/webserver .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Expose port 8080</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EXPOSE 8080</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Command to run the executable</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CMD ["./webserver"]</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-tailscale-in-kubernetes">Setting up Tailscale in Kubernetes<a href="https://mrpbennett.dev/blog/exposing-with-tailscale-funnel-k8s#setting-up-tailscale-in-kubernetes" class="hash-link" aria-label="Direct link to Setting up Tailscale in Kubernetes" title="Direct link to Setting up Tailscale in Kubernetes" translate="no">​</a></h2>
<p>Since I have a single <a href="https://k3s.io/" target="_blank" rel="noopener noreferrer" class="">k3s</a> node cluster running on a Raspberry Pi 5 currently (I will be overhauling my homelab very soon...keep your eyes on my blog for updates). I would have to run the <a href="https://tailscale.com/kb/1236/kubernetes-operator" target="_blank" rel="noopener noreferrer" class="">Kubernetes Tailscale Operator</a> to manage the Funnel. I won't go into too much detail here but you can find the <a href="https://tailscale.com/kb/1236/kubernetes-operator#prerequisites" target="_blank" rel="noopener noreferrer" class="">prerequisites</a> here. After you have set that up it's time to deploy the operator with helm.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">helm repo add tailscale https://pkgs.tailscale.com/helmcharts</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">helm repo update</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">helm upgrade \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  --install \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  tailscale-operator \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  tailscale/tailscale-operator \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  --namespace=tailscale \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  --create-namespace \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  --set-string oauth.clientId="&lt;OAauth client ID&gt;" \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  --set-string oauth.clientSecret="&lt;OAuth client secret&gt;" \</span></span><br></span><span class="token-line codeLine_lJS_" style="color:#393A34"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  --wait</span></span><br></span></code></pre></div></div>
<p>For the funnel feature to work you will need to add a <a href="https://tailscale.com/kb/1223/funnel#requirements-and-limitations" target="_blank" rel="noopener noreferrer" class="">node attribute</a> to allow nodes created by the Kubernetes operator to use Funnel:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token property" style="color:#36acaa">"nodeAttrs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tag:k8s"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// tag that Tailscale Operator uses to tag proxies; defaults to 'tag:k8s'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"attr"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"funnel"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Note that even if your policy has the funnel attribute assigned to <code>autogroup:member</code> (the default), you still need to add it to the tag used by proxies because <code>autogroup:member</code> does not include tagged devices.</p></div></div>
<p>Once the Operator is deployed and you have your policy set up configured, it's time to deploy the application.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploying-the-application">Deploying the Application<a href="https://mrpbennett.dev/blog/exposing-with-tailscale-funnel-k8s#deploying-the-application" class="hash-link" aria-label="Direct link to Deploying the Application" title="Direct link to Deploying the Application" translate="no">​</a></h2>
<p>As this is a simple application, the manifests are pretty straight forward. Here's an example of how you can deploy it:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 192.168.7.2</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">5000/websvrgo</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">0.0.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"32Mi"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100m"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"128Mi"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"500m"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token key atrule" style="color:#00a4db">metallb.io/loadBalancerIPs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 192.168.7.60</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> LoadBalancer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Ingress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> funnel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">tailscale.com/funnel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ingressClassName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tailscale</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">tls</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">hosts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">rules</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">host</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">paths</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">pathType</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Prefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">backend</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">service</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> websvrgo</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token key atrule" style="color:#00a4db">number</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><br></span></code></pre></div></div>
<p>The important manifest here is the ingress where we're exposing the host <code>websvrgo</code> to the internet. The two things to note is that we're using the <code>annotation</code> of <code>tailscale.com/funnel: "true"</code> and the <code>IngressClassName</code> of <code>tailscale</code>. This unsures we're exposing the ingress service with the tailnet.</p>
<p>This would deploy our application to the internet on the following:</p>
<div class="language-txt codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-txt codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">https://websvrgo.&lt;tailnet&gt;.ts.net</span><br></span></code></pre></div></div>
<p>So when I get round to things, I can set something similar to this up and have a webhook send <code>POST</code> requests to the url exposed by my tailnet.</p>]]></content:encoded>
            <category>kubernetes</category>
        </item>
        <item>
            <title><![CDATA[Using vim bindings for window swapping with Omarchy]]></title>
            <link>https://mrpbennett.dev/blog/window-swapping-with-vim</link>
            <guid>https://mrpbennett.dev/blog/window-swapping-with-vim</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I have been using Omarchy for a good while now at the time of writing this, my OS is 19 days old and I have gone many installs since v1. One thing I struggled with being on a keyboard without dedicated arrow keys was window navigation. With LazyVim being my main editor I have got use to using vim motions for navigation. Therefore behold my keybindings for window navigation in Omarchy.]]></description>
            <content:encoded><![CDATA[<p>I have been using <a href="https://omarchy.org/" target="_blank" rel="noopener noreferrer" class="">Omarchy</a> for a good while now at the time of writing this, my OS is 19 days old and I have gone many installs since v1. One thing I struggled with being on a keyboard without dedicated arrow keys was window navigation. With <a href="https://lazyvim.org/" target="_blank" rel="noopener noreferrer" class="">LazyVim</a> being my main editor I have got use to using vim motions for navigation. Therefore behold my keybindings for window navigation in Omarchy.</p>
<div class="language-conf codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-conf codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Unbind SUPER J &amp; K from toggle splitscreen, show key bindings, and map SUPER S to toggle splitscreen</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">unbind = SUPER, J</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">unbind = SUPER, K</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER, S, Toggle split, togglesplit # dwindle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Bind move focus keys and move window keys to use vim motions</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER, H, Move focus left, movefocus, l</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER, L, Move focus right, movefocus, r</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER, K, Move focus up, movefocus, u</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER, J, Move focus down, movefocus, d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER ALT, H, Move window to group on left, moveintogroup, l</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER ALT, L, Move window to group on right, moveintogroup, r</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER ALT, K, Move window to group on top, moveintogroup, u</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER ALT, J, Move window to group on bottom, moveintogroup, d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Bind SUPER SHIFT W to close(quit) windows, SUPER B to show key bindings, and unbind SUPER W so I don't accidently close a window when trying to open the browser.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">unbind = SUPER, W</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER SHIFT, W, Close active window, killactive</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER, B, Show key bindings, exec, omarchy-menu-keybindings</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Swap active window with the one next to it with SUPER + SHIFT + vim motions</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER SHIFT, H, Swap window to the left, swapwindow, l</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER SHIFT, L, Swap window to the right, swapwindow, r</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER SHIFT, K, Swap window up, swapwindow, u</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bindd = SUPER SHIFT, J, Swap window down, swapwindow, d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>]]></content:encoded>
            <category>linux</category>
            <category>omarchy</category>
        </item>
        <item>
            <title><![CDATA[Gateway API migration]]></title>
            <link>https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx</link>
            <guid>https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[As of now Ingress NGINX is being retired, sad times all around. As per the blog post Ingress NGINX Retirement]]></description>
            <content:encoded><![CDATA[<p>As of now Ingress NGINX is being retired, sad times all around. As per the blog post <a href="https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/" target="_blank" rel="noopener noreferrer" class="">Ingress NGINX Retirement: What You Need to Know</a> it mentions:</p>
<blockquote>
<p>Existing deployments of Ingress NGINX will continue to function and installation artifacts will remain available.</p>
</blockquote>
<p>But where would the fun be in that? Although I can tell you now, I should have left my setup the way it is... but nope, this is me migrating to the Gateway API. So let's get started!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-gateway-api">Why Gateway API?<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#why-gateway-api" class="hash-link" aria-label="Direct link to Why Gateway API?" title="Direct link to Why Gateway API?" translate="no">​</a></h2>
<p>Before diving into the migration, let's understand why Gateway API is a significant improvement over traditional Ingress resources:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="benefits-of-gateway-api">Benefits of Gateway API<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#benefits-of-gateway-api" class="hash-link" aria-label="Direct link to Benefits of Gateway API" title="Direct link to Benefits of Gateway API" translate="no">​</a></h3>
<ol>
<li class="">
<p><strong>Better Separation of Concerns</strong>: Gateway API separates infrastructure concerns (handled by platform teams) from application routing (handled by development teams). This is achieved through different resource types like <code>Gateway</code> and <code>HTTPRoute</code>.</p>
</li>
<li class="">
<p><strong>More Expressive Routing</strong>: Unlike Ingress which has basic path and host-based routing, Gateway API supports advanced features like:</p>
<ul>
<li class="">Header-based routing</li>
<li class="">Query parameter matching</li>
<li class="">Weighted traffic splitting</li>
<li class="">Method-based routing</li>
</ul>
</li>
<li class="">
<p><strong>Standardized Across Controllers</strong>: Gateway API provides a consistent interface across different ingress controllers (Envoy, NGINX, Istio, etc.), reducing vendor lock-in.</p>
</li>
<li class="">
<p><strong>Enhanced Multi-tenancy</strong>: Clear role-based access control with different resources for different personas in the organization.</p>
</li>
<li class="">
<p><strong>Built-in Extensibility</strong>: Designed with extension points for custom functionality without breaking the core API.</p>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ingress-vs-gateway-api-comparison">Ingress vs Gateway API Comparison<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#ingress-vs-gateway-api-comparison" class="hash-link" aria-label="Direct link to Ingress vs Gateway API Comparison" title="Direct link to Ingress vs Gateway API Comparison" translate="no">​</a></h3>
<table><thead><tr><th>Feature</th><th>Ingress</th><th>Gateway API</th></tr></thead><tbody><tr><td><strong>API Maturity</strong></td><td>Stable (v1)</td><td>Stable for core features (v1)</td></tr><tr><td><strong>Routing Capabilities</strong></td><td>Basic (host/path)</td><td>Advanced (headers, methods, weights)</td></tr><tr><td><strong>Role Separation</strong></td><td>Single resource</td><td>Multiple resources (Gateway, HTTPRoute)</td></tr><tr><td><strong>Protocol Support</strong></td><td>HTTP/HTTPS only</td><td>HTTP/HTTPS, TCP, UDP, gRPC</td></tr><tr><td><strong>Traffic Splitting</strong></td><td>Controller-specific annotations</td><td>Native support</td></tr><tr><td><strong>Cross-namespace Routing</strong></td><td>Limited</td><td>Built-in with proper RBAC</td></tr><tr><td><strong>Vendor Portability</strong></td><td>Controller-specific features</td><td>Standardized API</td></tr><tr><td><strong>TLS Configuration</strong></td><td>Basic</td><td>Advanced (SNI, multiple certs)</td></tr><tr><td><strong>Backend Configuration</strong></td><td>Limited</td><td>Rich (retries, timeouts, load balancing)</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started-with-the-gateway-api">Getting started with the Gateway API<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#getting-started-with-the-gateway-api" class="hash-link" aria-label="Direct link to Getting started with the Gateway API" title="Direct link to Getting started with the Gateway API" translate="no">​</a></h2>
<p>For the migration I picked <a href="https://gateway.envoyproxy.io/" target="_blank" rel="noopener noreferrer" class="">Envoy Gateway</a> as the documentation seemed to be the best, and the application seems to be well liked within the Kubernetes community so let's get cracking. I'm going to deploy this with an apps of apps pattern using ArgoCD, which allows me to deploy more manifests than just the Helm chart, enabling more modular deployments.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-app-entry-point">The App entry point<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#the-app-entry-point" class="hash-link" aria-label="Direct link to The App entry point" title="Direct link to The App entry point" translate="no">​</a></h3>
<p>First I went ahead and created the application's entry point. This is picked up by my registry app which populates all other apps.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argoproj.io/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Application</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argocd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">argocd.argoproj.io/sync-wave</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'0'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">finalizers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> resources</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">finalizer.argocd.argoproj.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">project</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">source</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">repoURL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://github.com/mrpbennett/home-ops.git'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubernetes/apps/envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway </span><span class="token comment" style="color:#999988;font-style:italic"># this points to the directory where the manifests are stored.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">targetRevision</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HEAD</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">directory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">recurse</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">destination</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">server</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://kubernetes.default.svc'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">syncPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">automated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">prune</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">selfHeal</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">syncOptions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> CreateNamespace=true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">retry</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">limit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">backoff</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">duration</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 5s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">maxDuration</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 5m0s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">factor</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="app-directory-structure">App directory structure<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#app-directory-structure" class="hash-link" aria-label="Direct link to App directory structure" title="Direct link to App directory structure" translate="no">​</a></h3>
<p>Next, the applications directory needs to be created. I went with the following setup:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">📁 envoy-gateway </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── 📁 helm-chart</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│&nbsp;&nbsp; └── envoy-gateway-helm.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── 📁 resources</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── gateway-class.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── https-gateway.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    └── wildcard-certificate.yaml</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploying-the-helm-chart">Deploying the Helm chart<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#deploying-the-helm-chart" class="hash-link" aria-label="Direct link to Deploying the Helm chart" title="Direct link to Deploying the Helm chart" translate="no">​</a></h3>
<p>If you read the <a href="https://gateway.envoyproxy.io/docs/tasks/quickstart/" target="_blank" rel="noopener noreferrer" class="">quick guide</a>, it suggests you install via Helm. I headed over to the Install section instead and read through <a href="https://gateway.envoyproxy.io/docs/install/install-argocd/" target="_blank" rel="noopener noreferrer" class="">https://gateway.envoyproxy.io/docs/install/install-argocd/</a> where it gives you a basic manifest.</p>
<p>Here mine is slightly tweaked. I added some of the default Helm <a href="https://gateway.envoyproxy.io/docs/install/gateway-helm-api/" target="_blank" rel="noopener noreferrer" class="">chart values</a> just because I wanted to see them in case I wanted to make a change. I also changed the service type to <code>LoadBalancer</code> as I am using bare-metal and <a href="https://metallb.io/" target="_blank" rel="noopener noreferrer" class="">MetalLb</a>.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argoproj.io/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Application</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">helm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argocd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">project</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">source</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">helm</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">valuesObject</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">deployment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">envoyGateway</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1024Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 100m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 256Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Set type: to LB so it can talk with MetalLb</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">service</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">trafficDistribution</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> LoadBalancer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">config</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># -- EnvoyGateway configuration. Visit https://gateway.envoyproxy.io/docs/api/extension_types/#envoygateway to view all options.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">envoyGateway</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">gateway</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">controllerName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.envoyproxy.io/gatewayclass</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">controller</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">provider</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Kubernetes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">logging</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">level</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">default</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> info</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">extensionApis</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">createNamespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">kubernetesClusterDomain</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cluster.local</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">chart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">helm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">helm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">repoURL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> docker.io/envoyproxy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">targetRevision</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1.6.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">destination</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">server</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//kubernetes.default.svc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">syncPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">syncOptions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> CreateNamespace=true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ServerSideApply=true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">automated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">prune</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">selfHeal</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Set <code>ServerSideApply</code> to true to enable Kubernetes server-side apply. This helps avoid the 262,144-byte annotation size limit.</p></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-steps">Validation Steps<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#validation-steps" class="hash-link" aria-label="Direct link to Validation Steps" title="Direct link to Validation Steps" translate="no">​</a></h4>
<p>After deploying the Helm chart, verify the Envoy Gateway installation:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Check if the Envoy Gateway pods are running</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify the GatewayClass is available</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get gatewayclass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check Envoy Gateway controller logs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n envoy-gateway-system deployment/envoy-gateway -f</span><br></span></code></pre></div></div>
<p>Expected output should show the <code>envoy-gateway</code> pod in <code>Running</code> state and the controller logs should indicate successful startup.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="adding-extra-resources">Adding extra resources<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#adding-extra-resources" class="hash-link" aria-label="Direct link to Adding extra resources" title="Direct link to Adding extra resources" translate="no">​</a></h3>
<p>Below are the extra manifests I deployed alongside my Helm chart. Here, I am creating a <code>GatewayClass</code>, <code>Gateway</code> and <code>Certificate</code>. Instead of using annotations on the Gateway (which can cause conflicts), I created an explicit <code>Certificate</code> resource, which is then handled by cert-manager.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> GatewayClass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gatewayclass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">controllerName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.envoyproxy.io/gatewayclass</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">controller</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">shared</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">gatewayClassName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gatewayclass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">listeners</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">443</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'*.70ld.dev'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">tls</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Terminate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">certificateRefs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> wildcard</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">70ld</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">tls</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">manager.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Certificate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> wildcard</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">70ld</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">certificate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">secretName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> wildcard</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">70ld</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">tls</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">issuerRef</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> letsencrypt</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cloudflare</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dns</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">issuer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ClusterIssuer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">dnsNames</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'*.70ld.dev'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'70ld.dev'</span><br></span></code></pre></div></div>
<p>With the Gateway, the key points about this configuration are:</p>
<ul>
<li class="">Uses <code>Terminate</code> mode for TLS termination at the gateway</li>
<li class="">References the Secret created by cert-manager</li>
<li class="">Restricts to our domain with the <code>hostname</code> field</li>
</ul>
<p>Now this can be committed and ArgoCD will deploy the manifests for me. Let's check some things before moving forward.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-steps-1">Validation Steps<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#validation-steps-1" class="hash-link" aria-label="Direct link to Validation Steps" title="Direct link to Validation Steps" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Verify the GatewayClass is accepted</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get gatewayclass envoy-gatewayclass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status should show ACCEPTED=True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check Gateway status</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get gateway envoy-shared-gateway -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status should show READY=True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify the certificate is ready</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get certificate wildcard-70ld-certificate -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status should show READY=True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check the LoadBalancer service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get svc -n envoy-gateway-system</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="service" src="https://mrpbennett.dev/assets/images/svc-a9efffb904fadcf14591be92dff0e80e.png" width="3837" height="794" class="img_ev3q"></p>
<p>Here we can see that the <code>LoadBalancer</code> service was given the external IP <code>191.168.7.53</code> for our service. This will allow us to create DNS records. ✅</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-cert-manager">Setting up Cert Manager<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#setting-up-cert-manager" class="hash-link" aria-label="Direct link to Setting up Cert Manager" title="Direct link to Setting up Cert Manager" translate="no">​</a></h2>
<p>I currently have cert manager setup via manifests, but after reading <a href="https://cert-manager.io/docs/usage/gateway/" target="_blank" rel="noopener noreferrer" class="">https://cert-manager.io/docs/usage/gateway/</a>, it seems the better way to install it would be via Helm. The reason for Helm is because, for me, it's easier to see what I am changing. I'll show this below.</p>
<p>Cert-manager can generate TLS certificates for Gateway resources. This is configured by adding annotations to a Gateway and is similar to the process for Securing Ingress Resources.</p>
<p>The Gateway resource holds the TLS configuration, as illustrated in the following diagram (source: <a href="https://gateway-api.sigs.k8s.io/" target="_blank" rel="noopener noreferrer" class="">https://gateway-api.sigs.k8s.io</a>):</p>
<p><img decoding="async" loading="lazy" alt="tls-gateway" src="https://mrpbennett.dev/assets/images/tls-gateway-c6028906a998abc2d827b5d37d02fe97.png" width="954" height="454" class="img_ev3q"></p>
<p>Since we have applied the Gateway controller and Gateway Class above, we can install Cert Manager via Helm. Since I am using ArgoCD, this is how I would deploy it. Doing it this way allows us to enable the GatewayAPI by setting some values. As well as providing some extra manifests, for example below I have a <code>ClusterIssuer</code> and the <code>Secret</code> for my Cloudflare API key:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argoproj.io/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Application</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argocd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">project</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">sources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">chart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">repoURL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> oci</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//quay.io/jetstack/charts/cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">targetRevision</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1.19.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">helm</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">valuesObject</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">crds</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">config</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> controller.config.cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">manager.io/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ControllerConfiguration</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">enableGatewayAPI</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">extraObjects</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              apiVersion: v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              kind: Secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                name: cloudflare-api-token-secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                namespace: cert-manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              type: Opaque</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              data:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                api-token: ZGgxxx</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              apiVersion: cert-manager.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              kind: ClusterIssuer</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                name: letsencrypt-cloudflare-dns-issuer</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                acme:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                  server: https://acme-v02.api.letsencrypt.org/directory</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                  email: p@mrpbennett.dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                  privateKeySecretRef:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                    name: letsencrypt-cloudflare-dns-issuer-secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                  solvers:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                    - dns01:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                        cloudflare:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                          apiTokenSecretRef:</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                            name: cloudflare-api-token-secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                            key: api-token</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">destination</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">server</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//kubernetes.default.svc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">syncPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">automated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">prune</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">selfHeal</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">syncOptions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> CreateNamespace=true</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-steps-2">Validation Steps<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#validation-steps-2" class="hash-link" aria-label="Direct link to Validation Steps" title="Direct link to Validation Steps" translate="no">​</a></h4>
<p>After deploying cert-manager, verify the installation:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Check cert-manager pods are running</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n cert-manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify the ClusterIssuer is ready</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get clusterissuer letsencrypt-cloudflare-dns-issuer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status should show READY=True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check cert-manager logs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n cert-manager deployment/cert-manager -f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify Gateway API support is enabled</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n cert-manager deployment/cert-manager | grep -i gateway</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="external-dns">External DNS<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#external-dns" class="hash-link" aria-label="Direct link to External DNS" title="Direct link to External DNS" translate="no">​</a></h2>
<p>Next I had to make sure External DNS worked with the new Gateway implementation. This was pretty simple - just a few changes were needed here. I installed External DNS to be used with <a href="https://pi-hole.net/" target="_blank" rel="noopener noreferrer" class="">Pi-hole</a>. The tutorial can be found <a href="https://kubernetes-sigs.github.io/external-dns/v0.15.0/docs/tutorials/pihole/#externaldns-manifest" target="_blank" rel="noopener noreferrer" class="">here</a>. This worked well with my Ingress setup, but I needed to make some changes to the <code>deployment</code> and the <code>clusterrole</code>. Those changes are as follows:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rbac.authorization.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ClusterRole</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> external</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">rules</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">apiGroups</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"services"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"endpoints"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"pods"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">verbs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"get"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"watch"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"list"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">apiGroups</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"extensions"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"networking.k8s.io"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ingresses"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">verbs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"get"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"watch"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"list"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">apiGroups</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"nodes"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">verbs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"list"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">"watch"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">## add the following:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">apiGroups</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'gateway.networking.k8s.io'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'gateways'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'httproutes'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'tlsroutes'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'tcproutes'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'udproutes'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">verbs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'get'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'watch'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'list'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">apiGroups</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'discovery.k8s.io'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'endpointslices'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">verbs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'get'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'watch'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'list'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">apiGroups</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">''</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'namespaces'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">verbs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'get'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'list'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'watch'</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>Then add the following flag to the <code>args:</code> within the deployment: <code>--source=gateway-httproute</code> so it can check Gateway HTTP route sources. With the following changes, this should generate a DNS record within Pi-hole.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-steps-3">Validation Steps<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#validation-steps-3" class="hash-link" aria-label="Direct link to Validation Steps" title="Direct link to Validation Steps" translate="no">​</a></h4>
<p>Verify External DNS is working with Gateway API:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Check External DNS pods</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n external-dns</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># View External DNS logs to see if it's picking up Gateway resources</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n external-dns deployment/external-dns -f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify it can see the Gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n external-dns deployment/external-dns | grep -i gateway</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hooking-it-all-up">Hooking it all up<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#hooking-it-all-up" class="hash-link" aria-label="Direct link to Hooking it all up" title="Direct link to Hooking it all up" translate="no">​</a></h2>
<p>Everything should be deployed and set up correctly to accept some HTTP routes. Here is a test deployment to check that everything is working fine.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">alpine</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 10m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 16Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 100m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 128Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TCP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ClusterIP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gateway.networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HTTPRoute</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">route</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">parentRefs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">shared</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> envoy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gateway</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">hostnames</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'test-new.70ld.dev'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">rules</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">matches</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> PathPrefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">value</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">backendRefs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testapp2</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-steps-4">Validation Steps<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#validation-steps-4" class="hash-link" aria-label="Direct link to Validation Steps" title="Direct link to Validation Steps" translate="no">​</a></h4>
<p>Before checking Pi-hole, let's validate the HTTPRoute deployment:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Check if the test deployment is running</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get deployment testapp2 -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify the service is accessible</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get svc testapp2-service -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check HTTPRoute status</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get httproute testapp2-route -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status should show ACCEPTED=True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Describe HTTPRoute for detailed status</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl describe httproute testapp2-route -n envoy-gateway-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Test internal connectivity (from within cluster)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl run test-pod --image=curlimages/curl -i --tty --rm -- curl -k https://test-new.70ld.dev</span><br></span></code></pre></div></div>
<p>Once the test application has been deployed, it's time to head over to my Pi-hole instance to check if the DNS record has populated. BOOM! It worked...</p>
<p><img decoding="async" loading="lazy" alt="pihole" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtUAAABSCAYAAACbmSgiAAAU5klEQVR4nO3dB3CU1frH8ScFkhBKpEiTq4KIImIvqNj9Y6+IHbtgH3VkdLj2jl4d5yrKFUWxXHVG7F0BC6CCiqgUFbGCSDGAaRCS//s7l5P7ZtmWvEkubr6fmQzJZnff3Ze85zznOc85m9un3/bV1kTaFrW3hlJdXW279N/KJn4wzepDr2Vl8XIDgMZGewMgqrq2I71yqmxUq3LLKyy07JZ5ZllZ1uCCWKxqdYVVlJTYiNJ8m782O+2H1rdd3H/gAJs+a27wdhrh/USUawAAAMgoCnAV6J5ZVW5bZ5dYjjV8DnWtZdmcqhwbV163gDpTEVQDAABkIAW6fy/JMzQNgmoAAAAgIoJqAAAAICKCagAAACAigmoAAAAgIoJqAAAAICKCagAAACAigmoAAAAgIoJqAAAAICKCagAAACAigmoAAAAgomYdVLctam8A0BRobwBERTuyYWvWQXV5ebkBQGPLz8+nvQEQSaa1I3o/mYbyDwAAACAigmoAAAAgIoJqAAAAICKCagAAACAigmoAAAAgIoJqAAAAICKCagAAACAigmoAAAAgIoJqAAAAICKCagAAACAigmoAAIAM1DOr0obmllgfW2M5Vm0Nba1l2TxrYeMrC+37akJKzgAAAECGUUB9S26x5RUWWnbLNmZZWdbgqqut/+oKu6Wk2EZWFjX7wDrbAAAAkFGUoXYBdV5+4wTUEjyvnl/H0fGaO4JqAACADKOSj+yWedYUdBwdr7kjqMYGpWPHjrZ1376Wk5OT8r5bbbWVde3a1eqqVatW7hjt2rUzAAAykauhbqwMdazgOI1Rs/1XQ1CNDcquu+5qw4cNs/z8/JT3Pfe882y//fazuurevbs7xpZbbmkAADR3Oe07Wtfb7rOeL0+xnq9MdV+bPf2mtTvieEP6WKhYB3vuuaeVlpba559/bg3tiCOOsC+++MJ++ukn+6s5Lwhut9lmm7i/q6qqsssuu6zm5379+tn+++/vMszfffedvfzyy/b7778bgNratm3rBpl77LGHbbTRRrWuI0+zNYcccoh169bNysvLbf78+fbss8+6dsrr1auX7b333rb55ptby5Yt7ZdffrHJkyfbV199lfT4WUHmqU+fPjZgwADr37+/TZw40V2vsTQAPm7wYOu9RW9r06a1FRcX20cffWRvv/12zX30PGeccYabJZKSkhJ75JFHXBuQyG233VZz/1jff/+93XvvvXF/p/d7ySWXrHe73vedd97pvm/durUde+yx7pwUFRVZdna2/fzzzzZmzBhbtWqVAc1Np4tGWMG2O9qqN1+yqvIyd1tB/52sw3mXWsX8eVY+e5YhNYLqOlDnsui33xo8qG7RooUdeOCBtmTJkr9kUK3BwG/BeYk1cOBAmxn8zttll13s5JNPtnfffdeefvppO/zww+2KK65wnePChQsNwH8NPf30IFDdwn2vwWmsjTfe2M4++2x3/SlA7dSpk7u+Lr30Uhs1apStXbvWDWJ1HwW5zz//vHXo0MF23nlnO/Oss+wfd92V9LrbIjj2+eefb6mceeaZtkmPHjZt6lT79ddfbbPNNnPH0DEVoLZp08YF1N988429/vrrlpeXZyeccIKdHry/m266yVavXh33ed9//33XNoYpGN5tt93ccyfiA3HdR8G7t2LFiprvBw0aZFtvvbU7xqJFi6x379622+67uferwLu6mmlsZLaslnlWNPhUyy5s7b5a7bqXVS5eVBNQi4LplptvYZ0uHWmlM6a620pnTLOyzz8xxEdQXQeJsiaiTIc6Od1HGZF4HUVBQYErPfjjjz9s2bJlNber04lik002sbKyslrPGaaOScdds2aN60DiddBRfPzxx+vdtu+++7rX9Owzz9Tcpgz1zJlf2CuvvOJ+HjdunF1zzTU2MMiiPRME2amoQ+3SpYt7D+osq4KgIV2q1VYNtbJRyd5/Y58rIF33/fOf7t/jjz/eZatjadBaWVlpzwTXmP5eFTQ++uijdvnll7sM97Rp01wgO3bsWPv6669rHjdjxgy74YYb3OxSsqD622+/dQG62q3bb7897n2UwVa2/O6777Yff/zR3RabdFBgr3bxueees5UrV7rb9Jr1OpXB/vLLL+M+twLwWH4QEa/N8Xw7/dprr9UKpMNeeukle+utt2qy0jNnznTnUu1W+/btE7alQKZo0aWbtTv2ZMvOL6i5LbdzV2t31Anr37d7D2vX/T+3Z7cqJKhOgqA6DTvssIPLtIgyPbsGGVe58sorXfDcI8jSKFuj34myHG+88Yb78k466STX0Sn4FgXWDz74oOsY99prr5r76Cs8TRnPRRdfbH8sX25z5syxI488KpgaLnK3z5s3zz1nOBBUxmjIkCEuOyR6vcpq6bEKHkeMGGETJkyw9957r+Yxt956q+tgrr322prb9DqPO+44GzlyZK3sTzwKYA877DDXcamzFw04NEWtjszTeZo9e7Ztv/329kyS59M503vQTIGn95pONkkDlnPPPdc23XRT97POjY4ZT7JzpWOfeOKJNnr0aHfs8GvT+frss8/ctDvQVFRCpcywv8ZEM10qp1Kgq6Baf8PhgFpUnlFRUeHKHqJS26WSEx9Qx+OvUwW7Pqj2M1t1GbTq+lSAfscddyS9n46jY/pjxaNzFj5v4hMhsbcDmWj1TwtsyV03WOe/326Lb77KSj56P+n9u9891v275N5bDYnllpU23b6CbYva21+R6v4eHDPGzgmyJOpAJk2e7G5X4Kmgbfjw4UFn9rPdcsstrkE/4IAD7OCDD3aZHt1ftdi77767PfbYYy6gUxC+00472fIgMP7ggw9s6dKldvTRR9ukSZNsXpBZKi8rS/malCFSlkeBq45z6KGHumNoAPDpp5+6++j3p512mgvulfVRUHvMMcfYKaec4l6rOmR1PJrm9UG1Am0FiqrnVFbYd36qU/QZ4lQGBe+9NHgPU6dOrblNQbXomGGLFy9251CBrDr6eFQmoqD2zTffdOdLGWfViPrgN5lzzjnHBR/jx4+vOfc6V7FSnSudU9Vg7rjjjrWC6m233dZl8pT9A5qS6qZV8hFLWdZ4t3sa/OvaaYi1DLqePvzwQ/e92j5d55oNWhuaRVJmWQuKlWV+6KGH3HFVCqYsstqudKi+W9etstrxSs3C9DrUTqnN0HtVOzt37tyE2Wc9t9pjfc2ePSdpMA5kquw2bV1NdcnUyVa4RzDT/OVn7nZ/G9LD7h9p0BThnCAgU1ZlRdDg6nv/swJoZUWeeupJ15Hovi+88ILrOHZfl1nVdKLuq4Zdi4nUkaimWJkRdRC+jlrf63kXLFiQ8jUpm6JstoI5dU5+qjTcmSpbrGP532mq94knnrDCwkIXlMvcIEDUYh1PdYZaBKRsub73evbsmVYHqEVLOwaB/SfB9Gy4Y/VZsbKYAYNfUOWz/LHUQe6zzz4uINZ0rs6vXttTTz1llSnKP5TVUn2ngnEFxTq2psNfffXV9e6b6lzp/+qrr762fkEQnRXaokiDGM066JwBTUl/rxr8KYMrGtypxEqD5JZJBpwHH3KICxyVyY5CgbmuTw2GTz/9DDf41GLKm2++2bbbbrua++m60wyP2oCrr77azRzpenv88ccT1lPH6tu3r2sjfACfjJICum6VZFCJi8pnNMOmGbFYGjTfeOONbkCthZtjxz5kQHNUsM32rsZa9K9+Dt+G9FD+EZEWuGiBoRbqhCnQ7bwuO6vSgH2DTM2FF17oMq3K3JSlyEars1IgGxZera9MTHiVus+u+HpCdXgqeVAnpD2Zw/7888+a/Z3nzpnjylmUoVYWWe9HAaI6QG05p+x5586dXXY4nKFNREFmbm6uTZ8+Pe7v09l/OkwZcj2fz77Xhd6LJFvUJOmeqxkzpgeZ6h1cR63/C70Xfa/FTkBTmzJlim0XBIoKCLUDiGaXVIahwbtKsOLRNa3r3Q/qo/DbXmqRtQaqEyY859oNBfYKVtWOqI1SgDts2LBgxm9+MHP3nasPV9CvwXy6mWq1K2pTNShORWs2NGDQDKCoTVT5nl6TZg7D7aYG6PpZ61K0AFLfh3ctAYC6IKiOqLCwtW20UQs7eNCg9X7npykVrI664w63bZ5qpo866igXrL7zzjsJawqVgVInEKaV8uGtssL88/gsql/8qGyzOowwTYeqrlJmr8u4q/xB2Vl1dKp7VnZdGR49nzpiZcbVWaey5ZZ9gsxt8XpTy37BkDpYf2zxgwBle+PR4kSpzzZXOpbemwLjZOpyrjR4UQevoFqZL21RFi5zAZqK/rbvv+8+V4Kk9sKXm2k7uT9L1v+b18BYAbiu46hZavGJAT2XkgWi61RrC7SwUdlqDVSVlVb78a9/jXGzetqaTwsC1Q5qli6ddkUDZL23dOgYPqAWtZka+KqNUxY/vJAyvJZEwb52JVH5SjqvCQBiEVRHVFpaEmSNq+2ee+5Jej/VDmsVvjLCew0c6DoaCS/cC1Mdt6ZK60uZbHUuWqSkVfeJqGNUJ6IyCZ8ZV/mJBgR567JJPXv2sgU/LKhVzpFIt25dXe11LJ9JV9AarqtWFlwZs0SZ++J1wXh9dkjRMTUVrMcmC8rTPVe6jzpkP92uYEbnjp0C8L+iv8lZs2a5L9Hfu9qY2NkZDU41U1YRXGta29EQdN3qGssvKKh1u65l/U57a4uy6ZOCQNovWNRgQIG1yjP22WfflAGsBsfKgEfZdlNld5JsHYZm1xRUa/aJoBpAfVBTXQfqFHKya58yBb/qxNL9uGwFlNo+TkGrShvEB6t1LY1IRh2bpjZVD5zqeTWlqvIHZXL0ftTpKdDU47WLQM9e6dVTK6vdoWOHILO7ftZZz6WMUfhDYnR/1Uomq0deti7j1DemLENb32Wn+PhVn/329eOessthdTlX2spLnbzOV+8gg98YHwQE1Jd241BZhg+yRdeKFlMroHxg9OiEs131oXahX3BNh68b1T7rGvPXX866gW0sPSadT1BWFl40a5SKntOXfYVpfYX4Ab/atVh+EFCWxkJxAIiHoLoO1Khr+lBZXZUtKCuk+ruSoJPShy74jkP1jNqaTdOtot+p9MP/rGytVsn7bag0ValAVqvP1bD7koeoVFuoDI+yL34rPwXyKusIU4ZW91M5Q7hmUd+r1KEoeN1zZs+puV1b+mm7vdh9u1XT2SK3RdyssAYOCkg1JazaRXV+mv5VB+ynjkU7qog/VyojUcetc3PQQQe5xVh6rRcHryE7ZoCjoFjTzrqfqJZdr0XnXsfVMfX+tTVefc+VBkXqmDXT0Cp4LYlqx4Eo1Jaovl9f+rvUANT/7Bf1aucNBcu+flrlC9qxRms2fKmEHqu9pv12lhsHM0P+efRVsC7LrGtEn2CoUi9Ps1S6j79NbZN+1uDb0245CuK1y45KxvTaVL+sxYt+wKnab13zPrAV7eSh1+3LUPQ6tWBQixhj6Xkl3p7TsY9TG3DBBRe4nXrUTuua184j2hL0hx9+cG2uBsV6vRdddJG7vwYbanOHBNe+Xvcnn7AHL4D6ofyjDlQDrYDMf1ywSj7UUI++/34bOnSoW/XuKePrpxrVwSmA1IIeT/W5ej5Rza8CS+1ycf3117uG/aqrror8wSOawtSHQSjAV6fmTQ06MmWS/EIllXtoelSlGOHFkHq8Fh0pa60yB08LMNUJ6TnCWS8/qPBTrbG0/V/3Tbq7QYa+RLtthI+prLVmBFQXqnMgDz/8sNuOS1vr6UslIR/HWXyoTl9Bgt8BRa/7gQcecB3oWWed5W5TUPziiy/aqaeeWq9zJQoWFBR88+03bL+FRqFSLP836w0fNsz9q0WBCpBVp6y/dX2AkujvXbsBhfdL19aTPdYtotbfdizfhul5NEj2g1kZPHhwrTUGfts5tU/a31406H3yySfd+o/rrrvO3aY2YVxwLfntNzVg1XWpa9gPWHU9aZekL9Z94qp+r2PH21ZTQbDEa1diH6drU1uBKpBWe+rpWn3s0cdqzpNKYDQA0WvytBZEH1MerscGmou1xcutctkS973+1c/+e6Qv6289ezfZ57F27tbDGooCr136b2UTP6jfghvtmZ0o+Ev6uCAbq1IPBaLhQEudhTI7WtVeGjTasR+GoClY/T4nN9eWLlnidgyJpc5AHYieuyE/yU9BoTrprOA1Lgk6waidhoJvDRjq+5HqylApw6Q683hZbZVW6DXHlpwoC6ZgPtEHTej/QFk0BQnhKVzdrnOrLHiqvXkb+lwBomxufdqbVPzsi9oiXRf1/XhtDYiVzdbsVH2eQwH5pkFmuDpot3Tdxlt/Eb6PBs+xu4+ofdQgta5rFBI9TsG22mrdHq+9FbVjGnToPae7EBL4X6lrO/JsiyXWsn3HhL9vtfMA63L9P2zxbSOtZMqkZE9l3e8dZ9UV5bZwxPkJ77N6+VIbsqaTpUvvZ2Vx6rKuWPsPHGDTZ82ttb3thoKgGgAaWWMF1QCaj4YOqnPaFdnfHn7O1iz8pebDXuLRR5m3+b9glnjCv235uPsT3o+gmvIPAACAZmftimJbPOpaaz90uLU7coglWjlcVVpiqya+YcX/fsSQHEE1AABAM1T6yRT3hYZBUA0AAABERFANAAAARMQ+1QAAABlmrWVpVwdrEsFx3PGaOYJqAACADDPPWljV6gprCjqOjtfcEVQDAABkmPGVhVZRUmJVFeWNl7EOnlfPr+PoeM0dNdUAAAAZ5vvqXBtZWWRDg4C3T/CVYw0fWKvkQxnq8cFxdLzmjjMAAACQgRToXr+mnaFpEFQDAAAAERFUAwAAABERVAMAAAAREVQDAAAAERFUAwAAABERVAMAAAAREVQDAAAAERFUAwAAABERVAMAAAAREVQDAAAAETXroDo/P98AoCnQ3gCIinZkw9asg+qVxcsNABpb26L2tDcAIsm0dkTvJ9NQ/gEAAABERFANAAAARERQDQAAAEREUA0AAABERFANAAAARJRb0KrQAAAAANQfmWoAAAAgIoJqAAAAICKCagAAACAiguo0bNyluwFA2O+//WqNgfYGaF4aqy1B0yOoTlN5eZkBgOTnF1hjor0BmofGbkvQtAiqAQAAgIgIqgEAAICICKoBAACAiAiqAQAAgIgIqgEAAICI/h8puGWstBu/wAAAAABJRU5ErkJggg==" width="725" height="82" class="img_ev3q"></p>
<p>Awesome! This is good news - we can see it's pointing to the IP address of the <code>LoadBalancer</code> service. The IP address was handed out from a pool given to MetalLB.</p>
<p>Now let's check if the site actually loads...</p>
<p><img decoding="async" loading="lazy" alt="testdeploy" src="https://mrpbennett.dev/assets/images/nginx-502f9b573704e7e7a28c5a62728bd942.png" width="1918" height="552" class="img_ev3q"></p>
<p>Beautiful! It loads and we're getting HTTPS with no certificate warnings.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-validation">Final Validation<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#final-validation" class="hash-link" aria-label="Direct link to Final Validation" title="Direct link to Final Validation" translate="no">​</a></h4>
<p>To ensure everything is working correctly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Test the complete flow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -v https://test-new.70ld.dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check certificate details</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">openssl s_client -connect test-new.70ld.dev:443 -servername test-new.70ld.dev &lt; /dev/null 2&gt;/dev/null | openssl x509 -text -noout | grep -A2 "Subject:"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Verify DNS resolution</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nslookup test-new.70ld.dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Check Gateway metrics (if available)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward -n envoy-gateway-system svc/envoy-gateway-metrics-service 19001:19001</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Then visit http://localhost:19001/stats in your browser</span><br></span></code></pre></div></div>
<p>The migration is now complete! 🎉</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lessons-learned-">Lessons Learned 💡<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#lessons-learned-" class="hash-link" aria-label="Direct link to Lessons Learned 💡" title="Direct link to Lessons Learned 💡" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-went-wrong">What Went Wrong<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#what-went-wrong" class="hash-link" aria-label="Direct link to What Went Wrong" title="Direct link to What Went Wrong" translate="no">​</a></h3>
<ul>
<li class=""><strong>Complexity</strong>: Gateway API has more moving parts than Ingress</li>
<li class=""><strong>Learning Curve</strong>: New concepts like parentRefs and listeners took time</li>
<li class=""><strong>Migration Effort</strong>: Converting all existing applications was time-consuming</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-id-do-differently">What I'd Do Differently<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#what-id-do-differently" class="hash-link" aria-label="Direct link to What I'd Do Differently" title="Direct link to What I'd Do Differently" translate="no">​</a></h3>
<ul>
<li class=""><strong>Gradual Migration</strong>: Run both systems in parallel longer</li>
<li class=""><strong>Better Planning</strong>: Map out all existing Ingress resources first</li>
<li class=""><strong>Testing Strategy</strong>: Set up comprehensive testing before switching</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="performance-considerations">Performance Considerations<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#performance-considerations" class="hash-link" aria-label="Direct link to Performance Considerations" title="Direct link to Performance Considerations" translate="no">​</a></h3>
<ul>
<li class=""><strong>Resource Usage</strong>: Envoy Gateway uses ~200MB more memory than NGINX Ingress</li>
<li class=""><strong>Startup Time</strong>: Takes longer to initialize but performs similarly once running</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://mrpbennett.dev/blog/gatewayapi-migration-from-ingress-nginx#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Migrating from Ingress NGINX to Gateway API using Envoy Gateway was a significant undertaking, but the benefits are clear:</p>
<ul>
<li class=""><strong>Better separation of concerns</strong> between platform and application teams</li>
<li class=""><strong>More expressive routing capabilities</strong> for complex traffic patterns</li>
<li class=""><strong>Standardized API</strong> that works across different ingress controllers</li>
<li class=""><strong>Future-proof architecture</strong> aligned with Kubernetes evolution</li>
</ul>
<p>While the migration required effort and had its challenges, the Gateway API provides a more robust foundation for modern Kubernetes networking. The investment in learning and migrating pays off with increased flexibility and maintainability.</p>
<p><strong>Key Takeaways:</strong></p>
<ul>
<li class="">Plan your migration carefully and test thoroughly</li>
<li class="">Use explicit Certificate resources instead of annotations</li>
<li class="">Validate each step before proceeding</li>
<li class="">Keep both systems running during transition</li>
</ul>
<p>The future of Kubernetes ingress is bright with Gateway API! 🚀</p>]]></content:encoded>
            <category>kubernetes</category>
        </item>
        <item>
            <title><![CDATA[Managing multiple databases in CNPG]]></title>
            <link>https://mrpbennett.dev/blog/deadling-with-databases-cnpg</link>
            <guid>https://mrpbennett.dev/blog/deadling-with-databases-cnpg</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I love a bit of Kubernetes but I also hate it as I navigate the Kubernetes landscape and learning from doing. I have come across multiple gotyas. This is one of them, I run a CloudnativePG Cluster, Originally I only used this to play around with Postgres.]]></description>
            <content:encoded><![CDATA[<p>I love a bit of Kubernetes but I also hate it as I navigate the Kubernetes landscape and learning from doing. I have come across multiple gotyas. This is one of them, I run a CloudnativePG Cluster, Originally I only used this to play around with Postgres.</p>
<p>However, the more applications I deployed to my cluster the more applications depended on storing things in Postgres. This made me spin up a Postgres instance via Docker on a VM, this way I could keep the instance up, in case I needed to tear down my cluster.</p>
<p>This worked fine, but I wanted to migrate everything to within Kubernetes. Now with CNPG v1.25+ I and ArgoCDs <code>sync-wave</code> I can do just that.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploying-a-database">Deploying a database<a href="https://mrpbennett.dev/blog/deadling-with-databases-cnpg#deploying-a-database" class="hash-link" aria-label="Direct link to Deploying a database" title="Direct link to Deploying a database" translate="no">​</a></h3>
<p>Getting a basic cluster setup via CNPG is pretty simple, deploy an operator and then a cluster manifest. Now you have yourself a working High-Availability Postgres cluster. But bare bones it doesn't spin up with any databases unless you use <code>bootstrap.initdb</code> to configure a single database from the start.</p>
<p>But what about multiple databases, Each of my applications requires its own. With <code>v1.25+</code> this is as simple as deploying a <code>Database</code> manifest. Additional databases can be created or managed via <a href="https://cloudnative-pg.io/documentation/1.26/declarative_database_management/" target="_blank" rel="noopener noreferrer" class="">declarative database management</a> using the Database CRD, Below is an example of a database deployed for the application <code>Authentik</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> postgresql.cnpg.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Database</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> authentik</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">db</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> authentik</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">db </span><span class="token comment" style="color:#999988;font-style:italic"># db name</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">owner</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> authentik </span><span class="token comment" style="color:#999988;font-style:italic"># db owner</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">cluster</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cnpg</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cluster </span><span class="token comment" style="color:#999988;font-style:italic"># name of your cluster</span><br></span></code></pre></div></div>
<p>Before deploying you will have to give the <code>owner</code> a role within the cluster, if you're not using the <code>postgres</code> superuser. A <code>role</code> within the cluster manifest looks like this:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">managed</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">roles</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> authentik</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">ensure</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> present</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">comment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Simple application user for Authentik'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">login</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">superuser</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">passwordSecret</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cnpg</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">authentik</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">user</span><br></span></code></pre></div></div>
<p>For my use case, I gave the user <code>superuser</code> access, as I had trouble giving the role the correct permissions so I gave up and gave <code>superuser</code> access. You also have to give the user a password, which is as simple as creating a <code>secret</code> and deploying that.</p>
<p>That's pretty much it, this is how I deploy multiple databases within my cluster when it's created. And of course, you can deploy as many <code>Database</code> manifests as needed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sync-waveargocd">Sync-wave...ArgoCD<a href="https://mrpbennett.dev/blog/deadling-with-databases-cnpg#sync-waveargocd" class="hash-link" aria-label="Direct link to Sync-wave...ArgoCD" title="Direct link to Sync-wave...ArgoCD" translate="no">​</a></h3>
<p>Not sure about you, but I was wondering how I could deploy the applications and databases without things crashing because the database wasn't ready.</p>
<p>Enter <code>sync-wave</code> with ArgoCD. Adding a <code>sync-wave</code> annotation to ArgoCD <code>Application</code> manifest tells ArgoCD to deploy your applications in waves. The below manifest shows that I am deploying my Postgres cluster in <code>sync-wave: 0</code>. This will deploy in the first round, ArgoCD won't deploy any other waves till all applications in wave 0 are healthy.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token important">&amp;app</span><span class="token plain"> cnpg</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cluster</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> argocd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">annotations</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">argocd.argoproj.io/sync-wave</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'0'</span><br></span></code></pre></div></div>
<p>Therefore it would be practical to deploy all other applications that relied on the PostgreSQL cluster to deploy in wave 1 or wave 2. This way once the cluster is healthy and all of its databases are deployed. The other applications can be deployed and write to the databases. Something like this:</p>
<p><img decoding="async" loading="lazy" alt="sync-wave argocd" src="https://mrpbennett.dev/assets/images/syncwave-26c6ec9086702c0e2313f944247e012c.png" width="860" height="301" class="img_ev3q"></p>
<p>This is how I have set up my CloudNativePG cluster and it works pretty well. I am sure this is very basic but for me it works, you can find more about <code>sync-wave</code> or deploying <code>Databases</code> on the links below:</p>
<ul>
<li class=""><a href="https://cloudnative-pg.io/documentation/1.26/declarative_database_management/" target="_blank" rel="noopener noreferrer" class="">Database manifests</a></li>
<li class=""><a href="https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/" target="_blank" rel="noopener noreferrer" class="">ArgoCD sync-wave</a></li>
</ul>]]></content:encoded>
            <category>kubernetes</category>
        </item>
        <item>
            <title><![CDATA[Zed my way]]></title>
            <link>https://mrpbennett.dev/blog/zed-my-way</link>
            <guid>https://mrpbennett.dev/blog/zed-my-way</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[I love trying out new text editors, don't ask me why because when I get used to one, something new comes out that I want to try—that's just the way it goes, I guess.]]></description>
            <content:encoded><![CDATA[<p>I love trying out new text editors, don't ask me why because when I get used to one, something new comes out that I want to try—that's just the way it goes, I guess.</p>
<p>This time I am trying out <a href="https://zed.dev/" target="_blank" rel="noopener noreferrer" class="">Zed</a> "The editor for what's next" written in Rust.</p>
<blockquote>
<p>Zed is a next-generation code editor designed for high-performance collaboration with humans and AI.</p>
</blockquote>
<p>I must admit it's pretty sweet. But not as sweet as <a href="https://lazyvim.org/" target="_blank" rel="noopener noreferrer" class="">LazyVim</a> which is currently my daily driver. So coming from LazyVim, I wanted to get Zed close to my LazyVim experience, allowing me to get up to speed quicker than I would otherwise. Similar to LazyVim, Zed is managed by a bunch of <code>dotfiles</code>.</p>
<p>Here are my dotfiles for my Zed setup. I mainly write in Python, Go, and YAML. Being a homelabber, I spend a lot of time in Docker and Kubernetes manifests. Setting up YAML is important to me; currently with this setup I have yet to get Kubernetes completion sorted, but that is a task for another day.</p>
<p>Python and Go work well though—Go is simply awesome in terms of getting editors set up.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="settingsjson">settings.json<a href="https://mrpbennett.dev/blog/zed-my-way#settingsjson" class="hash-link" aria-label="Direct link to settings.json" title="Direct link to settings.json" translate="no">​</a></h2>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// settings.json</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"agent"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"always_allow_tool_actions"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"default_model"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"provider"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"copilot_chat"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"model"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"claude-sonnet-4"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"model_parameters"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"diagnostics"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"inline"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"enabled"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"inlay_hints"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"enabled"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"buffer_font_family"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"JetBrainsMono Nerd Font Mono"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"ui_font_family"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">".ZedSans"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"icon_theme"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Catppuccin Mocha"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"vim_mode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"cursor_blink"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"relative_line_numbers"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scroll_beyond_last_line"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"off"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scrollbar"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"show"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"never"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"ui_font_size"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"buffer_font_size"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">14.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"theme"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"mode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"system"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"light"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"One Light"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"dark"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Catppuccin Mocha (blue)"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Indentation, rainbow indentation</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"indent_guides"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"enabled"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"coloring"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"indent_aware"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"languages"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"Python"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"language_servers"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ruff"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"basedpyright"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"!pyright"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"formatter"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"code_action"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"source.organizeImports.ruff"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"code_action"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"source.fixAll.ruff"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"language_server"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ruff"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lsp"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"basedpyright"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"settings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"basedpyright.analysis"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"diagnosticMode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"inlayHints"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"callArgumentNames"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"gopls"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"initialization_options"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"gofumpt"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"yaml-language-server"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"settings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"yaml"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">// "keyOrdering": true,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"completion"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"format"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"enabled"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"singleQuote"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"schemaStore"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"enable"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"schemas"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"kubernetes"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"**/*.yaml"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"editor"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"tabSize"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="keymappings">Keymappings<a href="https://mrpbennett.dev/blog/zed-my-way#keymappings" class="hash-link" aria-label="Direct link to Keymappings" title="Direct link to Keymappings" translate="no">​</a></h2>
<p>After doing some googling, I came across this <a href="https://www.reddit.com/r/ZedEditor/comments/1jvo98k/zed_with_lazyvim_keymaps_my_perfect_editor_setup/" target="_blank" rel="noopener noreferrer" class="">awesome post</a> where <a href="https://www.reddit.com/user/xheisenbugx/" target="_blank" rel="noopener noreferrer" class="">xheisenbugx</a> had created keymappings similar to those of LazyVim for his Zed setup. The below is 99.9% from that post—I have adjusted it slightly for my needs, but these work really well.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// keymaps.json</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Terminal</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Workspace"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space f t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleBottomDock"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Window's navigation</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Dock || Terminal || Editor"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Window's motions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-h"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ActivatePaneLeft"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-l"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ActivatePaneRight"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-k"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ActivatePaneUp"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-j"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ActivatePaneDown"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"GitPanel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"git_panel::Close"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"AgentPanel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-\\"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleRightDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"cmd-k"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleRightDock"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// File panel (netrw)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ProjectPanel &amp;&amp; not_editing"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"a"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::NewFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"A"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::NewDirectory"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Rename"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Delete"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"x"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Cut"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"c"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Copy"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"p"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Paste"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Close project panel as project file panel on the right</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleLeftDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space e"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleLeftDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">":"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"command_palette::Toggle"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"%"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::NewFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"/"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::NewSearchInDirectory"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"enter"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::OpenPermanent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"escape"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::ToggleFocus"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"h"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::CollapseSelectedEntry"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"j"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"menu::SelectNext"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"k"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"menu::SelectPrevious"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"l"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::ExpandSelectedEntry"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"o"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::OpenPermanent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Delete"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::Rename"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::OpenPermanent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"v"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::OpenPermanent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"menu::SelectLast"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"g g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"menu::SelectFirst"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"-"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"project_panel::SelectParent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-6"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::AlternateFile"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Empty pane, set of keybindings that are available when there is no active editor</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"EmptyPane || SharedScreen"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// New file</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space f n"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::NewFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Open recent project</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space f p"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"projects::OpenRecent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Search in all the files</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"task::Spawn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"task_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"grep_search"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"reveal_target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"center"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Quit zed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space q q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"zed::Quit"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Workspace"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space g g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"task::Spawn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"task_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"start lazygit"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"reveal_target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"center"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Workspace"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space k 8"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"task::Spawn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"task_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"start k9s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"reveal_target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"center"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Fuzzy Finder</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Workspace"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space space"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"task::Spawn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"task_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"file_finder"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"reveal_target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"center"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Grep Search</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Workspace"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"space s g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token string" style="color:#e3116c">"task::Spawn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"task_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"grep_search"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"reveal_target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"center"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Editor &amp;&amp; VimControl &amp;&amp; !VimWaiting &amp;&amp; !menu"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Refactoring</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space c r "</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::Rename"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Chat with AI</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space a a"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"assistant::ToggleFocus"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-\\"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleRightDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"cmd-k"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleRightDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space a e"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"assistant::InlineAssist"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"cmd-l"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"assistant::InlineAssist"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space a t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleRightDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space g h d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::ExpandAllDiffHunks"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space g h D"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"git::Diff"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space g h r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"git::Restore"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space g h R"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"git::RestoreFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Toggle inlay hints</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space u i"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::ToggleInlayHints"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Toggle soft wrap</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space u w"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::ToggleSoftWrap"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Open markdown preview</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space m p"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"markdown::OpenPreview"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space m P"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"markdown::OpenPreviewToTheSide"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Open recent project</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space f p"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"projects::OpenRecent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Search word under cursor in current pane</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s w"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"buffer_search::Deploy"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Search word under cursor in all panes</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s W"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::DeploySearch"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Tab things. Almost as good as harpoon.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 1"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 2"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 3"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 4"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 5"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 6"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 7"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 8"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 9"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"pane::ActivateItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space 0"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::ActivateLastItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"] b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::ActivateNextItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"[ b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::ActivatePreviousItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space ,"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tab_switcher::Toggle"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Jump to the previous location</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space b b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::AlternateFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Close buffer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space b d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::CloseActiveItem"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Close inactive buffers</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space b q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::CloseInactiveItems"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// New file</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space b n"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::NewFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Search in the current buffer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim::Search"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Search in all the files</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"task::Spawn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"task_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"grep_search"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"reveal_target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"center"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space f n"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::NewFile"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Search</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space /"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::NewSearch"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Git</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space g b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"git::Blame"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// LSP &amp; Code actions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space c a"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::ToggleCodeActions"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"diagnostics::Deploy"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space s s"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"outline::Toggle"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Format</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space c f"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::Format"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// File explorer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space e"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleLeftDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Telescope</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space f f"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"file_finder::Toggle"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Quit zed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space q q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"zed::Quit"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Terminal</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"workspace::ToggleBottomDock"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Windows management</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space w s"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::SplitDown"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space w v"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::SplitRight"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space -"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::SplitDown"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space |"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::SplitRight"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space w c"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::CloseAllItems"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"space w d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pane::CloseAllItems"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Jump to hunks</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"] h"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToHunk"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"[ h"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToPreviousHunk"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"] c"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToHunk"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"[ c"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToPreviousHunk"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Jump to diagnostic</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"] d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToDiagnostic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"[ d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToPreviousDiagnostic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"] e"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToDiagnostic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"[ e"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::GoToPreviousDiagnostic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Excerpts</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"] q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::MoveToStartOfNextExcerpt"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"[ q"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::MoveToStartOfExcerpt"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Editor &amp;&amp; vim_mode == visual &amp;&amp; !VimWaiting &amp;&amp; !VimObject"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Line's Motions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-j"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::MoveLineDown"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-k"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::MoveLineUp"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Center the cursor on the screen when scrolling and find all references</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"VimControl &amp;&amp; !menu"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-d"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"workspace::SendKeystrokes"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ctrl-d z z"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"ctrl-u"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"workspace::SendKeystrokes"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ctrl-u z z"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"n"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"workspace::SendKeystrokes"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"n z z z v"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-n"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"workspace::SendKeystrokes"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"shift-n z z z v"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"shift-g"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"workspace::SendKeystrokes"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"shift-g z z"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"g r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::FindAllReferences"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim_operator == d"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"o"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"editor::ExpandAllDiffHunks"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"git::Restore"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim_mode == normal || vim_mode == visual"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"s"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim::PushSneak"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"S"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim::PushSneakBackward"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim_operator == a || vim_operator == i || vim_operator == cs"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"bindings"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vim::AnyBrackets"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="taskjson">task.json<a href="https://mrpbennett.dev/blog/zed-my-way#taskjson" class="hash-link" aria-label="Direct link to task.json" title="Direct link to task.json" translate="no">​</a></h2>
<p>Here I have added a <a href="https://zed.dev/docs/tasks" target="_blank" rel="noopener noreferrer" class=""><code>tasks.json</code></a> file. Zed supports ways to spawn (and rerun) commands using its integrated terminal to output the results, which allows the user to open a full-screen terminal window. I have added the following to <code>tasks.json</code>—the <code>command</code> is what you would type into the terminal to open the specified application. For example:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">$ lazygit</span><br></span></code></pre></div></div>
<p>This would open the Lazygit TUI within your terminal window.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// tasks.json</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// task for starting lazygit</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"label"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"start lazygit"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lazygit -p $ZED_WORKTREE_ROOT"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"reveal"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"hide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"on_success"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// task for starting k9s</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"label"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"start k9s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k9s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"reveal"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"hide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"on_success"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">//fuzzy finder</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"label"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"file_finder"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"zed \"$(tv files)\""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"hide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"allow_concurrent_runs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"use_new_terminal"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"shell"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"program"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/bin/zsh"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// grep search</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"label"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"grep_search"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use the command with the --exact flag to disable fuzzy search so it only gives you exact matches</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"zed \"$(tv --exact text)\""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"hide"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"always"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"allow_concurrent_runs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"use_new_terminal"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"shell"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"program"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/bin/sh"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>Below are a couple of screenshots of how <a href="https://k9scli.io/" target="_blank" rel="noopener noreferrer" class="">K9s</a> and <a href="https://github.com/jesseduffield/lazygit" target="_blank" rel="noopener noreferrer" class="">Lazygit</a> look when they're opened within Zed. Folloing on from this <a href="https://github.com/zed-industries/zed/discussions/22581" target="_blank" rel="noopener noreferrer" class="">Github Discussion</a> you're able to add fuzzy finder and grep search similar to what is provided by LazyVim. By using an app called <a href="https://github.com/alexpasmantier/television" target="_blank" rel="noopener noreferrer" class="">telvision</a>. Screen shots below:</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="lazygit">Lazygit<a href="https://mrpbennett.dev/blog/zed-my-way#lazygit" class="hash-link" aria-label="Direct link to Lazygit" title="Direct link to Lazygit" translate="no">​</a></h4>
<p>Keymapping: <code>space g g</code></p>
<p><img decoding="async" loading="lazy" alt="lazygit" src="https://mrpbennett.dev/assets/images/lazygit-f4abc81913eed6f671b74c2e1955bc5f.png" width="2880" height="1800" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="k9s">K9s<a href="https://mrpbennett.dev/blog/zed-my-way#k9s" class="hash-link" aria-label="Direct link to K9s" title="Direct link to K9s" translate="no">​</a></h4>
<p>Keymapping: <code>space k 8</code></p>
<p><img decoding="async" loading="lazy" alt="k9s" src="https://mrpbennett.dev/assets/images/k9s-6631a86f8f21d1ea8704850907f25d78.png" width="2880" height="1800" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="fuzzy-finder--grep-search">Fuzzy Finder &amp; Grep Search<a href="https://mrpbennett.dev/blog/zed-my-way#fuzzy-finder--grep-search" class="hash-link" aria-label="Direct link to Fuzzy Finder &amp; Grep Search" title="Direct link to Fuzzy Finder &amp; Grep Search" translate="no">​</a></h4>
<p>Keymapping: <code>space space</code></p>
<p><img decoding="async" loading="lazy" alt="fuzzy" src="https://mrpbennett.dev/assets/images/fuzzy-4b4ba4ade37d15dc48b0b3399fc10cfe.png" width="2880" height="1734" class="img_ev3q"></p>
<p>Keymapping: <code>space s g</code></p>
<p><img decoding="async" loading="lazy" alt="grep" src="https://mrpbennett.dev/assets/images/grep-275e75b30802f65d3823c637f59d3ef8.png" width="2880" height="1734" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next steps<a href="https://mrpbennett.dev/blog/zed-my-way#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<p>For me the next steps would be figuring out how I can replace the <a href="https://marketplace.visualstudio.com/items?itemName=ms-kubernetes-tools.vscode-kubernetes-tools" target="_blank" rel="noopener noreferrer" class="">Kubernetes VSC extension</a> with something that will give Zed the same functionality. As this allows me to connect to clusters and see Helm chart values.</p>]]></content:encoded>
            <category>kubernetes</category>
            <category>editor</category>
        </item>
        <item>
            <title><![CDATA[Installing a Talos cluster in Proxmox]]></title>
            <link>https://mrpbennett.dev/blog/talos-cluster-install</link>
            <guid>https://mrpbennett.dev/blog/talos-cluster-install</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[So it begins again... I have started rebuilding my homelab, but instead of going bare metal, I am moving to Proxmox mainly to reduce the number of physical machines as well as to experience working with a hypervisor. I have used Proxmox before, but this time I am going to try to get it as close to production-ready as I can.]]></description>
            <content:encoded><![CDATA[<p>So it begins again... I have started rebuilding my homelab, but instead of going bare metal, I am moving to <a href="https://www.proxmox.com/en/products/proxmox-virtual-environment/overview" target="_blank" rel="noopener noreferrer" class="">Proxmox</a> mainly to reduce the number of physical machines as well as to experience working with a hypervisor. I have used Proxmox before, but this time I am going to try to get it as close to production-ready as I can.</p>
<p>I will be running three <a href="https://www.bee-link.com/products/beelink-eqi13-pro" target="_blank" rel="noopener noreferrer" class="">Beelink EQi13 Pro Mini PC, Intel Core i5 13500H 12C/16T</a> units as a high availability Proxmox cluster running Ceph. The main use will be running an HA <a href="https://www.talos.dev/" target="_blank" rel="noopener noreferrer" class="">Talos</a> cluster across 3 control plane nodes and 3 worker nodes, amongst other microservices. Each unit will be upgraded to 64GB of RAM over time. This would give me the following total compute resources:</p>
<div class="language-txt codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-txt codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">36C/48T - 192GB RAM - 2TB storage</span><br></span></code></pre></div></div>
<p>I'll be running a 1TB boot drive as well as a 1TB storage drive, mainly for the Talos workers, as my other services can use the host boot drive. There will be plenty of space left on the boot drive once Proxmox is installed. I'll be running my cluster with a <code>2:1</code> ratio, giving me <code>96vCPU</code>, which is plenty. The RAM might end up being a bottleneck, but I doubt it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started">Getting started<a href="https://mrpbennett.dev/blog/talos-cluster-install#getting-started" class="hash-link" aria-label="Direct link to Getting started" title="Direct link to Getting started" translate="no">​</a></h2>
<p>Since we're going to be installing Talos, we will need to use their CLI. As I am on <a href="https://omarchy.org/" target="_blank" rel="noopener noreferrer" class="">Omarchy</a>, I can't use Homebrew, which is one of their recommended methods. Instead, we can use their provided script:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl -sL https://talos.dev/install | sh</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-the-iso">Building the ISO<a href="https://mrpbennett.dev/blog/talos-cluster-install#building-the-iso" class="hash-link" aria-label="Direct link to Building the ISO" title="Direct link to Building the ISO" translate="no">​</a></h2>
<p>Once installed, you will have access to <code>talosctl</code>. Now that you have the Talos CLI, it's time to generate the ISO. Since we're going to be using Proxmox, we will need the QEMU guest support as well as two other add-ons. I'll be running Longhorn as my storage solution, so we need to add the following extensions:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">- siderolabs/qemu-guest-agent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- siderolabs/iscsi-tools</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- siderolabs/util-linux-tools</span><br></span></code></pre></div></div>
<p>To build the ISO, we need to head over to <a href="https://factory.talos.dev/" target="_blank" rel="noopener noreferrer" class="">https://factory.talos.dev/</a> where you should select the following options:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">1. Cloud Server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2. Choose the latest version of Talos</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">3. Nocloud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4. Pick the architecture you need (since we're using Proxmox, it's amd64)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5. Add the extensions from above</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">6. Follow the defaults through to the final page</span><br></span></code></pre></div></div>
<p>Once you have completed those steps, you're able to download your ISO. Keep this page bookmarked, as you will need the information later.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-the-vms">Creating the VMs<a href="https://mrpbennett.dev/blog/talos-cluster-install#creating-the-vms" class="hash-link" aria-label="Direct link to Creating the VMs" title="Direct link to Creating the VMs" translate="no">​</a></h2>
<p>We have <code>talosctl</code> installed and our ISO ready, so now we can create our virtual machines.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="upload-the-iso">Upload the ISO<a href="https://mrpbennett.dev/blog/talos-cluster-install#upload-the-iso" class="hash-link" aria-label="Direct link to Upload the ISO" title="Direct link to Upload the ISO" translate="no">​</a></h3>
<p>First, we need to upload the ISO. From the Proxmox UI, select the "local" storage and enter the "Content" section. Click the "Upload" button:</p>
<p><img decoding="async" loading="lazy" alt="upload iso" src="https://mrpbennett.dev/assets/images/upload-iso-5453b232612a38d6a8fb08d9f077b1c1.png" width="1792" height="1348" class="img_ev3q"></p>
<p>Select the ISO you downloaded previously, then hit "Upload".</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-the-vm">Creating the VM<a href="https://mrpbennett.dev/blog/talos-cluster-install#creating-the-vm" class="hash-link" aria-label="Direct link to Creating the VM" title="Direct link to Creating the VM" translate="no">​</a></h3>
<p>First, we need to create our control plane. For this, I have allocated 4vCPU, 8GB RAM, and 250GB storage.</p>
<p><img decoding="async" loading="lazy" alt="create vm" src="https://mrpbennett.dev/assets/images/vm-c16312b6091ae3ca0e27b1497fd8df4e.png" width="1792" height="1348" class="img_ev3q"></p>
<p>Fill out a name for the new VM, for example: <code>talos-proxmox-0</code></p>
<p><img decoding="async" loading="lazy" alt="name-vm" src="https://mrpbennett.dev/assets/images/name-vm-49181c03eb9b1f8082599868dbbe4e39.png" width="1792" height="1348" class="img_ev3q"></p>
<p>In the OS tab, select the ISO we uploaded earlier:</p>
<p><img decoding="async" loading="lazy" alt="os" src="https://mrpbennett.dev/assets/images/vm-os-69f51b375a43e3ca6f507ad272422dfc.png" width="1792" height="1348" class="img_ev3q"></p>
<p>Keep the defaults in the "System" tab and the "Hard Disk" tab, only changing the size if desired. Next, verify that the RAM is set to at least 2GB:</p>
<p><img decoding="async" loading="lazy" alt="ram" src="https://mrpbennett.dev/assets/images/os-ram-6db59ddd28ee8191c5d0df6ac02c44c0.png" width="1792" height="1348" class="img_ev3q"></p>
<p>Keep the default values for networking, verifying that the VM is set to come up on the bridge interface. Finish creating the VM by clicking through the "Confirm" tab and then "Finish". Repeat this process for a second VM to use as a worker node. You can also repeat this for any additional nodes desired.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="start-control-plane-node">Start Control Plane Node<a href="https://mrpbennett.dev/blog/talos-cluster-install#start-control-plane-node" class="hash-link" aria-label="Direct link to Start Control Plane Node" title="Direct link to Start Control Plane Node" translate="no">​</a></h2>
<p>Once the VMs have been created and updated, start the VM that will be the first control plane node. This VM will boot the ISO image specified earlier and enter "maintenance mode". Once the machine has entered maintenance mode, there will be a console log that details the IP address that the node received. Take note of this IP address.</p>
<p>Before we start creating config files, export the control plane's IP so we can use it throughout our session:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export CONTROL_PLANE_IP=1.2.3.4</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="generate-machine-configurations">Generate Machine Configurations<a href="https://mrpbennett.dev/blog/talos-cluster-install#generate-machine-configurations" class="hash-link" aria-label="Direct link to Generate Machine Configurations" title="Direct link to Generate Machine Configurations" translate="no">​</a></h2>
<p>With the IP address above, you can now generate the machine configurations to use for installing Talos and Kubernetes. Remember how I advised saving the information you got after building the image from the image factory? This is where it comes in handy.</p>
<p>The following snippet <code>factory.talos.dev/installer/ce4c980550dd2ab1b17bbf2b08801c7eb59418eafe8f279833297925d67c7515:v1.11.0</code> will apply the extra packages when you're setting up your config files.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">talosctl gen config talos-proxmox-cluster https://$CONTROL_PLANE_IP:6443 --output-dir conf --install-image factory.talos.dev/installer/ce4c980550dd2ab1b17bbf2b08801c7eb59418eafe8f279833297925d67c7515:v1.11.0</span><br></span></code></pre></div></div>
<p>After you have run the above command, you should have a <code>controlplane.yaml</code>, <code>worker.yaml</code>, and <code>talosconf</code> file. These are the configuration files you use to configure your VMs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-your-control-plane">Creating your Control Plane<a href="https://mrpbennett.dev/blog/talos-cluster-install#creating-your-control-plane" class="hash-link" aria-label="Direct link to Creating your Control Plane" title="Direct link to Creating your Control Plane" translate="no">​</a></h2>
<p>First, we need to make some edits to our <code>controlplane.yaml</code>. We will give it a static IP and then set up the metrics server.</p>
<p>Under the network block, you will need to uncomment the following and make these changes:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">network</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> talos</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cp</span><span class="token punctuation" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># `interfaces` is used to define the network interface configuration.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">interfaces</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">interface</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> eth0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Assigns static IP addresses to the interface.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">addresses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> 192.168.7.10/22</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># A list of routes associated with the interface.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">routes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">network</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 0.0.0.0/0 </span><span class="token comment" style="color:#999988;font-style:italic"># The route's network (destination).</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">gateway</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 192.168.4.1 </span><span class="token comment" style="color:#999988;font-style:italic"># The route's gateway (if empty, creates link scope route).</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">metric</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1024</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># The optional metric for the route.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">mtu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1500</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># The interface's MTU.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">nameservers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> 1.1.1.1</span><br></span></code></pre></div></div>
<p>Of course, change these to what you need them to be, and double-check your network interface as well. Once the config is applied, the VM will have a hostname and static IP. Next, let's set up the metrics server.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="metrics-server">Metrics Server<a href="https://mrpbennett.dev/blog/talos-cluster-install#metrics-server" class="hash-link" aria-label="Direct link to Metrics Server" title="Direct link to Metrics Server" translate="no">​</a></h3>
<p>Metrics Server enables use of the Horizontal Pod Autoscaler and Vertical Pod Autoscaler. It does this by gathering metric data from the kubelets in a cluster. We need to enable kubelet certificate rotation, so set the following:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">machine</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">kubelet</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">extraArgs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">rotate-server-certificates</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<p>We will want to ensure that new certificates for the kubelets are approved automatically. This can easily be done with the Kubelet Serving Certificate Approver, which is the above snippet that will automatically approve the Certificate Signing Requests generated by the kubelets.</p>
<p>We can have Kubelet Serving Certificate Approver and metrics-server installed on the cluster automatically during bootstrap by adding the following snippet to the Cluster Config of the node that will be handling the bootstrap process:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">cluster</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">extraManifests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//raw.githubusercontent.com/alex1989hu/kubelet</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">serving</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cert</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">approver/main/deploy/standalone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">install.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//github.com/kubernetes</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">sigs/metrics</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server/releases/latest/download/components.yaml</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="config-for-longhorn">Config for Longhorn<a href="https://mrpbennett.dev/blog/talos-cluster-install#config-for-longhorn" class="hash-link" aria-label="Direct link to Config for Longhorn" title="Direct link to Config for Longhorn" translate="no">​</a></h3>
<p>Longhorn requires pod security enforce: "privileged".
By default, Talos Linux applies a baseline pod security profile across namespaces, except for the <code>kube-system</code> namespace.</p>
<p>We can do one of the following:</p>
<ol>
<li class="">Add the exemption to the cluster config:</li>
</ol>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">exemptions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespaces</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> kube</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> longhorn</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span></code></pre></div></div>
<p>Or, once you have installed Longhorn, create the following namespace:</p>
<ol start="2">
<li class="">Create a privileged namespace:</li>
</ol>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> longhorn</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">pod-security.kubernetes.io/enforce</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> privileged</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">pod-security.kubernetes.io/audit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> privileged</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">pod-security.kubernetes.io/warn</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> privileged</span><br></span></code></pre></div></div>
<p>Using the <code>controlplane.yaml</code> generated above, you can now apply this config using <code>talosctl</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">talosctl apply-config --insecure --nodes $CONTROL_PLANE_IP --file conf/controlplane.yaml</span><br></span></code></pre></div></div>
<p>You should now see activity in the Proxmox console for this VM. Talos will be installed to disk, the VM will reboot, and then Talos will configure the Kubernetes control plane on this VM. The VM will remain in the "Booting" stage until the bootstrap is completed in a later step.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-the-cluster">Using the Cluster<a href="https://mrpbennett.dev/blog/talos-cluster-install#using-the-cluster" class="hash-link" aria-label="Direct link to Using the Cluster" title="Direct link to Using the Cluster" translate="no">​</a></h2>
<p>Once the cluster is available, you can make use of <code>talosctl</code> and <code>kubectl</code> to interact with the cluster. For example, to view current running containers, run <code>talosctl containers</code> for a list of containers in the system namespace, or <code>talosctl containers -k</code> for the <code>k8s.io</code> namespace. To view the logs of a container, use <code>talosctl logs &lt;container&gt;</code> or <code>talosctl logs -k &lt;container&gt;</code>.</p>
<p>First, configure <code>talosctl</code> to talk to your control plane node by issuing the following, updating paths and IPs as necessary:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export TALOSCONFIG="_out/talosconfig"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">talosctl config endpoint $CONTROL_PLANE_IP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">talosctl config node $CONTROL_PLANE_IP</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="bootstrap-etcd">Bootstrap etcd<a href="https://mrpbennett.dev/blog/talos-cluster-install#bootstrap-etcd" class="hash-link" aria-label="Direct link to Bootstrap etcd" title="Direct link to Bootstrap etcd" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">talosctl bootstrap</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="generate-kubeconfig">Generate <code>kubeconfig</code><a href="https://mrpbennett.dev/blog/talos-cluster-install#generate-kubeconfig" class="hash-link" aria-label="Direct link to generate-kubeconfig" title="Direct link to generate-kubeconfig" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">talosctl kubeconfig .</span><br></span></code></pre></div></div>
<p>Now you're ready to go! Follow the same steps as you did when you created the control plane for a worker node:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">talosctl apply-config --insecure --nodes $WORKER_IP --file conf/worker.yaml</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>kubernetes</category>
        </item>
        <item>
            <title><![CDATA[How to Make Yazi and Helix Play Nice]]></title>
            <link>https://mrpbennett.dev/blog/2026/02/helix-with-yazi</link>
            <guid>https://mrpbennett.dev/blog/2026/02/helix-with-yazi</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[Look, I love Helix. It's fast, it's modal, and it doesn't make me feel like I'm piloting a 747 just to edit some code. But you know what it was missing? A solid file manager integration that didn't require me to spawn Zellij or tmux just to browse directories without leaving my editor.]]></description>
            <content:encoded><![CDATA[<p>Look, I love Helix. It's fast, it's modal, and it doesn't make me feel like I'm piloting a 747 just to edit some code. But you know what it was missing? A solid file manager integration that didn't require me to spawn Zellij or tmux just to browse directories without leaving my editor.</p>
<p>Enter Yazi—the blazingly fast terminal file manager written in Rust (because of course it is). And thanks to some absolute wizardry from the Yazi maintainer himself, we can now run Yazi directly inside Helix without all the multiplexer overhead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem">The Problem<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Initially, folks tried to replicate the LazyGit integration pattern with Yazi. You know, the usual:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">"C-f" = [":new", ":insert-output yazi --chooser-file=/dev/stdout", ":buffer-close!", ":redraw"]</span><br></span></code></pre></div></div>
<p>Spoiler alert: it didn't work. Helix would just freeze until you mashed Enter or quit Yazi. Not exactly the smooth experience we were hoping for.</p>
<p>The issue? Yazi is an interactive TUI application, and Helix's <code>:insert-output</code> wasn't designed to handle interactive programs gracefully. It was like trying to fit a square peg in a round hole—technically possible with enough force, but not pretty.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-solution-that-actually-works">The Solution (That Actually Works)<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#the-solution-that-actually-works" class="hash-link" aria-label="Direct link to The Solution (That Actually Works)" title="Direct link to The Solution (That Actually Works)" translate="no">​</a></h2>
<p>Then sxyazi (the Yazi creator) showed up in the GitHub discussion like some kind of terminal wizard and dropped this absolute gem:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[keys.normal]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">space.e = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':sh rm -f /tmp/unique-file',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':insert-output yazi "%{buffer_name}" --chooser-file=/tmp/unique-file',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':sh printf "\x1b[?1049h\x1b[?2004h" &gt; /dev/tty',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':open %sh{cat /tmp/unique-file}',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':redraw',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">]</span><br></span></code></pre></div></div>
<p>Let me break down what's happening here because it's legitimately clever:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-play-by-play">The Play-by-Play<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#the-play-by-play" class="hash-link" aria-label="Direct link to The Play-by-Play" title="Direct link to The Play-by-Play" translate="no">​</a></h3>
<ol>
<li class="">
<p><strong>Clear the temp file</strong> - We're using <code>/tmp/unique-file</code> as our communication bridge between Yazi and Helix. First, nuke it to avoid stale data.</p>
</li>
<li class="">
<p><strong>Launch Yazi with context</strong> - The <code>"%{buffer_name}"</code> expansion passes your current file location to Yazi, so it opens in the right directory. The <code>--chooser-file</code> flag tells Yazi to write the selected file path to our temp file when you pick something.</p>
</li>
<li class="">
<p><strong>The magic escape sequence</strong> - This is where it gets spicy. That <code>printf "\x1b[?1049h\x1b[?2004h"</code> sends ANSI escape codes to the terminal:</p>
<ul>
<li class=""><code>\x1b[?1049h</code> switches to the alternate screen buffer (the same thing full-screen TUIs use)</li>
<li class=""><code>\x1b[?2004h</code> enables bracketed paste mode</li>
</ul>
<p>These codes basically tell the terminal "hey, we're done with the interactive stuff now" and prevent Helix from getting confused about terminal state.</p>
</li>
<li class="">
<p><strong>Open the chosen file</strong> - Read whatever Yazi wrote to the temp file and open it in Helix.</p>
</li>
<li class="">
<p><strong>Refresh everything</strong> - <code>redraw</code> makes sure Helix updates its display properly.</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bonus-image-and-video-previews">Bonus: Image and Video Previews<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#bonus-image-and-video-previews" class="hash-link" aria-label="Direct link to Bonus: Image and Video Previews" title="Direct link to Bonus: Image and Video Previews" translate="no">​</a></h2>
<p>Because Yazi is actually insane (in a good way), you can preview images and videos directly in your terminal while browsing files. Just make sure your terminal supports it (kitty, wezterm, or anything with sixel support).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="extra-tweaks-for-the-power-users">Extra Tweaks for the Power Users<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#extra-tweaks-for-the-power-users" class="hash-link" aria-label="Direct link to Extra Tweaks for the Power Users" title="Direct link to Extra Tweaks for the Power Users" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="want-the-working-directory-to-follow-you-around">Want the working directory to follow you around?<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#want-the-working-directory-to-follow-you-around" class="hash-link" aria-label="Direct link to Want the working directory to follow you around?" title="Direct link to Want the working directory to follow you around?" translate="no">​</a></h3>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">C-f = [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':sh rm -f /tmp/unique-file',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':sh rm -f /tmp/unique-cwd-file',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ":insert-output yazi --chooser-file=/tmp/unique-file --cwd-file=/tmp/unique-cwd-file",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':insert-output echo "\x1b[?1049h\x1b[?2004h" &gt; /dev/tty',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':open %sh{cat /tmp/unique-file}',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':cd %sh{cat /tmp/unique-cwd-file}',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ':redraw',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">]</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="got-parentheses-in-your-paths-looking-at-you-nextjs">Got parentheses in your paths (looking at you, Next.js)?<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#got-parentheses-in-your-paths-looking-at-you-nextjs" class="hash-link" aria-label="Direct link to Got parentheses in your paths (looking at you, Next.js)?" title="Direct link to Got parentheses in your paths (looking at you, Next.js)?" translate="no">​</a></h3>
<p>Use single quotes around the buffer name expansion:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">":insert-output yazi '%{buffer_name}' --chooser-file=/tmp/unique-file",</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="mouse-scroll-acting-weird-after-using-yazi">Mouse scroll acting weird after using Yazi?<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#mouse-scroll-acting-weird-after-using-yazi" class="hash-link" aria-label="Direct link to Mouse scroll acting weird after using Yazi?" title="Direct link to Mouse scroll acting weird after using Yazi?" translate="no">​</a></h3>
<p>Toggle it off and on (yeah, seriously):</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">":set-option mouse false",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">":set-option mouse true",</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="known-issues">Known Issues<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#known-issues" class="hash-link" aria-label="Direct link to Known Issues" title="Direct link to Known Issues" translate="no">​</a></h2>
<p>There's still a minor quirk where exiting Helix after using Yazi can leave some terminal artifacts on screen. The latest fix using <code>:sh printf</code> instead of <code>:insert-output echo</code> helps, but it's not 100% solved yet. The Helix team is aware and there's <a href="https://github.com/helix-editor/helix/issues/15059" target="_blank" rel="noopener noreferrer" class="">an issue tracking</a> a proper command for launching external TUI apps.</p>
<p>Also, if you're using Zellij, you might want to check out alternative approaches that leverage floating panes instead of trying to run Yazi inside Helix directly. YMMV.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-bottom-line">The Bottom Line<a href="https://mrpbennett.dev/blog/2026/02/helix-with-yazi#the-bottom-line" class="hash-link" aria-label="Direct link to The Bottom Line" title="Direct link to The Bottom Line" translate="no">​</a></h2>
<p>This integration is honestly pretty slick once you get it set up. No more <code>Ctrl-Z</code>-ing out to your shell, no terminal multiplexer required, just press a keybind and you're in a proper file manager that doesn't suck.</p>
<p>Props to sxyazi for not only building Yazi but also taking the time to figure out how to make it work seamlessly with other tools. This is the kind of polish that makes the terminal ecosystem actually pleasant to use.</p>
<p>Now go forth and navigate your directories in style. Your <code>cd</code> key is about to get a lot less worn out.</p>]]></content:encoded>
            <category>terminal</category>
        </item>
        <item>
            <title><![CDATA[Zellij vs Tmux: My Terminal Multiplexer Journey]]></title>
            <link>https://mrpbennett.dev/blog/2026/02/tmux-zellij</link>
            <guid>https://mrpbennett.dev/blog/2026/02/tmux-zellij</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[Terminal multiplexers are one of those tools that, once you start using them, you wonder how you ever survived without one. Whether you're managing a homelab, running long-lived data engineering pipelines, or just tired of juggling dozens of terminal tabs, a multiplexer changes the game entirely.]]></description>
            <content:encoded><![CDATA[<p>Terminal multiplexers are one of those tools that, once you start using them, you wonder how you ever survived without one. Whether you're managing a homelab, running long-lived data engineering pipelines, or just tired of juggling dozens of terminal tabs, a multiplexer changes the game entirely.</p>
<p>In this post I'm going to walk through both <strong>Zellij</strong> and <strong>Tmux</strong> — two of the most popular options — share why I landed where I did, and show you the config I actually use day-to-day.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-a-terminal-multiplexer-anyway">What's a Terminal Multiplexer, Anyway?<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#whats-a-terminal-multiplexer-anyway" class="hash-link" aria-label="Direct link to What's a Terminal Multiplexer, Anyway?" title="Direct link to What's a Terminal Multiplexer, Anyway?" translate="no">​</a></h2>
<p>If you're new to the concept: a terminal multiplexer lets you split a single terminal window into multiple panes, manage named sessions that persist even after you disconnect, and generally treat your terminal like a proper workspace rather than a throwaway window.</p>
<p>This is invaluable when you're SSH'd into a remote machine, running a Kubernetes cluster, or just want a reproducible terminal layout that's waiting for you every time you sit down.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tmux--the-old-guard">Tmux — The Old Guard<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#tmux--the-old-guard" class="hash-link" aria-label="Direct link to Tmux — The Old Guard" title="Direct link to Tmux — The Old Guard" translate="no">​</a></h2>
<p>Tmux has been around since 2007 and is the de facto standard. It's available in virtually every package manager, battle-tested, and has an enormous community behind it. If you've ever inherited a server setup from someone else, there's a good chance Tmux was already installed.</p>
<p>I came to Tmux the way most people do — it was just already there. Every server I SSH'd into had it installed, every tutorial referenced it, and when I started reading about how <a href="https://www.anthropic.com/" target="_blank" rel="noopener noreferrer" class="">Anthropic</a> lets Claude spin up agents inside Tmux sessions, that sealed it: this was the tool I should actually learn properly.</p>
<p>What I like about Tmux is that it's essentially infrastructure at this point. The plugin ecosystem is enormous — TPM alone opens up things like Catppuccin theming and seamless clipboard integration. The scriptability is unmatched; you can build elaborate session layouts in shell scripts and have your entire workspace reconstruct itself from scratch.</p>
<p>The frustrations are real though. The default keybindings feel like they were designed for a keyboard I don't own. <code>Ctrl+b</code> as the prefix collides with shell navigation, and <code>%</code> and <code>"</code> for splits are ergonomically bizarre. Finding a leader key that doesn't fight with everything else in your workflow — Neovim, the shell, the OS — takes genuine experimentation. The config DSL is also verbose enough that you'll spend an afternoon reading the man page before you're comfortable with it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="my-tmux-config">My Tmux Config<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#my-tmux-config" class="hash-link" aria-label="Direct link to My Tmux Config" title="Direct link to My Tmux Config" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># ~/.config/tmux/tmux.conf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># General Settings</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Unbind default prefix (Ctrl+b)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">unbind C-b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Set Ctrl-s as new leader key</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g prefix Ctrl-s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind Ctrl-s send-prefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># mouse support and terminal settings</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g mouse on</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g default-terminal "tmux-256color"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g set-clipboard on          # use system clipboard</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g status-position top       # macOS / darwin style</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g detach-on-destroy off     # don't exit from tmux when closing a session</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">setw -g mode-keys vi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Enable Yazi Image Previewer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g allow-passthrough on</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -ga update-environment TERM</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -ga update-environment TERM_PROGRAM</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Reload config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">unbind r</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind R source-file "$HOME/.config/tmux/tmux.conf" \; display-message "tmux.conf reloaded ☺️"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Window, Pane &amp; Session Management</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Pane navigation using hjkl</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind-key h select-pane -L</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind-key j select-pane -D</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind-key k select-pane -U</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind-key l select-pane -R</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind -r -T prefix H resize-pane -L 20</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind -r -T prefix J resize-pane -D 20</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind -r -T prefix K resize-pane -U 7</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind -r -T prefix L resize-pane -R 7</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind x kill-pane</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind r command-prompt -I "#W" "rename-window '%%'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Pop a floating pane: Opens a large centered popup with your default shell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind f display-popup -w 80% -h 80% -E $SHELL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># LazyVim Window splits</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind | split-window -h -c "#{pane_current_path}"    # split window right</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind - split-window -v -c "#{pane_current_path}"    # split window down</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">unbind c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind t new-window -c "#{pane_current_path}"         # new tab with &lt;leader&gt; t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Window and pane numbering</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set-window-option -g pane-base-index 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g base-index 1                                 # Start windows at 1, not 0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">setw -g pane-base-index 1                           # Start panes at 1, not 0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g renumber-windows on                          # renumber all windows when any window is closed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g automatic-rename off</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g allow-rename off</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind s command-prompt -p "Session name:" "new-session -s '%%'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind o choose-session                               # zellij session keybind</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind d kill-session</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># List of plugins</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @plugin 'tmux-plugins/tpm'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @plugin 'tmux-plugins/tmux-sensible'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @plugin 'catppuccin/tmux#v2.1.3'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @plugin 'tmux-plugins/tmux-yank'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Catppuccin theme options</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># =============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @catppuccin_flavor "mocha"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @catppuccin_window_status_style "basic"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @catppuccin_window_current_text " #{window_name}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @catppuccin_window_text " #{window_name}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @catppuccin_window_current_number_color "#{?window_zoomed_flag,#{@thm_yellow},#{@thm_mauve}}" # typos: ignore</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g @catppuccin_window_number_color "#{?window_zoomed_flag,#{@thm_yellow},#{@thm_overlay_2}}" # typos: ignore</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># ============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># STATUS LINE CONFIGURATION</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># ============================================================================</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status line length settings</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g status-right-length 100</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g status-left-length 100</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g status-left "#{E:@catppuccin_status_session}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Status line modules</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set -g status-right "#{E:@catppuccin_status_application}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Initialize TMUX plugin manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run '~/.tmux/plugins/tpm/tpm'</span><br></span></code></pre></div></div>
<p>With my current Tmux config, I have tried to mirror as many key bindings as possible from Zellij and LazyVim to make the transition more manageable. Let's break some down:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">bind | split-window -h -c "#{pane_current_path}"    # split window right</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind - split-window -v -c "#{pane_current_path}"    # split window down</span><br></span></code></pre></div></div>
<p>The above are what I use within LazyVim to split my panes after pressing my leader key. Which means pane splitting for me is no longer a pain — I felt <code>%</code> wasn't ergonomic anyway.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">bind x kill-pane</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind r command-prompt -I "#W" "rename-window '%%'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind s command-prompt -p "Session name:" "new-session -s '%%'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind o choose-session</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bind d kill-session</span><br></span></code></pre></div></div>
<p>Here I have taken inspiration from Zellij for killing panes, renaming windows or sessions as well as picking a session. All taken from the Zellij keybindings.</p>
<p>These bindings are hopefully going to help me get the best experience from Tmux.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zellij--the-modern-challenger">Zellij — The Modern Challenger<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#zellij--the-modern-challenger" class="hash-link" aria-label="Direct link to Zellij — The Modern Challenger" title="Direct link to Zellij — The Modern Challenger" translate="no">​</a></h2>
<p>Zellij is the newer kid on the block, written in Rust, and takes a very different philosophy to the UX problem. Rather than requiring you to memorise a dense keymap upfront, it renders its available keybindings directly in a status bar — much friendlier for getting started. It also has a proper plugin system and a layout file format that makes reproducible workspaces genuinely pleasant.</p>
<p>I actually used Zellij first, before Tmux, which probably colours my opinion. What drew me in immediately was the status bar — that persistent strip at the bottom showing you exactly which mode you're in and what keys are available is a genuinely brilliant onboarding mechanism. With Tmux you're expected to have the man page memorised; with Zellij the tool teaches you as you go.</p>
<p>The modal UX (Pane mode, Tab mode, Session mode, Resize mode) maps well onto how I think about workspace management. Pressing <code>Ctrl p</code> to enter Pane mode and then navigating feels intentional and predictable, rather than a prefix-key soup. And because the keybindings are mode-scoped, there's much less surface area for conflicts with Neovim or shell bindings.</p>
<p>Where Zellij lags behind is ecosystem maturity. The plugin system is promising but not as deep as Tmux's. You won't find a Zellij equivalent for every Tmux plugin out there, and the community is smaller. But for day-to-day use it's genuinely plug-and-play in a way that Tmux never quite is out of the box.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="my-zellij-config">My Zellij Config<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#my-zellij-config" class="hash-link" aria-label="Direct link to My Zellij Config" title="Direct link to My Zellij Config" translate="no">​</a></h3>
<div class="language-kdl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kdl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">// ~/.config/zellij/config.kdl</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">keybinds clear-defaults=true {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    locked {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl g" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pane {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "left" { MoveFocus "left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "down" { MoveFocus "down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "up" { MoveFocus "up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "right" { MoveFocus "right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "c" { SwitchToMode "renamepane"; PaneNameInput 0; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "d" { NewPane "down"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "i" { TogglePanePinned; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "n" { NewPane; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "p" { SwitchFocus; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl p" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "r" { NewPane "right"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "s" { NewPane "stacked"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "z" { TogglePaneFrames; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        // hjkl splits ---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "h" {NewPane "right"; SwitchToMode "normal";}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "j" {NewPane "down"; SwitchToMode "normal";}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "k" {NewPane "up"; SwitchToMode "normal";}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "l" {NewPane "left"; SwitchToMode "normal";}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tab {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "left" { GoToPreviousTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "down" { GoToNextTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "up" { GoToPreviousTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "right" { GoToNextTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "1" { GoToTab 1; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "2" { GoToTab 2; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "3" { GoToTab 3; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "4" { GoToTab 4; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "5" { GoToTab 5; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "6" { GoToTab 6; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "7" { GoToTab 7; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "8" { GoToTab 8; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "9" { GoToTab 9; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "[" { BreakPaneLeft; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "]" { BreakPaneRight; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "b" { BreakPane; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "h" { GoToPreviousTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "j" { GoToNextTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "k" { GoToPreviousTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "l" { GoToNextTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "n" { NewTab; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "r" { SwitchToMode "renametab"; TabNameInput 0; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl t" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "x" { CloseTab; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "tab" { ToggleTab; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    resize {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "left" { Resize "Increase left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "down" { Resize "Increase down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "up" { Resize "Increase up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "right" { Resize "Increase right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "+" { Resize "Increase"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "-" { Resize "Decrease"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "=" { Resize "Increase"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "H" { Resize "Decrease left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "J" { Resize "Decrease down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "K" { Resize "Decrease up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "L" { Resize "Decrease right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "h" { Resize "Increase left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "j" { Resize "Increase down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "k" { Resize "Increase up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "l" { Resize "Increase right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl r" { SwitchToMode "normal"; } // Ctrl n: conflicted with lazyvim changed to Ctrl r</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    move {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "left" { MovePane "left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "down" { MovePane "down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "up" { MovePane "up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "right" { MovePane "right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "h" { MovePane "left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl h" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "j" { MovePane "down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "k" { MovePane "up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "l" { MovePane "right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "n" { MovePane; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "p" { MovePaneBackwards; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "tab" { MovePane; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    scroll {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "e" { EditScrollback; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "s" { SwitchToMode "entersearch"; SearchInput 0; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    search {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "c" { SearchToggleOption "CaseSensitivity"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "n" { Search "down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "o" { SearchToggleOption "WholeWord"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "p" { Search "up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "w" { SearchToggleOption "Wrap"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    session {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "a" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            LaunchOrFocusPlugin "zellij:about" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                floating true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                move_to_focused_tab true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            SwitchToMode "normal"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "c" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            LaunchOrFocusPlugin "configuration" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                floating true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                move_to_focused_tab true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            SwitchToMode "normal"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl o" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "p" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            LaunchOrFocusPlugin "plugin-manager" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                floating true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                move_to_focused_tab true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            SwitchToMode "normal"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "s" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            LaunchOrFocusPlugin "zellij:share" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                floating true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                move_to_focused_tab true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            SwitchToMode "normal"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "w" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            LaunchOrFocusPlugin "session-manager" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                floating true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                move_to_focused_tab true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            SwitchToMode "normal"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt left" { MoveFocusOrTab "left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt down" { MoveFocus "down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt up" { MoveFocus "up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt right" { MoveFocusOrTab "right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt +" { Resize "Increase"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt -" { Resize "Decrease"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt =" { Resize "Increase"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt [" { PreviousSwapLayout; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt ]" { NextSwapLayout; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt f" { ToggleFloatingPanes; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl g" { SwitchToMode "locked"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt h" { MoveFocusOrTab "left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt i" { MoveTab "left"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt j" { MoveFocus "down"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt k" { MoveFocus "up"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt l" { MoveFocusOrTab "right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt n" { NewPane; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt o" { MoveTab "right"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt p" { TogglePaneInGroup; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Alt Shift p" { ToggleGroupMarking; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl q" { Quit; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "move" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl h" { SwitchToMode "move"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "session" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl o" { SwitchToMode "session"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "scroll" "search" "tmux" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl b" { SwitchToMode "tmux"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "scroll" "search" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl s" { SwitchToMode "scroll"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "tab" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl t" { SwitchToMode "tab"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "pane" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl p" { SwitchToMode "pane"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "locked" "resize" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl r" { SwitchToMode "resize"; } // changed Ctrl n to Ctrl r to stop conflict with LazVim</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "normal" "locked" "entersearch" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "enter" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_except "normal" "locked" "entersearch" "renametab" "renamepane" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "esc" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_among "pane" "tmux" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "x" { CloseFocus; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_among "scroll" "search" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "PageDown" { PageScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "PageUp" { PageScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "left" { PageScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "down" { ScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "up" { ScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "right" { PageScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl b" { PageScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "d" { HalfPageScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl f" { PageScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "h" { PageScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "j" { ScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "k" { ScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "l" { PageScrollDown; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl s" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "u" { HalfPageScrollUp; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    entersearch {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl c" { SwitchToMode "scroll"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "esc" { SwitchToMode "scroll"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "enter" { SwitchToMode "search"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    renametab {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "esc" { UndoRenameTab; SwitchToMode "tab"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_among "renametab" "renamepane" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl c" { SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    renamepane {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "esc" { UndoRenamePane; SwitchToMode "pane"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shared_among "session" "tmux" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "d" { Detach; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tmux {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "left" { MoveFocus "left"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "down" { MoveFocus "down"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "up" { MoveFocus "up"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "right" { MoveFocus "right"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "space" { NextSwapLayout; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "\"" { NewPane "down"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "%" { NewPane "right"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "," { SwitchToMode "renametab"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "[" { SwitchToMode "scroll"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "Ctrl b" { Write 2; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "c" { NewTab; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "h" { MoveFocus "left"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "j" { MoveFocus "down"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "k" { MoveFocus "up"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "l" { MoveFocus "right"; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "n" { GoToNextTab; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "o" { FocusNextPane; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "p" { GoToPreviousTab; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">plugins {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    about location="zellij:about"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    compact-bar location="zellij:compact-bar"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    configuration location="zellij:configuration"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    filepicker location="zellij:strider" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        cwd "/"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    plugin-manager location="zellij:plugin-manager"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    session-manager location="zellij:session-manager"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    status-bar location="zellij:status-bar"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    strider location="zellij:strider"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tab-bar location="zellij:tab-bar"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    welcome-screen location="zellij:session-manager" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        welcome_screen true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">load_plugins {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">web_client {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    font "monospace"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">simplified_ui true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">theme "catppuccin-mocha"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">show_startup_tips false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pane_frames false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>The above is my current default for Zellij just cleans up a few things and add some key mappings that don't conflict with navigation in LazyVim</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-i-havent-picked-yet">Why I Haven't Picked (Yet)<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#why-i-havent-picked-yet" class="hash-link" aria-label="Direct link to Why I Haven't Picked (Yet)" title="Direct link to Why I Haven't Picked (Yet)" translate="no">​</a></h2>
<p>Which one fits into my workflow? I am still deciding. I think I prefer Zellij, however Tmux seems to be so popular that it's worth sticking with and giving a proper go. I like both — the thing that draws me to Zellij is the plug-and-play experience. Mirroring key bindings across both tools means I don't get into much of a fuss when switching between them, which is genuinely helpful.</p>
<p>What I love about Zellij over Tmux is the use of modes — Tab, Pane, Session, and so on. Having these dedicated modes helps with navigation and usage. Whereas Tmux requires a leader key, and finding one that is ergonomic and fits within a broader workflow is proving hard. I have settled on <code>Ctrl-s</code> for now — it's accessible and doesn't stomp on anything critical.</p>
<p>In terms of performance I haven't noticed much difference between the two. I'm not a power user; I generally use these tools to keep sessions alive and split panes while working in different tabs for different tasks.</p>
<p>For now I'll continue working with both until I've settled on one. One of the main reasons I wanted to give Tmux a proper go is because of <a href="https://www.anthropic.com/" target="_blank" rel="noopener noreferrer" class="">Anthropic</a> allowing Claude to spin up agents in Tmux sessions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrapping-up">Wrapping Up<a href="https://mrpbennett.dev/blog/2026/02/tmux-zellij#wrapping-up" class="hash-link" aria-label="Direct link to Wrapping Up" title="Direct link to Wrapping Up" translate="no">​</a></h2>
<p>Both tools will serve you well — the "right" answer really comes down to whether you value the massive ecosystem and scriptability of Tmux, or the modern UX and Rust-powered ergonomics of Zellij.</p>
<p>If you're just getting started, I'd say try Zellij first — the discoverability alone will save you hours of frustration. Once you've got a feel for what a multiplexer can do, come back to Tmux with fresh eyes. You might find, like me, that you want both in your toolkit.</p>]]></content:encoded>
            <category>terminal</category>
        </item>
        <item>
            <title><![CDATA[Managing Kubernetes Secrets the GitOps Way with Vault and VSO]]></title>
            <link>https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso</link>
            <guid>https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso</guid>
            <pubDate>Thu, 26 Feb 2026 12:13:10 GMT</pubDate>
            <description><![CDATA[GitOps is great right up until you need to store a secret. Everything else lives in Git — manifests, Helm charts, ArgoCD app definitions — but the moment you need a database password or an S3 key, you've got a problem. You absolutely cannot put credentials in a repository, no matter how private it is.]]></description>
            <content:encoded><![CDATA[<p>GitOps is great right up until you need to store a secret. Everything else lives in Git — manifests, Helm charts, ArgoCD app definitions — but the moment you need a database password or an S3 key, you've got a problem. You absolutely cannot put credentials in a repository, no matter how private it is.</p>
<p>That's the constraint. And solving it properly took me longer than I'd like to admit.</p>
<p>The answer is <a href="https://www.vaultproject.io/" target="_blank" rel="noopener noreferrer" class="">HashiCorp Vault</a>. Vault holds the actual secret values while your Git repo holds only a <em>reference</em> to them. The bridge between the two is the Vault Secrets Operator (VSO) — it reads those references from your cluster, fetches the real values from Vault, and creates native Kubernetes secrets that your pods consume without knowing anything has changed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-secret-zero-problem">The Secret Zero Problem<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#the-secret-zero-problem" class="hash-link" aria-label="Direct link to The Secret Zero Problem" title="Direct link to The Secret Zero Problem" translate="no">​</a></h2>
<p>The tension here is simple: GitOps says the desired state of your infrastructure lives in Git. Security says never put secrets in Git. You need something that satisfies both at once.</p>
<p>The pattern is: Git stores a reference to the secret (safe to commit), Vault stores the actual values, and an operator running in your cluster bridges the gap. Your pods just see a normal Kubernetes secret and don't care where it came from.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-vso">Why VSO?<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#why-vso" class="hash-link" aria-label="Direct link to Why VSO?" title="Direct link to Why VSO?" translate="no">​</a></h2>
<p>There are other tools that solve this problem. The External Secrets Operator (ESO) is the most popular — it's provider-agnostic and works with AWS Secrets Manager, GCP, Azure, and Vault alike. The Vault Agent Injector uses sidecar containers to push secrets into pods at runtime.</p>
<p>I went with VSO because Vault is my only secrets backend, and VSO is HashiCorp's own Kubernetes-native operator purpose-built for it. Fewer moving parts, and it has one feature I really wanted: automatic pod rollout restarts when a secret changes. With ESO you'd need to bolt on a separate tool like Reloader to get that. With VSO it's built in.</p>
<p>VSO also has first-class support for Vault's dynamic secrets, where Vault generates short-lived credentials on the fly rather than storing long-lived passwords — but that's a rabbit hole for another post.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-vault">Setting Up Vault<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#setting-up-vault" class="hash-link" aria-label="Direct link to Setting Up Vault" title="Direct link to Setting Up Vault" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploying-vault">Deploying Vault<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#deploying-vault" class="hash-link" aria-label="Direct link to Deploying Vault" title="Direct link to Deploying Vault" translate="no">​</a></h3>
<p>For a homelab or learning environment, the Helm chart with dev mode gets you going quickly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">helm repo add hashicorp https://helm.releases.hashicorp.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">helm install vault hashicorp/vault --set "server.dev.enabled=true"</span><br></span></code></pre></div></div>
<p>Dev mode means no unsealing ceremony and no TLS — it just works. You'd want HA mode with auto-unseal for production, but dev mode removes all the friction while you're learning the setup.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="enabling-a-secrets-engine">Enabling a Secrets Engine<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#enabling-a-secrets-engine" class="hash-link" aria-label="Direct link to Enabling a Secrets Engine" title="Direct link to Enabling a Secrets Engine" translate="no">​</a></h3>
<p>Vault organises secrets into engines. For static key-value secrets — which covers most application config — you want the <strong>KV</strong> engine. When you first open the Vault UI and navigate to Secrets Engines, you'll see a list that includes KV, Kubernetes, LDAP, PKI Certificates, and more. Pick <strong>KV</strong>. The Kubernetes engine is something else entirely; it generates Kubernetes service account tokens dynamically, which is a more advanced use case.</p>
<p>Choose <strong>Version 2</strong> when enabling KV — you get versioning, soft delete, and metadata at no extra complexity.</p>
<p>Or via the CLI:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vault secrets enable -path=kv kv-v2</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-secrets-in-the-vault-ui">Creating Secrets in the Vault UI<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#creating-secrets-in-the-vault-ui" class="hash-link" aria-label="Direct link to Creating Secrets in the Vault UI" title="Direct link to Creating Secrets in the Vault UI" translate="no">​</a></h3>
<p>You don't need to shell into the Vault pod to create secrets. The UI is perfectly fine for this, and it's the most intuitive way when you're getting started.</p>
<p>Navigate into your KV engine and click "Create secret." You'll fill in two things:</p>
<p><strong>Path</strong> — a hierarchical location for your secret, like a folder structure. Something like <code>rustfs/vault-test</code> or <code>homelab/grafana</code>. This is the path your VSO resources will reference later.</p>
<p><strong>Secret data</strong> — key-value pairs below the path. For example, an S3 configuration secret might look like:</p>
<table><thead><tr><th>Key</th><th>Value</th></tr></thead><tbody><tr><td><code>AWS_ACCESS_KEY_ID</code></td><td>your access key</td></tr><tr><td><code>AWS_SECRET_ACCESS_KEY</code></td><td>your secret key</td></tr><tr><td><code>S3_BUCKET</code></td><td>your bucket name</td></tr></tbody></table>
<p>Grouping related secrets under a single path is good practice — one VSO resource can pull them all into a single Kubernetes secret, and your deployments can mount everything at once with <code>envFrom</code>.</p>
<p>Beyond the UI, you can also manage secrets via the Vault CLI (set <code>VAULT_ADDR</code> and <code>VAULT_TOKEN</code>), the REST API, or Terraform for a fully declarative approach — more on that at the end.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-quick-note-on-cubbyhole">A Quick Note on Cubbyhole<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#a-quick-note-on-cubbyhole" class="hash-link" aria-label="Direct link to A Quick Note on Cubbyhole" title="Direct link to A Quick Note on Cubbyhole" translate="no">​</a></h3>
<p>Vault has a special engine called cubbyhole. This is <em>not</em> where you store application secrets — that's what KV is for. Cubbyhole is scoped entirely to a single Vault token. Only that exact token can read its own cubbyhole, not even root can peek in, and when the token expires the cubbyhole is gone with it.</p>
<p>Its main use case is secure secret introduction via response wrapping. An admin wraps a secret with a short TTL, hands the wrapping token to an application, and the application can unwrap it exactly once. If anyone intercepts and unwraps it first, the legitimate attempt fails — so you know it's been compromised. Think of KV as the filing cabinet and cubbyhole as a self-destructing envelope.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configuring-vault-authentication">Configuring Vault Authentication<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#configuring-vault-authentication" class="hash-link" aria-label="Direct link to Configuring Vault Authentication" title="Direct link to Configuring Vault Authentication" translate="no">​</a></h2>
<p>Before VSO can fetch anything, Vault needs to trust your Kubernetes cluster. This is the step that's easy to overlook and will result in <code>403 permission denied</code> errors if it's missing.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="enabling-kubernetes-auth">Enabling Kubernetes Auth<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#enabling-kubernetes-auth" class="hash-link" aria-label="Direct link to Enabling Kubernetes Auth" title="Direct link to Enabling Kubernetes Auth" translate="no">​</a></h3>
<p>Check if it's already enabled:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vault auth list</span><br></span></code></pre></div></div>
<p>If you only see <code>token/</code> in the list, Kubernetes auth isn't enabled. You can enable it through the UI under <strong>Access → Auth Methods → Enable new method → Kubernetes</strong>, or via the CLI:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vault auth enable kubernetes</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="configuring-the-auth-method">Configuring the Auth Method<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#configuring-the-auth-method" class="hash-link" aria-label="Direct link to Configuring the Auth Method" title="Direct link to Configuring the Auth Method" translate="no">​</a></h3>
<p>Once enabled, Vault needs to know how to talk to the Kubernetes API. In the UI, go to <strong>Access → Authentication Methods → kubernetes → Configure</strong> and set the <strong>Kubernetes host</strong> to:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">https://kubernetes.default.svc.cluster.local:443</span><br></span></code></pre></div></div>
<p>You can leave the CA certificate and Token Reviewer JWT fields blank if Vault is running as a pod in your cluster — it'll pick up the CA cert and service account JWT automatically from its own pod mount.</p>
<p>Via the CLI (port-forward if needed):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward svc/vault -n vault 8200:8200</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export VAULT_ADDR="http://127.0.0.1:8200"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vault write auth/kubernetes/config \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kubernetes_host="https://kubernetes.default.svc.cluster.local:443"</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-policies">Creating Policies<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#creating-policies" class="hash-link" aria-label="Direct link to Creating Policies" title="Direct link to Creating Policies" translate="no">​</a></h3>
<p>Policies control what secrets a role can access. Create one per application or group of secrets. In the UI, go to <strong>Policies → Create ACL policy</strong>:</p>
<ul>
<li class=""><strong>Name:</strong> <code>rustfs-read</code></li>
<li class=""><strong>Policy:</strong></li>
</ul>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">path "kv/data/rustfs/*" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  capabilities = ["read"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Don't be tempted to use the <code>default</code> policy for this. The <code>default</code> policy is attached to every token in Vault, so adding secret paths to it means every token in your cluster can access those secrets — the opposite of least-privilege.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-roles">Creating Roles<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#creating-roles" class="hash-link" aria-label="Direct link to Creating Roles" title="Direct link to Creating Roles" translate="no">​</a></h3>
<p>Roles bind Kubernetes service accounts to Vault policies. In the UI, go to <strong>Access → kubernetes → Create role</strong>:</p>
<ul>
<li class=""><strong>Name:</strong> <code>vso-role</code></li>
<li class=""><strong>Bound service account names:</strong> <code>default</code></li>
<li class=""><strong>Bound service account namespaces:</strong> each namespace where you'll use VSO (e.g. <code>cnpg</code>, <code>rustfs</code>, <code>default</code>)</li>
<li class=""><strong>Token policies:</strong> <code>rustfs-read</code></li>
<li class=""><strong>Token TTL:</strong> <code>3600</code></li>
</ul>
<p>This tells Vault: "when a pod using the <code>default</code> service account in the <code>cnpg</code> namespace presents a JWT, give it a token with the <code>rustfs-read</code> policy."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="installing-vso">Installing VSO<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#installing-vso" class="hash-link" aria-label="Direct link to Installing VSO" title="Direct link to Installing VSO" translate="no">​</a></h2>
<p>VSO installs into its own namespace and watches across the cluster for its custom resources:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">helm install vault-secrets-operator hashicorp/vault-secrets-operator \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -n vault-secrets-operator-system --create-namespace</span><br></span></code></pre></div></div>
<p>Verify the CRDs it installed:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get crds | grep vault</span><br></span></code></pre></div></div>
<p>As of VSO 1.3.0 you'll see <code>vaultconnections</code>, <code>vaultauths</code>, <code>vaultstaticsecrets</code>, and <code>vaultdynamicsecrets</code>. Note that <code>ClusterVaultConnection</code> isn't available in this version — you'll use namespace-scoped resources instead. A small amount of repetition across namespaces, but it keeps things explicit and easy to reason about.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-gitops-manifests">The GitOps Manifests<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#the-gitops-manifests" class="hash-link" aria-label="Direct link to The GitOps Manifests" title="Direct link to The GitOps Manifests" translate="no">​</a></h2>
<p>This is where it all comes together. You need three custom resources, and they all go in your <strong>workload namespace</strong> — not the Vault namespace, not the VSO namespace. VSO watches across namespaces just like how Flux runs in <code>flux-system</code> but manages resources everywhere.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="vaultconnection--where-is-vault">VaultConnection — Where is Vault?<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#vaultconnection--where-is-vault" class="hash-link" aria-label="Direct link to VaultConnection — Where is Vault?" title="Direct link to VaultConnection — Where is Vault?" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> secrets.hashicorp.com/v1beta1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> VaultConnection</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vault</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">connection</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cnpg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">address</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//vault.vault.svc.cluster.local</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">8200</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="vaultauth--how-do-i-authenticate">VaultAuth — How do I authenticate?<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#vaultauth--how-do-i-authenticate" class="hash-link" aria-label="Direct link to VaultAuth — How do I authenticate?" title="Direct link to VaultAuth — How do I authenticate?" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> secrets.hashicorp.com/v1beta1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> VaultAuth</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vault</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">auth</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cnpg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">method</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubernetes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">mount</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kubernetes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">kubernetes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">role</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vso</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">role</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">serviceAccount</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">vaultConnectionRef</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vault</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">connection</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="vaultstaticsecret--what-do-i-fetch-and-where-do-i-put-it">VaultStaticSecret — What do I fetch and where do I put it?<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#vaultstaticsecret--what-do-i-fetch-and-where-do-i-put-it" class="hash-link" aria-label="Direct link to VaultStaticSecret — What do I fetch and where do I put it?" title="Direct link to VaultStaticSecret — What do I fetch and where do I put it?" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> secrets.hashicorp.com/v1beta1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> VaultStaticSecret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rustfs</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cloudnativepg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cnpg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">vaultAuthRef</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vault</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">auth</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">mount</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kv</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kv</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">v2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rustfs/cloudnativepg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">refreshAfter</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 60s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">destination</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rustfs</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cloudnativepg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">create</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">rolloutRestartTargets</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">app</span><br></span></code></pre></div></div>
<p>A few things worth calling out here:</p>
<p><strong><code>mount: kv</code></strong> must match the path where you mounted the KV engine in Vault. If you see <code>kv/</code> in the Vault UI breadcrumb, use <code>kv</code> here.</p>
<p><strong><code>destination.name</code></strong> is the name of the Kubernetes secret VSO will create — this is what your deployments reference. It doesn't need to match the Vault path; name it whatever makes sense for your workloads.</p>
<p><strong><code>destination.create: true</code></strong> tells VSO it's allowed to create the secret if it doesn't already exist.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-rolloutrestarttargets">Understanding rolloutRestartTargets<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#understanding-rolloutrestarttargets" class="hash-link" aria-label="Direct link to Understanding rolloutRestartTargets" title="Direct link to Understanding rolloutRestartTargets" translate="no">​</a></h3>
<p>This is one of VSO's standout features. When you update a secret in Vault, VSO will detect the change and update the Kubernetes secret — but your running pods still have the old values loaded in memory. Without something to restart them, they'll keep using stale credentials until the next deploy.</p>
<p><code>rolloutRestartTargets</code> solves this by telling VSO to automatically trigger a rolling restart of the specified workloads whenever the secret changes. Update your <code>AWS_SECRET_ACCESS_KEY</code> in the Vault UI, and within 60 seconds (based on <code>refreshAfter</code>) VSO picks up the change, updates the Kubernetes secret, and rolls new pods with the fresh values. No manual intervention, no Reloader, nothing extra needed.</p>
<p>You can target multiple workloads if the same secret is shared across them:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">rolloutRestartTargets</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rustfs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> another</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> StatefulSet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> some</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">database</span><br></span></code></pre></div></div>
<p>It's optional — leave it out and VSO will still keep the Kubernetes secret in sync; you'd just need to restart pods yourself to pick up changes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-world-example-cloudnativepg-backups">Real-World Example: CloudNativePG Backups<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#real-world-example-cloudnativepg-backups" class="hash-link" aria-label="Direct link to Real-World Example: CloudNativePG Backups" title="Direct link to Real-World Example: CloudNativePG Backups" translate="no">​</a></h2>
<p>Here's how this fits together in practice. CloudNativePG needs S3 credentials for WAL archiving and backups. The CNPG cluster spec expects a secret with specific key names:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">backup</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">retentionPolicy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1d"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">barmanObjectStore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">destinationPath</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> s3</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//cloudnativepg</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dev</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">backup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">endpointURL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//rustfs</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">svc.rustfs.svc.cluster.local</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">9000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">s3Credentials</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">accessKeyId</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rustfs</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cloudnativepg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> access_key</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">secretAccessKey</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> rustfs</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cloudnativepg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> access_secret_key</span><br></span></code></pre></div></div>
<p>The things that need to match are the secret <strong>name</strong> (<code>rustfs-cloudnativepg</code>) and the <strong>key names</strong> (<code>access_key</code>, <code>access_secret_key</code>). So in Vault, create a secret at path <code>rustfs/cloudnativepg</code> with exactly those key names. Then the <code>VaultStaticSecret</code> with <code>destination.name: rustfs-cloudnativepg</code> creates the Kubernetes secret that CNPG can consume directly — no changes needed on the CNPG side.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reducing-boilerplate">Reducing Boilerplate<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#reducing-boilerplate" class="hash-link" aria-label="Direct link to Reducing Boilerplate" title="Direct link to Reducing Boilerplate" translate="no">​</a></h2>
<p>Three resource types per namespace sounds like a lot, but in practice you share the <code>VaultConnection</code> and <code>VaultAuth</code> across all apps within the same namespace. You only need one <code>VaultStaticSecret</code> per secret path.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cnpg namespace:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ├── VaultConnection  (one per namespace)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ├── VaultAuth        (one per namespace)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ├── VaultStaticSecret - rustfs-cloudnativepg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ├── VaultStaticSecret - another-secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  └── VaultStaticSecret - yet-another-secret</span><br></span></code></pre></div></div>
<p>Each <code>VaultStaticSecret</code> references the same <code>vaultAuthRef: vault-auth</code>. Adding a new secret to an existing namespace is just one more file.</p>
<p>For a brand new namespace, you need the <code>VaultConnection</code> and <code>VaultAuth</code> too — two extra files. And don't forget to add the namespace to your Vault role's <code>bound_service_account_namespaces</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="common-pitfalls">Common Pitfalls<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#common-pitfalls" class="hash-link" aria-label="Direct link to Common Pitfalls" title="Direct link to Common Pitfalls" translate="no">​</a></h2>
<p>Working through this setup for the first time, a few things caught me out.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="permission-denied-on-login-403">"permission denied" on login (403)<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#permission-denied-on-login-403" class="hash-link" aria-label="Direct link to &quot;permission denied&quot; on login (403)" title="Direct link to &quot;permission denied&quot; on login (403)" translate="no">​</a></h3>
<p>This almost always means Kubernetes auth isn't configured properly — or isn't enabled at all. Run <code>vault auth list</code> and check if <code>kubernetes/</code> appears. If it doesn't, enable it. If it does, verify the Kubernetes host is set correctly in the auth config.</p>
<p>One important debugging tip: <strong>check the VSO controller logs directly</strong>, not just the resource status. The <code>describe</code> output on a <code>VaultStaticSecret</code> often shows a generic <code>permission denied</code> on the secret read, which points you at the wrong thing. The actual VSO logs will tell you the real failure — for instance, <code>namespace not authorized</code> on the <em>login</em> step, which is a completely different issue than a missing read policy. To get them:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl logs -n vault-secrets-operator-system \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  deployment/vault-secrets-operator-controller-manager</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="invalid-role-name-400">"invalid role name" (400)<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#invalid-role-name-400" class="hash-link" aria-label="Direct link to &quot;invalid role name&quot; (400)" title="Direct link to &quot;invalid role name&quot; (400)" translate="no">​</a></h3>
<p>You've enabled Kubernetes auth but haven't created the role yet. The auth method exists, but Vault doesn't know what <code>vso-role</code> is. Create the role via the UI (Access → kubernetes → Create role) or CLI.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="vaultauth-not-found">"VaultAuth not found"<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#vaultauth-not-found" class="hash-link" aria-label="Direct link to &quot;VaultAuth not found&quot;" title="Direct link to &quot;VaultAuth not found&quot;" translate="no">​</a></h3>
<p>The <code>VaultAuth</code> and <code>VaultConnection</code> resources must be in the <strong>same namespace</strong> as your <code>VaultStaticSecret</code>. They don't go in the Vault namespace or the VSO operator namespace.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cant-reach-vault-from-the-cli">Can't reach Vault from the CLI<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#cant-reach-vault-from-the-cli" class="hash-link" aria-label="Direct link to Can't reach Vault from the CLI" title="Direct link to Can't reach Vault from the CLI" translate="no">​</a></h3>
<p>If <code>vault login</code> fails with a network error, you're probably trying to hit Vault over an external URL that isn't routable from your machine. Use port-forwarding:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl port-forward svc/vault -n vault 8200:8200</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export VAULT_ADDR="http://127.0.0.1:8200"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">vault login</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="wildcard-namespace-binding-silently-fails">Wildcard namespace binding silently fails<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#wildcard-namespace-binding-silently-fails" class="hash-link" aria-label="Direct link to Wildcard namespace binding silently fails" title="Direct link to Wildcard namespace binding silently fails" translate="no">​</a></h3>
<p>If your Vault role has <code>bound_service_account_namespaces</code> set to <code>"*"</code> (with quotes), no namespace will ever match. This can happen when configuring the role through the Vault UI — it stores a literal quoted string rather than the wildcard. Every VSO login attempt will return <code>namespace not authorized</code> even though the auth method and policy are correct.</p>
<p>Via the CLI:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">vault read auth/kubernetes/role/vso-role</span><br></span></code></pre></div></div>
<p>Check the <code>bound_service_account_namespaces</code> field. If you see <code>"*"</code> rather than <code>*</code>, update the role. This is exactly the kind of subtle drift that Terraform catches automatically — it would flag the difference in <code>terraform plan</code> before you ever apply.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="vso-caches-auth-failures-aggressively">VSO caches auth failures aggressively<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#vso-caches-auth-failures-aggressively" class="hash-link" aria-label="Direct link to VSO caches auth failures aggressively" title="Direct link to VSO caches auth failures aggressively" translate="no">​</a></h3>
<p>If you fix a Vault-side misconfiguration (wrong policy name, bad namespace binding, missing secret path) but the <code>VaultStaticSecret</code> still won't sync, the VSO controller may be caching the earlier failure and has stopped retrying. Restarting it clears the state:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl rollout restart deployment -n vault-secrets-operator-system \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  vault-secrets-operator-controller-manager</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="clustervaultconnection-not-found">ClusterVaultConnection not found<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#clustervaultconnection-not-found" class="hash-link" aria-label="Direct link to ClusterVaultConnection not found" title="Direct link to ClusterVaultConnection not found" translate="no">​</a></h3>
<p>If you try to use a <code>ClusterVaultConnection</code> and get a CRD not found error, your version of VSO doesn't support it. As of VSO 1.3.0, this CRD isn't available. Use namespace-scoped <code>VaultConnection</code> resources instead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="managing-vault-configuration-with-terraform">Managing Vault Configuration with Terraform<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#managing-vault-configuration-with-terraform" class="hash-link" aria-label="Direct link to Managing Vault Configuration with Terraform" title="Direct link to Managing Vault Configuration with Terraform" translate="no">​</a></h2>
<p>Once you've been through the manual setup and understand how the pieces fit together, the natural next step is managing Vault's configuration declaratively with Terraform. Auth methods, policies, roles, and secret paths all become version-controlled — which is exactly where you want them in a GitOps setup.</p>
<p>The manual setup taught me an important lesson the hard way: the Vault UI introduces subtle bugs that are nearly impossible to spot. Setting <code>bound_service_account_namespaces</code> to <code>*</code> in the UI stored it as the literal string <code>"*"</code> — which means no namespace ever matches. Every VSO login failed with <code>namespace not authorized</code>, and the only way I found the issue was digging through the operator logs and then running <code>vault read auth/kubernetes/role/vso-role</code> to inspect the raw values. <code>terraform plan</code> would have flagged this immediately.</p>
<p>After that incident I moved the entire Vault configuration to Terraform. If you already have a manual setup, you can import it rather than recreating from scratch:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform import vault_mount.kv kv</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform import vault_auth_backend.kubernetes kubernetes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform import vault_policy.rustfs_read rustfs-read</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform import vault_kubernetes_auth_backend_role.vso_role auth/kubernetes/role/vso-role</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform import vault_kubernetes_auth_backend_config.config auth/kubernetes/config</span><br></span></code></pre></div></div>
<p>Then <code>terraform plan</code> will surface any drift between your actual Vault config and the desired state — wrong policy names, quoted wildcards, missing secrets — before it becomes a runtime problem.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="provider-setup">Provider Setup<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#provider-setup" class="hash-link" aria-label="Direct link to Provider Setup" title="Direct link to Provider Setup" translate="no">​</a></h3>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">terraform {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  required_providers {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    vault = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      source  = "hashicorp/vault"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      version = "~&gt; 4.0"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">provider "vault" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  address = "http://127.0.0.1:8200"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  # Token supplied via VAULT_TOKEN env var</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="secrets-engine">Secrets Engine<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#secrets-engine" class="hash-link" aria-label="Direct link to Secrets Engine" title="Direct link to Secrets Engine" translate="no">​</a></h3>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "vault_mount" "kv" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  path        = "kv"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type        = "kv"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  options     = { version = "2" }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  description = "KV v2 secrets engine"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="kubernetes-auth">Kubernetes Auth<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#kubernetes-auth" class="hash-link" aria-label="Direct link to Kubernetes Auth" title="Direct link to Kubernetes Auth" translate="no">​</a></h3>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "vault_auth_backend" "kubernetes" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type = "kubernetes"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">resource "vault_kubernetes_auth_backend_config" "config" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  backend         = vault_auth_backend.kubernetes.path</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kubernetes_host = "https://kubernetes.default.svc.cluster.local:443"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="policies">Policies<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#policies" class="hash-link" aria-label="Direct link to Policies" title="Direct link to Policies" translate="no">​</a></h3>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "vault_policy" "rustfs_read" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name   = "rustfs-read"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  policy = &lt;&lt;EOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">path "kv/data/rustfs/*" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  capabilities = ["read"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOT</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="roles">Roles<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#roles" class="hash-link" aria-label="Direct link to Roles" title="Direct link to Roles" translate="no">​</a></h3>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "vault_kubernetes_auth_backend_role" "vso_role" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  backend                          = vault_auth_backend.kubernetes.path</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  role_name                        = "vso-role"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  bound_service_account_names      = ["default"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  bound_service_account_namespaces = ["cnpg", "rustfs", "default"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  token_ttl                        = 3600</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  token_policies                   = ["rustfs-read"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="secrets">Secrets<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#secrets" class="hash-link" aria-label="Direct link to Secrets" title="Direct link to Secrets" translate="no">​</a></h3>
<div class="language-hcl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-hcl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">resource "vault_kv_secret_v2" "rustfs_cloudnativepg" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  mount = vault_mount.kv.path</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name  = "rustfs/cloudnativepg"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  data_json = jsonencode({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    access_key        = var.cnpg_access_key</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    access_secret_key = var.cnpg_secret_access_key</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  })</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">variable "cnpg_access_key" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type      = string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  sensitive = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">variable "cnpg_secret_access_key" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type      = string</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  sensitive = true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>The actual values get supplied via environment variables or a <code>terraform.tfvars</code> file that stays out of Git:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export TF_VAR_cnpg_access_key="your-access-key"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export TF_VAR_cnpg_secret_access_key="your-secret-key"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export VAULT_TOKEN="your-root-token"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">terraform apply</span><br></span></code></pre></div></div>
<p>The entire Vault configuration — auth methods, policies, roles, secret paths — is now declarative and version-controlled. The only things not in Git are the actual secret values and the Vault token. As your homelab grows, adding a new application's secrets is just a few more Terraform resources.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-end-to-end-flow">The End-to-End Flow<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#the-end-to-end-flow" class="hash-link" aria-label="Direct link to The End-to-End Flow" title="Direct link to The End-to-End Flow" translate="no">​</a></h2>
<p>Putting it all together:</p>
<ol>
<li class="">Create or update a secret in the Vault UI (or CLI, API, or Terraform)</li>
<li class="">Your Git repo contains the VaultConnection, VaultAuth, VaultStaticSecret, and Deployment manifests</li>
<li class="">Flux or ArgoCD syncs these manifests to the cluster</li>
<li class="">VSO picks up the VaultStaticSecret, authenticates to Vault via the Kubernetes auth method, and fetches the real values</li>
<li class="">VSO creates a native Kubernetes secret in the workload namespace</li>
<li class="">Your pods consume it like any other Kubernetes secret</li>
<li class="">If the secret changes in Vault, VSO detects it on the next refresh cycle, updates the Kubernetes secret, and restarts your pods if you've configured <code>rolloutRestartTargets</code></li>
</ol>
<p>Git remains the single source of truth for <em>what</em> secrets your application needs and <em>where</em> they come from. Vault remains the single source of truth for the secret <em>values</em>. Neither bleeds into the other.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's Next<a href="https://mrpbennett.dev/blog/kubernetes-secrets-vault-vso#whats-next" class="hash-link" aria-label="Direct link to What's Next" title="Direct link to What's Next" translate="no">​</a></h2>
<p>Once this pattern is comfortable, there are natural next steps. Dynamic secrets let Vault generate short-lived database credentials on the fly using <code>VaultDynamicSecret</code> resources — no more long-lived passwords. Vault's PKI engine can act as your own certificate authority. Transit encryption lets your applications encrypt and decrypt data through Vault's API without ever handling encryption keys directly.</p>
<p>For getting secrets out of your Git repo and into your cluster safely though, VSO with static KV secrets is a solid foundation that scales well as your homelab grows.</p>]]></content:encoded>
            <category>kubernetes</category>
        </item>
        <item>
            <title><![CDATA[Setting up HorizontalPodAutoscaler in Kubernetes]]></title>
            <link>https://mrpbennett.dev/blog/setting-up-horizontalpodautoscaler-in-kubernete</link>
            <guid>https://mrpbennett.dev/blog/setting-up-horizontalpodautoscaler-in-kubernete</guid>
            <pubDate>Thu, 21 Nov 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[scaling a hpa]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="scaling a hpa" src="https://mrpbennett.dev/assets/images/vpa-vs-hpa-02-cd9c5fd66cc8954fa5946598ceeba133.png" width="800" height="350" class="img_ev3q"></p>
<p>Today I have set up HorizontalPodAutoscaler (HPA) on a basic level. What is HorizontalPodAutoscaler you ask! Well, HorizontalPodAutoscaler allows your application to well, scale...horizontally. HPA adjusts the number of pods running your app to match how much work needs to be done. In the words of ChatGPT here is the ELI5 version:</p>
<blockquote>
<p>Imagine you have a lemonade stand, and on a hot day, a lot of people come to buy lemonade. If you're working alone, you might get overwhelmed. So, you decide to call your friends to help when the line gets too long. When it gets less busy, your friends go back home.</p>
<p>A HorizontalPodAutoscaler (HPA) in Kubernetes works like that. It watches your application (like the lemonade stand) and sees how "busy" it is — for example, by checking how much CPU or memory it's using. If things get too busy, it automatically adds more helpers (called "pods") to handle the extra work. When things slow down, it reduces the number of helpers to save resources.</p>
</blockquote>
<p>Horizontal scaling means that the response to increased load is to deploy more Pods. This is different from vertical scaling, which for Kubernetes would mean assigning more resources (for example: memory or CPU) to the Pods that are already running for the workload.</p>
<p><img decoding="async" loading="lazy" alt="HPA example" src="https://mrpbennett.dev/assets/images/flow-fa8f712c0b33d473ca1efe545a7cdef2.png" width="1146" height="630" class="img_ev3q"></p>
<p>Kubernetes implements horizontal pod autoscaling as a control loop that runs intermittently (it is not a continuous process). The interval is set by the <code>--horizontal-pod-autoscaler-sync-period</code> parameter to the <a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/" target="_blank" rel="noopener noreferrer" class=""><code>kube-controller-manager</code></a> (and the default interval is 15 seconds).</p>
<p>Once during each period, the controller manager queries the resource utilization against the metrics specified in each <code>HorizontalPodAutoscaler</code> definition. The controller manager finds the target resource defined by the <code>scaleTargetRef</code>, then selects the pods based on the target resource's <code>.spec.selector</code> labels, and obtains the metrics from either the resource metrics API (for per-pod resource metrics), or the custom metrics API (for all other metrics).</p>
<p>With the introduction to v2 of the <code>autoscaling</code> API this allows for any HPA target can be scaled based on the resource usage of the pods in the scaling target. When defining the pod specification the resource requests like CPU and memory should be specified. Therefore if you're application is CPU resource intensive you can use <code>resource.name: cpu</code> or if it's memory heavy use <code>resource.name: memory</code> then the HPA will scale the target up or down depending on resource utilisation based scaling specify a metric source like this:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Resource</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">resource</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">target</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Utilization</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">averageUtilization</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><br></span></code></pre></div></div>
<p>The HPA controller will keep the average utilization of the pods in the scaling target at 60%. Utilization is the ratio between the current usage of resources to the requested resources of the pod.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploying-the-application">Deploying the application<a href="https://mrpbennett.dev/blog/setting-up-horizontalpodautoscaler-in-kubernete#deploying-the-application" class="hash-link" aria-label="Direct link to Deploying the application" title="Direct link to Deploying the application" translate="no">​</a></h2>
<p>To get started I went ahead and deployed a simple nginx application, this application only has the one replica. I am hoping the HPA will scale this up when I overload it. Within the file is also a simple service that exposes the nginx app via a <code>LoadBalancer</code>. This will only work if you're running something like <a href="https://metallb.universe.tf/" target="_blank" rel="noopener noreferrer" class="">MetalLB</a> or <a href="https://kube-vip.io/docs/usage/kubernetes-services/" target="_blank" rel="noopener noreferrer" class="">KubeVIP</a>.</p>
<p>For the HPA to actually work <code>spec.spec.resources</code> will need to be defined. You will also need some sort of metrics to be used. In my cluster, I have set up <a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack" target="_blank" rel="noopener noreferrer" class=""><code>kube-prometheus-stack</code></a></p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">matchLabels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">template</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">containerPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'100m'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'128Mi'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">cpu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'200m'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'256Mi'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">service</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">selector</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TCP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">targetPort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> LoadBalancer</span><br></span></code></pre></div></div>
<p>Once the application has been deployed we can check that it's running.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">NAME                                READY   STATUS    RESTARTS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-deployment-794f949fb8-zgl4l   1/1     Running   0          10m</span><br></span></code></pre></div></div>
<p>Now that the pod is up and running. The HPA can be created in the example below will scale on CPU usage, the HPA will scale when the pod uses more than 20% of its CPU and scale up to 5 pods.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> autoscaling/v2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> HorizontalPodAutoscaler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">hpa</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">scaleTargetRef</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">minReplicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">maxReplicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">metrics</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Resource</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">resource</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">target</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Utilization</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">averageUtilization</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><br></span></code></pre></div></div>
<p>Now deploy the HPA.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f hpa.yaml</span><br></span></code></pre></div></div>
<p>Once deployed, check that it's running and describe to see the events. As this has just been deployed there are no events currently which is expected as the application hasn't had any excessive load.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get hpa -n nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">NAME        REFERENCE                     TARGETS       MINPODS   MAXPODS   REPLICAS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-hpa   Deployment/nginx-deployment   cpu: 0%/40%   1         5         1          3m44s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl describe hpa nginx-hpa -n nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Name:                                                  nginx-hpa</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Namespace:                                             test-applications</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Labels:                                                app.kubernetes.io/instance=test-applications</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Annotations:                                           &lt;none&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CreationTimestamp:                                     Thu, 21 Nov 2024 10:36:28 +0000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Reference:                                             Deployment/nginx-deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Metrics:                                               ( current / target )</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> resource cpu on pods (as a percentage of request):  0% (0) / 40%</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Min replicas:                                          1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Max replicas:                                          5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Deployment pods:                                       1 current / 1 desired</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Conditions:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Type            Status  Reason               Message</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ----            ------  ------               -------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> AbleToScale     True    ScaleDownStabilized  recent recommendations were higher than current one, applying the highest recent recommendation</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ScalingActive   True    ValidMetricFound     the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ScalingLimited  False   DesiredWithinRange   the desired count is within the acceptable range</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Events:           &lt;none&gt;</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="testing-it-out">Testing it out<a href="https://mrpbennett.dev/blog/setting-up-horizontalpodautoscaler-in-kubernete#testing-it-out" class="hash-link" aria-label="Direct link to Testing it out" title="Direct link to Testing it out" translate="no">​</a></h2>
<p>Everything has been deployed and is in a state where the HPA isn't needed. It's time to see how the autoscaler reacts to increased load. To do this, I'll be using <a href="https://github.com/rakyll/hey" target="_blank" rel="noopener noreferrer" class="">hey</a> to run this install via Homebrew <code>brew install hey</code></p>
<p>Because the nginx service has been exposed by a <code>LoadBalancer</code> it allows the use of the external IP.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get svc -n nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">NAME            TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-service   LoadBalancer   10.102.26.86   192.168.5.54   80:30141/TCP   10m</span><br></span></code></pre></div></div>
<p>With the application being in its standard state we can see the single pod in ArgoCD</p>
<p><img decoding="async" loading="lazy" alt="single_pod_argocd" src="https://mrpbennett.dev/assets/images/before_hpa-2f79c5f68b4e6ba940d978e667251479.png" width="2762" height="1034" class="img_ev3q"></p>
<p>With hey installed its time to test the application scaling, run the following:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">hey -n 10000 -c 100 http://192.168.5.54</span><br></span></code></pre></div></div>
<ul>
<li class=""><code>-n 100000</code>: Total number of requests to send.</li>
<li class=""><code>-c 1000</code>: Number of concurrent requests.</li>
</ul>
<p>This command generates sufficient load to increase CPU usage and triggers scaling.</p>
<p>After running the above now run, give it a few minutes before things start to change:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get hpa nginx-hpa -n nginx --watch</span><br></span></code></pre></div></div>
<p>Then in a separate terminal run the following to see the pods scale up:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get deployment -l app=nginx -w</span><br></span></code></pre></div></div>
<p>After a few we can see that hey increased the CPU usage to 169% which then started to reduce.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME        REFERENCE                     TARGETS       MINPODS   MAXPODS   REPLICAS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-hpa   Deployment/nginx-deployment   cpu: 0%/20%   1         5         2          13m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-hpa   Deployment/nginx-deployment   cpu: 169%/20%   1         5         2          14m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-hpa   Deployment/nginx-deployment   cpu: 72%/20%    1         5         4          14m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-hpa   Deployment/nginx-deployment   cpu: 0%/20%     1         5         5          14m</span><br></span></code></pre></div></div>
<p>After a while the pods will have increased to their maximum to allow for the increase resource consumption.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME               READY   UP-TO-DATE   AVAILABLE   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">nginx-deployment   5/5     5            5           14m</span><br></span></code></pre></div></div>
<p>Looking back at ArgoCD the 5 pods are there.</p>
<p><img decoding="async" loading="lazy" alt="more_pods_in_argocd" src="https://mrpbennett.dev/assets/images/after_hpa-30bebadc5da35f7ccf96466284688f03.png" width="2762" height="1034" class="img_ev3q"></p>
<p>Once the load has decreased on the application, the pods will scale down back to normal...And that's it! A very handy tool to prevent your application from slowing down or breaking all together.</p>]]></content:encoded>
            <category>kubernetes</category>
        </item>
    </channel>
</rss>