<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nikolai Emil | Devantler | Blog</title><description>Personal site of Nikolai Emil Damm — software engineer, open-source advocate, and Kubernetes enthusiast.</description><link>https://devantler.tech/</link><language>en</language><item><title>Storing Sensitive Values in .zshrc with macOS Keychain</title><link>https://devantler.tech/blog/storing-secrets-in-zshrc-with-macos-keychain/</link><guid isPermaLink="true">https://devantler.tech/blog/storing-secrets-in-zshrc-with-macos-keychain/</guid><description>A quick guide on using macOS Keychain to avoid storing secrets in plaintext in your .zshrc.

</description><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Storing tokens and passwords directly in &lt;code dir=&quot;auto&quot;&gt;~/.zshrc&lt;/code&gt; means they sit on disk in plaintext. macOS Keychain provides a built-in, encrypted alternative. Here’s how to use it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;store-a-secret&quot;&gt;Store a Secret&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Use the &lt;code dir=&quot;auto&quot;&gt;security&lt;/code&gt; CLI to add a value to your Keychain:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;security&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add-generic-password&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$USER&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;my_secret_name&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-w&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;SECRET_VALUE&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;Prepend the command with a space (note the leading space above) to prevent it from being saved in your &lt;code dir=&quot;auto&quot;&gt;.zsh_history&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-a &quot;$USER&quot;&lt;/code&gt; — associates the entry with your macOS user account.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-s &apos;my_secret_name&apos;&lt;/code&gt; — a label to identify the secret (e.g., &lt;code dir=&quot;auto&quot;&gt;GITHUB_TOKEN&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-w &apos;SECRET_VALUE&apos;&lt;/code&gt; — the actual secret value.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;retrieve-a-secret&quot;&gt;Retrieve a Secret&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To retrieve the value later:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;security&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;find-generic-password&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$USER&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;my_secret_name&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-w&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This prints the secret to stdout, making it easy to capture in a variable.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;use-it-in-zshrc&quot;&gt;Use It in .zshrc&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Export the secret as an environment variable by adding this to &lt;code dir=&quot;auto&quot;&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;security&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;find-generic-password&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$USER&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-w&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;/div&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Task&lt;/th&gt;&lt;th&gt;Command&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Store&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;security add-generic-password -a &quot;$USER&quot; -s &apos;name&apos; -w &apos;value&apos;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Retrieve&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;security find-generic-password -a &quot;$USER&quot; -s &apos;name&apos; -w&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Delete and re-add, or use &lt;code dir=&quot;auto&quot;&gt;-U&lt;/code&gt; flag to update in place&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Delete&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;security delete-generic-password -a &quot;$USER&quot; -s &apos;name&apos;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;That’s it — no more plaintext secrets in your &lt;code dir=&quot;auto&quot;&gt;.zshrc&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>macos</category><category>security</category><category>zsh</category><category>developer-experience</category></item><item><title>Building an AI Assistant for Kubernetes with GitHub Copilot SDK</title><link>https://devantler.tech/blog/building-an-ai-assistant-for-kubernetes-with-github-copilot-sdk/</link><guid isPermaLink="true">https://devantler.tech/blog/building-an-ai-assistant-for-kubernetes-with-github-copilot-sdk/</guid><description>How I built KSail&apos;s interactive AI chat assistant using Go, GitHub Copilot SDK, and Bubbletea TUI framework.

</description><pubDate>Sun, 25 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;What if managing Kubernetes clusters was as simple as having a conversation?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt; has always been about reducing overhead. It started as shell scripts, became a &lt;a href=&quot;https://devantler.tech/blog/building-ksail-from-shell-to-dotnet-to-go/&quot;&gt;.NET tool with embedded binaries&lt;/a&gt;, and is now a pure Go SDK that bundles kubectl, helm, kind, k3d, flux, and argocd into a single binary. Each iteration removed friction: first installation overhead (one binary instead of six tools), then workflow overhead (consistent commands across distributions).&lt;/p&gt;
&lt;p&gt;The chat feature is the next step: &lt;strong&gt;removing cognitive overhead&lt;/strong&gt;. Instead of memorizing that &lt;code dir=&quot;auto&quot;&gt;ksail cluster init --distribution Talos --provider Hetzner --cni Cilium --gitops-engine Flux --local-registry &apos;${GITHUB_USER}:${GITHUB_TOKEN}@ghcr.io/devantler-tech/ksail/system-test-manifests&apos; --control-planes 3 --workers 2&lt;/code&gt; is the syntax to create a Talos cluster on Hetzner with three control plane nodes, two worker nodes, Cilium as a CNI, and Flux as your GitOps engine set up to sync with an OCI image in GHCR, you can just say: “Create a HA Talos cluster on Hetzner with Cilium and Flux set up to sync with ghcr.io/devantler-tech/ksail/system-test-manifests.” (pheeew).&lt;/p&gt;
&lt;p&gt;This post covers the technical journey of building &lt;code dir=&quot;auto&quot;&gt;ksail chat&lt;/code&gt; using the &lt;a href=&quot;https://github.com/github/copilot-sdk-go&quot;&gt;GitHub Copilot SDK for Go&lt;/a&gt;, &lt;a href=&quot;https://github.com/charmbracelet/bubbletea&quot;&gt;Bubbletea&lt;/a&gt; TUI framework, and &lt;a href=&quot;https://github.com/charmbracelet/glamour&quot;&gt;glamour&lt;/a&gt; for markdown rendering.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;see-it-in-action&quot;&gt;See It in Action&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Before diving into the implementation, let’s see what we’re building. Here’s &lt;code dir=&quot;auto&quot;&gt;ksail chat&lt;/code&gt; creating a cluster on Hetzner Cloud from a single natural language request:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Start the conversation&lt;/strong&gt; — ask to create a cluster on Hetzner&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Starting the chat with a Hetzner cluster request&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1598&quot; height=&quot;1574&quot; src=&quot;https://devantler.tech/_astro/ksail-copilot-chat-hetzner-start.C7kLY5vc_132Hjp.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Task complete&lt;/strong&gt; — the assistant finishes provisioning the cluster&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Cluster creation finished in chat&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1602&quot; height=&quot;2044&quot; src=&quot;https://devantler.tech/_astro/ksail-copilot-chat-hetzner-finished.B2Zo_hNl_k90RJ.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Verification&lt;/strong&gt; — the cluster appears in Hetzner Cloud&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Verification in Hetzner Cloud dashboard&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2784&quot; height=&quot;308&quot; src=&quot;https://devantler.tech/_astro/ksail-copilot-chat-hetzner-verificiation.C1CBICrj_bedxp.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;The entire workflow happens in the terminal — no context switching, no remembering flags.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail already makes Kubernetes development easier by bundling common tools into a single Go binary. But users still need to remember commands, flags, and workflows. The chat feature completes the progression:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Before KSail&lt;/th&gt;&lt;th&gt;With KSail CLI&lt;/th&gt;&lt;th&gt;With KSail Chat&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;5+ tools to install&lt;/td&gt;&lt;td&gt;1 binary&lt;/td&gt;&lt;td&gt;1 binary&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Memorize each tool’s flags&lt;/td&gt;&lt;td&gt;Read &lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Just describe what you want&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Copy commands from docs&lt;/td&gt;&lt;td&gt;Run unified commands&lt;/td&gt;&lt;td&gt;”Create a cluster”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;KSail already knows &lt;em&gt;how&lt;/em&gt; to do everything — now it understands &lt;em&gt;what&lt;/em&gt; you want to do. The assistant needs to understand KSail’s capabilities, execute real commands (not just explain them), work entirely in the terminal, and respect security boundaries. Building that required three key pieces working together.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-tech-stack&quot;&gt;The Tech Stack&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Each requirement above pointed to a specific technical choice: an AI backend for understanding and action, a TUI framework for terminal-native interaction, and a markdown renderer for readable output.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;github-copilot-sdk-for-go&quot;&gt;GitHub Copilot SDK for Go&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/github/copilot-sdk-go&quot;&gt;Copilot SDK for Go&lt;/a&gt; provides the AI backbone. At time of writing, I used v0.1.18. It handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Streaming responses&lt;/strong&gt; — tokens arrive as they’re generated&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool calling&lt;/strong&gt; — the model can invoke functions you define&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conversation history&lt;/strong&gt; — multi-turn dialogues with context&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The SDK abstracts away the complexity of the Copilot API, letting me focus on building the experience.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;bubbletea-tui-framework&quot;&gt;Bubbletea TUI Framework&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/charmbracelet/bubbletea&quot;&gt;Bubbletea&lt;/a&gt; is an Elm-inspired framework for building terminal apps in Go. It uses a functional model where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; holds the application state&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update&lt;/strong&gt; handles messages and returns a new model&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View&lt;/strong&gt; renders the model to a string&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This architecture makes state management predictable — every state transition is explicit. See the &lt;a href=&quot;https://pkg.go.dev/github.com/devantler-tech/ksail/v5/pkg/cli/ui/chat#Model&quot;&gt;chat TUI model&lt;/a&gt; for the full implementation.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;glamour-for-markdown-rendering&quot;&gt;Glamour for Markdown Rendering&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;AI responses often include markdown formatting. &lt;a href=&quot;https://github.com/charmbracelet/glamour&quot;&gt;glamour&lt;/a&gt; renders it beautifully in the terminal — code blocks get syntax highlighting, headers become bold, and lists render cleanly.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;architecture-overview&quot;&gt;Architecture Overview&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The SDK, TUI, and renderer handle &lt;em&gt;how&lt;/em&gt; the interface works. The harder question: how do we make the AI actually &lt;em&gt;useful&lt;/em&gt;? An LLM that just talks is nice, but we need it to execute real commands and stay within security boundaries.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;auto-generated-tools-from-cobra-commands&quot;&gt;Auto-Generated Tools from Cobra Commands&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;ksail-copilot-chat-2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2008&quot; height=&quot;2258&quot; src=&quot;https://devantler.tech/_astro/ksail-copilot-chat-2.BDBzC6oX_VT9VU.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;KSail has 92+ CLI commands, all built with &lt;a href=&quot;https://github.com/spf13/cobra&quot;&gt;Cobra&lt;/a&gt;. This consistency is what makes auto-generation possible — every command has the same structure: name, description, flags with types and defaults.&lt;/p&gt;
&lt;p&gt;I built a &lt;a href=&quot;https://pkg.go.dev/github.com/devantler-tech/ksail/v5/pkg/svc/chat/generator&quot;&gt;generator&lt;/a&gt; that walks KSail’s command tree, converts each command’s flags to JSON Schema, and creates tool handlers that invoke the actual KSail logic. The AI doesn’t shell out to &lt;code dir=&quot;auto&quot;&gt;ksail&lt;/code&gt; — it calls the same Go functions the CLI uses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The lesson learned&lt;/strong&gt;: auto-discovering all 92 commands broke things. LLMs have a practical limit on how many tools they can reason about effectively — this is a known issue called &lt;em&gt;tool overload&lt;/em&gt;. When presented with too many options, the model’s ability to select the right tool degrades significantly. Some commands (like internal debug utilities) also shouldn’t be exposed at all. The solution was to keep auto-discovery but add an exclusion filter — now only ~25 curated commands become tools. Best of both worlds: no manual sync, but a focused toolset the model can actually use well.&lt;/p&gt;
&lt;p&gt;Tools let the assistant &lt;em&gt;act&lt;/em&gt;. But to use them well, it needs context about KSail itself.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;embedded-documentation-context&quot;&gt;Embedded Documentation Context&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The assistant needs to understand KSail’s concepts. I use &lt;code dir=&quot;auto&quot;&gt;go:embed&lt;/code&gt; to bundle 100+ documentation files at compile time, giving the model deep knowledge of KSail’s architecture, configuration options, and best practices — without requiring network requests. See &lt;a href=&quot;https://pkg.go.dev/github.com/devantler-tech/ksail/v5/pkg/svc/chat#BuildSystemContext&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;BuildSystemContext&lt;/code&gt;&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;This embedded context includes KSail’s distribution and provider abstractions. The same chat conversation can create a local Kind cluster for development or a Talos cluster on Hetzner for production — the AI uses the same tools, just with different flags. Users don’t need to know which underlying tool handles which provider.&lt;/p&gt;
&lt;p&gt;With tools and context in place, the assistant can execute commands. But power needs guardrails.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;security-boundaries-for-file-operations&quot;&gt;Security Boundaries for File Operations&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The assistant can read and write files, but only within the current working directory. The &lt;code dir=&quot;auto&quot;&gt;securePath&lt;/code&gt; function in the &lt;a href=&quot;https://pkg.go.dev/github.com/devantler-tech/ksail/v5/pkg/svc/chat&quot;&gt;tools package&lt;/a&gt; validates all paths by resolving symlinks and verifying the target stays within the workspace — preventing symlink escape attacks where a malicious symlink could point outside the workspace.&lt;/p&gt;
&lt;p&gt;With the core architecture solid — tools, context, and security — the final layer is polish.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;user-experience-polish&quot;&gt;User Experience Polish&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Beyond the core AI integration, the TUI includes thoughtful UX details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prompt history&lt;/strong&gt; — press ↑/↓ to recall previous messages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-time tool streaming&lt;/strong&gt; — see command output as it runs, not just when complete&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collapsible tool output&lt;/strong&gt; — Tab toggles individual tools, Ctrl+T toggles all&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mouse scrolling&lt;/strong&gt; — wheel scrolls the conversation viewport&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptive rendering&lt;/strong&gt; — markdown re-renders when terminal width changes&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;building-with-ai-assistance&quot;&gt;Building with AI Assistance&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here’s the meta part: an AI assistant built &lt;em&gt;using&lt;/em&gt; AI assistance. The same pattern — removing cognitive overhead — applied to the development process itself.&lt;/p&gt;
&lt;p&gt;VS Code’s agent mode with Claude Opus 4.5 was invaluable throughout this project. But TUI development presents a unique challenge — the AI can’t “see” what renders in your terminal. How do you debug visual issues with a text-based assistant?&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-image-snapshot-workflow&quot;&gt;The Image Snapshot Workflow&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;My solution: image snapshots. The workflow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Run the TUI&lt;/strong&gt; with test inputs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Screenshot&lt;/strong&gt; the terminal output&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Share the image&lt;/strong&gt; with Claude via VS Code’s attachment feature&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Describe what’s wrong&lt;/strong&gt; — “The viewport is overlapping the input field”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply the suggested fix&lt;/strong&gt; and iterate&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This visual feedback loop was essential for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debugging layout issues&lt;/li&gt;
&lt;li&gt;Fine-tuning the viewport scroll behavior&lt;/li&gt;
&lt;li&gt;Aligning the collapsible tool output sections&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;iterating-on-tui-design&quot;&gt;Iterating on TUI Design&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The TUI went through several iterations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version 1&lt;/strong&gt;: Simple streaming text, no viewport — text would scroll off screen&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version 2&lt;/strong&gt;: Added viewport with scroll handling — but input field position was wrong&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version 3&lt;/strong&gt;: Proper layout with input at bottom, content scrolling above — working!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version 4&lt;/strong&gt;: Added collapsible tool invocations with Tab toggle — polished experience&lt;/p&gt;
&lt;p&gt;Each iteration was a conversation with Claude, showing screenshots and describing what felt wrong.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;technical-decisions-and-trade-offs&quot;&gt;Technical Decisions and Trade-offs&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every project has its thorny edge cases. Here are the three that taught me the most.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;per-turn-subscription-pattern&quot;&gt;Per-Turn Subscription Pattern&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Bubbletea uses subscriptions for async events (like streaming tokens). A naive approach subscribes once at initialization, but this causes issues when starting new turns — old subscriptions conflict with new ones. The solution was to subscribe fresh for each AI turn, creating a new channel and subscription tied to that specific conversation turn. See the &lt;a href=&quot;https://pkg.go.dev/github.com/devantler-tech/ksail/v5/pkg/cli/ui/chat#Model.Update&quot;&gt;Model.Update&lt;/a&gt; method for the implementation.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;context-cancellation-challenges&quot;&gt;Context Cancellation Challenges&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The Copilot SDK doesn’t support mid-stream cancellation. If you cancel the context, the SDK panics rather than gracefully stopping. My workaround: use a context that’s never cancelled, and track “stop requested” via a separate flag. This isn’t ideal, but it’s documented in the code for future improvement when the SDK adds support.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;tool-result-ordering&quot;&gt;Tool Result Ordering&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Tools can execute in any order, but results should appear in the order the model requested them. The SDK’s channel-based output didn’t guarantee this, so I implemented a mutex-protected FIFO buffer to ensure consistent ordering in the UI.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The chat assistant is functional but still in its infancy. Each improvement targets a specific friction point — continuing the “remove overhead” theme:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Smarter clarifications&lt;/strong&gt; — remove the overhead of being precise upfront; let vague requests trigger smart follow-up questions, or intelligent inferences&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More tools&lt;/strong&gt; — reduce the gap between what KSail can do and what the assistant can do&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permission prompts&lt;/strong&gt; — guard write operations with user confirmation to increase safety, and allow configurable auto-approvals for power users&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conversation persistence&lt;/strong&gt; — remove the overhead of re-explaining context across sessions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better context&lt;/strong&gt; — understand the user’s specific cluster state, not just general KSail knowledge&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming cancellation&lt;/strong&gt; — more responsive interaction when the SDK supports it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to try it:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Install KSail v5+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Start a chat session (requires GitHub Copilot access)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Or use non-TUI mode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--tui=false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The full implementation is in the &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail repository&lt;/a&gt; — the TUI lives in &lt;code dir=&quot;auto&quot;&gt;pkg/cli/ui/chat/&lt;/code&gt; and the tools generator in &lt;code dir=&quot;auto&quot;&gt;pkg/svc/chat/generator/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Feedback and contributions welcome!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This post was written with AI assistance (Claude Opus 4.5 via VS Code), following my outline and intent.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>ai</category><category>ksail</category><category>go</category><category>copilot</category><category>tui</category></item><item><title>Creating Kubernetes Clusters on Hetzner with KSail and Talos</title><link>https://devantler.tech/blog/creating-development-kubernetes-clusters-on-hetzner-with-ksail-and-talos/</link><guid isPermaLink="true">https://devantler.tech/blog/creating-development-kubernetes-clusters-on-hetzner-with-ksail-and-talos/</guid><description>A step-by-step guide to creating Talos Linux Kubernetes clusters on Hetzner Cloud using KSail.

</description><pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Setting up Kubernetes environments doesn’t have to be expensive or complicated. With &lt;a href=&quot;https://www.hetzner.com/cloud/&quot;&gt;Hetzner Cloud&lt;/a&gt;’s affordable pricing, &lt;a href=&quot;https://www.talos.dev/&quot;&gt;Talos Linux&lt;/a&gt;’s security-focused immutable OS, and &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt;’s unified tooling, you can have a cluster running in minutes. This post walks through the complete setup.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-hetzner--talos--ksail&quot;&gt;Why Hetzner + Talos + KSail?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Hetzner Cloud&lt;/strong&gt; offers some of the most affordable cloud VPS servers in the industry. A capable &lt;code dir=&quot;auto&quot;&gt;cx23&lt;/code&gt; server (2 vCPU, 4GB RAM) costs around €3.74/month — significantly cheaper than equivalent offerings from AWS, GCP, or Azure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Talos Linux&lt;/strong&gt; is a minimal, immutable operating system designed specifically for Kubernetes. There’s no SSH, no shell, no package manager — just the Talos API and Kubernetes. This dramatically reduces the attack surface and eliminates configuration drift.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KSail&lt;/strong&gt; brings it all together with a single binary that handles cluster provisioning, GitOps setup, and workload management. Instead of juggling &lt;code dir=&quot;auto&quot;&gt;hcloud&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;talosctl&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;kubectl&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;helm&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;flux&lt;/code&gt; commands, you use one consistent interface.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1-create-a-hetzner-account&quot;&gt;Step 1: Create a Hetzner Account&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you don’t already have a Hetzner account:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://console.hetzner.cloud/&quot;&gt;Hetzner Cloud Console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Click “Register” and complete the signup process&lt;/li&gt;
&lt;li&gt;Verify your email and complete any required identity verification&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hetzner has documentation for getting started: &lt;a href=&quot;https://docs.hetzner.com/general/billing-and-account-management/account-getting-started/&quot;&gt;Account Getting Started Guide&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2-create-a-hetzner-project&quot;&gt;Step 2: Create a Hetzner Project&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Projects in Hetzner Cloud are organizational units that contain your resources (servers, networks, load balancers, etc.). Each project has its own API tokens and billing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log into the &lt;a href=&quot;https://console.hetzner.cloud/&quot;&gt;Hetzner Cloud Console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Click “New Project” in the sidebar&lt;/li&gt;
&lt;li&gt;Name your project (e.g., “kubernetes-dev” or “my-homelab”)&lt;/li&gt;
&lt;li&gt;Click “Create”&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3-generate-an-api-token&quot;&gt;Step 3: Generate an API Token&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail needs an API token to provision and manage Hetzner Cloud resources.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In your project, go to &lt;strong&gt;Security&lt;/strong&gt; → &lt;strong&gt;API Tokens&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate API Token&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Name the token (e.g., “ksail-cluster-management”)&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Read &amp;#x26; Write&lt;/strong&gt; permissions — KSail needs to create and delete servers&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate API Token&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copy the token immediately&lt;/strong&gt; — you won’t be able to see it again!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more details, see Hetzner’s &lt;a href=&quot;https://docs.hetzner.com/cloud/api/getting-started/generating-api-token/&quot;&gt;Generating API Token Guide&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-4-export-the-api-token&quot;&gt;Step 4: Export the API Token&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail reads the Hetzner API token from the &lt;code dir=&quot;auto&quot;&gt;HCLOUD_TOKEN&lt;/code&gt; environment variable. Add it to your shell configuration:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# For the current session&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HCLOUD_TOKEN&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;your-api-token-here&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# To persist across sessions, add to your shell config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;export HCLOUD_TOKEN=&quot;your-api-token-here&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.zshrc&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# or ~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.zshrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can verify the token is set:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$HCLOUD_TOKEN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Should show first 10 characters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-5-install-ksail&quot;&gt;Step 5: Install KSail&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail is distributed as a single binary. The easiest installation method is via Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Alternatively, if you have Go installed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;go&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com/devantler-tech/ksail/v5@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Verify the installation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-6-scaffold-your-cluster-project&quot;&gt;Step 6: Scaffold Your Cluster Project&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail’s &lt;code dir=&quot;auto&quot;&gt;init&lt;/code&gt; command scaffolds a complete project structure. For a basic Talos cluster on Hetzner with Cilium CNI:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Talos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Hetzner&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cni&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cilium&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This creates &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt; (cluster configuration), &lt;code dir=&quot;auto&quot;&gt;talos/&lt;/code&gt; (Talos configs), and &lt;code dir=&quot;auto&quot;&gt;k8s/&lt;/code&gt; (your Kubernetes manifests).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For GitOps workflows&lt;/strong&gt;, add a GitOps engine and external registry:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Talos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Hetzner&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--cni&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cilium&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--gitops-engine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Flux&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--local-registry&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;${GITHUB_USER}:${GITHUB_TOKEN}@ghcr.io/your-org/your-cluster&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This configures Flux to sync manifests from GitHub Container Registry. See &lt;a href=&quot;#step-9-deploying-workloads&quot;&gt;Step 9: Deploying Workloads&lt;/a&gt; for the full GitOps workflow.&lt;/p&gt;
&lt;p&gt;For all available flags and configuration options, see the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-init&quot;&gt;CLI flags reference&lt;/a&gt; — All &lt;code dir=&quot;auto&quot;&gt;cluster init&lt;/code&gt; options&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/declarative-configuration&quot;&gt;ksail.yaml reference&lt;/a&gt; — Configuration file schema&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/features&quot;&gt;Features overview&lt;/a&gt; — CNI, CSI, GitOps, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;step-7-create-the-cluster&quot;&gt;Step 7: Create the Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;With your configuration ready, create the cluster:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creates servers in Hetzner Cloud&lt;/li&gt;
&lt;li&gt;Configures the private network&lt;/li&gt;
&lt;li&gt;Bootstraps Talos Linux on each node&lt;/li&gt;
&lt;li&gt;Initializes the Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Installs your selected CNI, CSI, and other components&lt;/li&gt;
&lt;li&gt;Configures your local kubeconfig&lt;/li&gt;
&lt;li&gt;Configures your local talosconfig&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The process takes 3-5 minutes depending on your cluster size.&lt;/p&gt;
&lt;p&gt;You can watch the progress as KSail outputs status updates for each stage.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-8-working-with-your-cluster&quot;&gt;Step 8: Working with Your Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once your cluster is running, KSail provides commands for common operations:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Show cluster status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# List all KSail-managed clusters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# Open K9s for interactive management&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stop&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Stop the cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# Start a stopped cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your kubeconfig is automatically configured, so standard &lt;code dir=&quot;auto&quot;&gt;kubectl&lt;/code&gt; commands work too.&lt;/p&gt;
&lt;p&gt;For the full command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-root&quot;&gt;Cluster Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-9-deploying-workloads&quot;&gt;Step 9: Deploying Workloads&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail wraps kubectl and GitOps operations under the &lt;code dir=&quot;auto&quot;&gt;workload&lt;/code&gt; command. For cloud clusters like Hetzner, you have two main options for deploying workloads.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;option-a-direct-kubectl-workflow&quot;&gt;Option A: Direct kubectl Workflow&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The simplest approach applies manifests directly to the cluster:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./k8s&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Apply Kustomize manifests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pods&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;# Check pod status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;logs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deployment/my-app&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# View logs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This works well for quick iterations but doesn’t provide GitOps benefits like drift detection and automatic reconciliation.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;option-b-gitops-with-external-registry&quot;&gt;Option B: GitOps with External Registry&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For cloud clusters without a local Docker registry, you can use an external OCI registry like GitHub Container Registry (ghcr.io). This enables full GitOps workflows with Flux or ArgoCD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Create a GitHub Personal Access Token&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)&lt;/li&gt;
&lt;li&gt;Create a token with &lt;code dir=&quot;auto&quot;&gt;write:packages&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;read:packages&lt;/code&gt; scopes&lt;/li&gt;
&lt;li&gt;Export the credentials:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GITHUB_USER&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;your-username&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ghp_your-token-here&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Initialize with GitOps and External Registry&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When scaffolding your cluster, specify the GitOps engine and external registry:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Talos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Hetzner&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--cni&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cilium&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--gitops-engine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Flux&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--local-registry&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;${GITHUB_USER}:${GITHUB_TOKEN}@ghcr.io/your-org/your-cluster&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;--local-registry&lt;/code&gt; flag accepts the format &lt;code dir=&quot;auto&quot;&gt;[user:pass@]host[:port][/path]&lt;/code&gt;. Environment variable placeholders like &lt;code dir=&quot;auto&quot;&gt;${GITHUB_TOKEN}&lt;/code&gt; are expanded at runtime, keeping credentials out of your config files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Create the Cluster&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This installs Flux and configures it to sync from your external registry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Push and Reconcile Workloads&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Package manifests and push to registry&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Trigger GitOps reconciliation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reconcile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;push&lt;/code&gt; command packages your &lt;code dir=&quot;auto&quot;&gt;k8s/&lt;/code&gt; directory as an OCI artifact and pushes it to ghcr.io. Flux then pulls and applies the manifests automatically.&lt;/p&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;You can also set the registry via environment variable: &lt;code dir=&quot;auto&quot;&gt;KSAIL_REGISTRY=&apos;ghcr.io/org/repo&apos; ksail workload push&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For the full workload command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/workload/workload-root&quot;&gt;Workload Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;cleaning-up&quot;&gt;Cleaning Up&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;When you’re done with the cluster:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This removes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All Hetzner Cloud servers&lt;/li&gt;
&lt;li&gt;The private network&lt;/li&gt;
&lt;li&gt;Placement groups&lt;/li&gt;
&lt;li&gt;Local kubeconfig entries&lt;/li&gt;
&lt;li&gt;Local talosconfig entries&lt;/li&gt;
&lt;/ul&gt;
&lt;aside aria-label=&quot;Caution&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Caution&lt;/p&gt;&lt;div&gt;&lt;p&gt;This is destructive and cannot be undone. All data in the cluster will be lost.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h2 id=&quot;cost-considerations&quot;&gt;Cost Considerations&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A minimal development setup (1 control plane) using a &lt;code dir=&quot;auto&quot;&gt;cx23&lt;/code&gt; server costs under €4/month.&lt;/p&gt;
&lt;p&gt;For a more robust setup (3 control planes + 2 workers) using &lt;code dir=&quot;auto&quot;&gt;cx23&lt;/code&gt; servers:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Component&lt;/th&gt;&lt;th&gt;Monthly Cost&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;3x cx23 control planes&lt;/td&gt;&lt;td&gt;€11.22&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2x cx23 workers&lt;/td&gt;&lt;td&gt;€7.48&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Private network&lt;/td&gt;&lt;td&gt;Free&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;~€18.70/month&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;This is remarkably affordable for a Kubernetes development cluster.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Explore the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt; for advanced topics including secret management with SOPS, mirror registries, and GitOps workflows.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;planned-production-grade-features&quot;&gt;Planned: Production-Grade Features&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Integration with &lt;a href=&quot;https://github.com/hetznercloud/hcloud-cloud-controller-manager&quot;&gt;Hetzner Cloud Controller Manager&lt;/a&gt; and &lt;a href=&quot;https://github.com/hetznercloud/csi-driver&quot;&gt;Hetzner Cloud CSI Driver&lt;/a&gt; is planned for future releases. This will enable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cloud Load Balancers&lt;/strong&gt;: Automatic provisioning of Hetzner Load Balancers for Kubernetes Services of type &lt;code dir=&quot;auto&quot;&gt;LoadBalancer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistent Storage&lt;/strong&gt;: Dynamic provisioning of Hetzner Cloud Volumes for PersistentVolumeClaims&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;feedback-welcome&quot;&gt;Feedback Welcome&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;This is the first iteration of Hetzner Cloud support in KSail. If you encounter bugs or find missing features, please &lt;a href=&quot;https://github.com/devantler-tech/ksail/issues&quot;&gt;open an issue on GitHub&lt;/a&gt;. Your feedback helps improve the tool for everyone.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;a-note-on-cloud-provider-support&quot;&gt;A Note on Cloud Provider Support&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Testing and maintaining cloud provider integrations comes with ongoing infrastructure costs. Hetzner is supported because I use it for my own homelab and want to manage it via KSail. Additional cloud providers (AWS, GCP, Azure, etc.) will not be added without sponsorship to cover testing costs.&lt;/p&gt;
&lt;p&gt;If you’d like to see your preferred cloud provider supported, consider &lt;a href=&quot;https://github.com/sponsors/devantler&quot;&gt;sponsoring the project on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5. The content is based on real-world experience running Talos development clusters on Hetzner Cloud.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>kubernetes</category><category>ksail</category><category>talos</category><category>hetzner</category><category>cloud</category></item><item><title>AI-Powered GitHub Issues: How I Use Copilot with Claude Opus 4.5 to Create Impactful Issues</title><link>https://devantler.tech/blog/ai-powered-github-issues-with-copilot-and-claude-opus/</link><guid isPermaLink="true">https://devantler.tech/blog/ai-powered-github-issues-with-copilot-and-claude-opus/</guid><description>How I leverage GitHub Copilot with Claude Opus 4.5 to analyze codebases, investigate context, and create well-structured GitHub issues that save time for myself and my team.

</description><pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Creating good GitHub issues is an underrated skill. A well-written issue saves hours of back-and-forth during refinement, reduces misunderstandings, and helps developers focus on solving problems rather than deciphering vague descriptions. But writing those issues takes time — time spent investigating code, formulating problems clearly, and ensuring the description meets team expectations.&lt;/p&gt;
&lt;p&gt;I’ve found a workflow that dramatically reduces this overhead: using &lt;a href=&quot;https://docs.github.com/en/copilot&quot;&gt;GitHub Copilot&lt;/a&gt; with &lt;a href=&quot;https://www.anthropic.com/news/claude-opus-4-5&quot;&gt;Claude Opus 4.5&lt;/a&gt; to analyze codebases and generate structured, impactful issues.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem-with-traditional-issue-creation&quot;&gt;The Problem with Traditional Issue Creation&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Before adopting this workflow, creating a proper issue typically involved:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Investigation&lt;/strong&gt;: Digging through code to understand the current state&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analysis&lt;/strong&gt;: Identifying what’s wrong or what’s missing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Formulation&lt;/strong&gt;: Translating technical findings into clear descriptions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structuring&lt;/strong&gt;: Ensuring the issue follows team conventions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review&lt;/strong&gt;: Double-checking that nothing important was missed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a complex feature or bug, this process could easily take multiple hours — and that’s assuming you’re already familiar with the codebase.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;my-ai-assisted-workflow&quot;&gt;My AI-Assisted Workflow&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;My approach flips the script. Instead of manually investigating and writing, I give GitHub Copilot a clear but short description of the expected outcome or problem, and let Claude Opus 4.5 do the heavy lifting.&lt;/p&gt;
&lt;pre&gt;flowchart LR
    A[&quot;📁 Select Context&quot;] --&gt; B[&quot;💬 Prompt with Intent&quot;]
    B --&gt; C[&quot;📋 Reference Templates&quot;]
    C --&gt; D[&quot;🤖 AI Analysis&quot;]
    D --&gt; E[&quot;🎫 Create Issue&quot;]
    E --&gt; F[&quot;✅ Review &amp;#x26; Refine&quot;]&lt;/pre&gt;
&lt;p&gt;Here’s how it works:&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;step-1-select-the-context&quot;&gt;Step 1: Select the Context&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;I attach the relevant folders, repositories, or files in &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode&quot;&gt;VS Code Chat Agent mode&lt;/a&gt;. This might be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A specific service or module where I’ve noticed a problem&lt;/li&gt;
&lt;li&gt;Multiple related files that need coordinated changes&lt;/li&gt;
&lt;li&gt;An entire project when planning a new feature&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;step-2-prompt-with-intent-and-reference-templates&quot;&gt;Step 2: Prompt with Intent and Reference Templates&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;I provide Copilot with a concise description of what I need, always referencing my issue templates. The key is being clear about:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;What to analyze&lt;/strong&gt; — the specific area of the codebase&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What the problem or goal is&lt;/strong&gt; — the expected outcome&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Which template to use&lt;/strong&gt; — this ensures the output follows team conventions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here are some example prompts I use:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For a bug:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Analyze the metrics-server installation in pkg/svc/installers/ and identify&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;why it&apos;s not working with the latest Kubernetes version. Use my bug issue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;template from .github/ISSUE_TEMPLATE/BUG.yaml to create the issue.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;For a feature:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Investigate the current cluster provisioning flow in pkg/svc/provisioners/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;and create a feature issue for adding Hetzner Cloud support. Follow the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;feature template from .github/ISSUE_TEMPLATE/FEATURE.yaml.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;For process improvements:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Our team&apos;s refinement sessions often run over time because issues lack context.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Create an improvement kata issue to improve how we write and prepare issues&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;before refinement, using the KATA.yaml template.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;step-3-review-and-refine&quot;&gt;Step 3: Review and Refine&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Opus 4.5 generates a complete issue that typically requires minimal editing. The model is remarkably good at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understanding codebases and their architecture&lt;/li&gt;
&lt;li&gt;Identifying the actual problem, not just symptoms&lt;/li&gt;
&lt;li&gt;Writing clear, actionable descriptions&lt;/li&gt;
&lt;li&gt;Following the template structure precisely&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;structured-templates-are-key&quot;&gt;Structured Templates Are Key&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The secret sauce isn’t just the AI — it’s combining AI with well-designed issue templates. I use &lt;a href=&quot;https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms&quot;&gt;YAML-based GitHub issue forms&lt;/a&gt; stored in &lt;code dir=&quot;auto&quot;&gt;.github/ISSUE_TEMPLATE/&lt;/code&gt; that enforce structure and guide both humans and AI to provide the right information.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;bug-reports&quot;&gt;Bug Reports&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For bugs, my template captures the essential debugging information:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;🐛 Bug&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;An unexpected problem or behavior.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[bug]: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Bug&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;expected_behavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Expected Behavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Describe what you expected to happen.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actual_behavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Actual Behavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Describe what actually happened.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Steps to Replicate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;List the steps to replicate the bug.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1. Step 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2. Step 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3. Step 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When I prompt Copilot with something like &lt;em&gt;“The cluster fails to start when Cilium is enabled on ARM64 — create a bug issue”&lt;/em&gt;, it produces output that maps directly to these fields with accurate technical details.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;feature-requests&quot;&gt;Feature Requests&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For features, I use the standard agile user story format with acceptance criteria:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;🚀 Feature&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Submit a new feature as a user story with acceptance criteria.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[feature]: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Feature&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;user_story&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;User Story&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Use the standard agile story format.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;**As a** [role],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;**I want** [an action or feature],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;**So that** [a reason or benefit].&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;acceptance_criteria&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Acceptance Criteria&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Provide a task list of conditions that must be satisfied.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- [ ] Criteria 1: Describe the first acceptance criterion here.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- [ ] Criteria 2: Describe the second acceptance criterion here.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- [ ] Criteria 3: Describe additional criteria as needed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The user story format forces clarity about &lt;em&gt;who&lt;/em&gt; benefits, &lt;em&gt;what&lt;/em&gt; they need, and &lt;em&gt;why&lt;/em&gt; it matters. Claude Opus 4.5 excels at filling this in with genuine user-centric thinking rather than developer-centric feature descriptions.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;improvement-katas&quot;&gt;Improvement Katas&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For continuous improvement initiatives, I use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Toyota_Kata&quot;&gt;Toyota Kata framework&lt;/a&gt;. Unlike bugs and features, katas focus on human processes rather than code — things like team communication, workflow bottlenecks, or development practices.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;🥋 Improvement Kata&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Use the Improvement Kata framework for structured problem-solving.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[kata]: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Kata&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;problem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Problem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Describe the current situation and why it is problematic.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Current situation:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Why this is problematic:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;definition-of-awesome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Definition of Awesome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;How would we like it to be? Describe the ideal state.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;next-target-condition&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Next Target Condition&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;X weeks from now, what has changed?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Actions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;List of actions to take toward the target condition.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- [ ] **Action 1:** ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- [ ] **Action 2:** ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- [ ] **Action 3:** ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;validations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This template is particularly powerful with AI assistance. Opus 4.5 can help structure observations about team dynamics, identify process bottlenecks, and propose actionable improvement experiments — turning vague frustrations into measurable improvement initiatives.&lt;/p&gt;
&lt;p&gt;You can find all my issue templates in the &lt;a href=&quot;https://github.com/devantler-tech/.github/tree/main/.github/ISSUE_TEMPLATE&quot;&gt;devantler-tech/.github repository&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-real-example-from-prompt-to-issue&quot;&gt;A Real Example: From Prompt to Issue&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To show this workflow in action, here’s a real issue I created for KSail using this approach.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;what-i-typed&quot;&gt;What I Typed&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Analyze the existing addon patterns in pkg/svc/installer/ (cert-manager,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;metrics-server, CNI, CSI) and create a feature issue for adding a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;--policy-engine flag that supports Kyverno and Gatekeeper. Use the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FEATURE.yaml template from .github/ISSUE_TEMPLATE/.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s it — one sentence describing what I wanted, plus a reference to existing patterns and my template.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;what-the-ai-produced&quot;&gt;What the AI Produced&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Copilot analyzed the codebase, identified all the integration points, and generated &lt;a href=&quot;https://github.com/devantler-tech/ksail/issues/1614&quot;&gt;this issue&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**[&lt;/span&gt;&lt;span&gt;feature&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;: Add &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;--policy-engine&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; flag to install Kyverno or Gatekeeper&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;User Story&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;As a&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; KSail user,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;I want&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; to install a policy engine (Kyverno or Gatekeeper) when creating a cluster,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;So that&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt; I can enforce policies on my workloads from the start without manual post-provisioning steps.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Background&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Policy engines are essential for enforcing security, compliance, and best practices in Kubernetes clusters. Adding first-class support for policy engines follows the same pattern as existing addons (cert-manager, metrics-server, CNI, CSI, GitOps engines).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The AI didn’t stop there. It analyzed the existing addon implementations and produced detailed implementation requirements:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;### Implementation Requirements&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#### 1. API Types (&lt;/span&gt;&lt;span&gt;`pkg/apis/cluster/v1alpha1/`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Add &lt;/span&gt;&lt;span&gt;`PolicyEngine`&lt;/span&gt;&lt;span&gt; enum type with values &lt;/span&gt;&lt;span&gt;`PolicyEngineNone`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`PolicyEngineKyverno`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`PolicyEngineGatekeeper`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Add &lt;/span&gt;&lt;span&gt;`PolicyEngine PolicyEngine`&lt;/span&gt;&lt;span&gt; field to &lt;/span&gt;&lt;span&gt;`ClusterSpec`&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Implement &lt;/span&gt;&lt;span&gt;`Set()`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`String()`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`Type()`&lt;/span&gt;&lt;span&gt; methods for pflags compatibility&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#### 2. Installer Service (&lt;/span&gt;&lt;span&gt;`pkg/svc/installer/`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Create &lt;/span&gt;&lt;span&gt;`pkg/svc/installer/kyverno/installer.go`&lt;/span&gt;&lt;span&gt; implementing &lt;/span&gt;&lt;span&gt;`installer.Installer`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Create &lt;/span&gt;&lt;span&gt;`pkg/svc/installer/gatekeeper/installer.go`&lt;/span&gt;&lt;span&gt; implementing &lt;/span&gt;&lt;span&gt;`installer.Installer`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#### 3. CLI Integration (&lt;/span&gt;&lt;span&gt;`cmd/cluster/`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Add &lt;/span&gt;&lt;span&gt;`--policy-engine`&lt;/span&gt;&lt;span&gt; flag to &lt;/span&gt;&lt;span&gt;`init.go`&lt;/span&gt;&lt;span&gt; and &lt;/span&gt;&lt;span&gt;`create.go`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It even included the correct Helm chart references:&lt;/p&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Engine&lt;/th&gt;&lt;th&gt;Repo URL&lt;/th&gt;&lt;th&gt;Chart&lt;/th&gt;&lt;th&gt;Default Namespace&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Kyverno&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;https://kyverno.github.io/kyverno/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;kyverno/kyverno&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;kyverno&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Gatekeeper&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;https://open-policy-agent.github.io/gatekeeper/charts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gatekeeper/gatekeeper&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gatekeeper-system&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;the-result&quot;&gt;The Result&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The generated issue required minimal editing — just some polish to the acceptance criteria. And here’s the kicker: I implemented the entire feature using another single prompt:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Implement https://github.com/devantler-tech/ksail/issues/1614&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The issue was so well-structured that Copilot could follow it as a specification. Total time from idea to merged PR: about 2 hours of active work, and about 4 hours in total (with the time AI was working). Imagine how much work you could do if you parallelized this across multiple issues!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;real-world-impact&quot;&gt;Real-World Impact&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I use this approach in two contexts:&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;personal-projects-ksail&quot;&gt;Personal Projects (KSail)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt;, my tool for creating, maintaining and operating Kubernetes clusters with ease, this workflow has been invaluable. When I notice something that needs improvement, I can quickly generate a detailed issue without interrupting my flow. The AI understands the Go codebase, the embedded tool clients, and the overall architecture — producing issues that accurately describe both problems and solutions.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;professional-work-tv-2&quot;&gt;Professional Work (TV 2)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;At &lt;a href=&quot;https://tv2.dk&quot;&gt;TV 2&lt;/a&gt;, where I work, this approach has had an even bigger impact.&lt;/p&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Traditional&lt;/th&gt;&lt;th&gt;AI-Assisted&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Investigation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;1-2 hours&lt;/td&gt;&lt;td&gt;1 min (context selection)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Analysis&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;1-2 hour&lt;/td&gt;&lt;td&gt;1 min (prompting)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Writing&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0.5-1 hour&lt;/td&gt;&lt;td&gt;3 min (AI generation)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Structuring&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;10 min&lt;/td&gt;&lt;td&gt;— (template handles it)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Review&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;30 min&lt;/td&gt;&lt;td&gt;25 min&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;3-6 hours&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;~30 minutes&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Time saved on investigation&lt;/strong&gt;: Instead of spending hours digging through unfamiliar code, I can generate a comprehensive issue in under 30 minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Better refinement sessions&lt;/strong&gt;: When issues arrive at refinement already following our template and containing accurate technical details, the team can focus on alignment, estimation and planning rather than clarification.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Senior-level quality&lt;/strong&gt;: Opus 4.5 is remarkably capable at writing issues that meet senior software engineers’ expectations. The descriptions are technical enough to be useful, clear enough to be actionable, and structured enough to fit our workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Every issue follows the same format, making it easier for team members to find the information they need.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;when-this-approach-doesnt-work&quot;&gt;When This Approach Doesn’t Work&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This workflow isn’t magic. There are situations where AI-assisted issue creation falls short:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Issues requiring stakeholder input&lt;/strong&gt;: If the issue depends on business decisions, user research, or information that isn’t in the codebase, the AI can’t help much. It can structure the issue, but it can’t interview your product owner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Highly sensitive or confidential context&lt;/strong&gt;: Be careful about what context you share with AI tools. If the issue involves security vulnerabilities, credentials, or proprietary business logic, you may need to write it manually or heavily redact the AI’s output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-repository or external dependencies&lt;/strong&gt;: When issues span multiple repositories or depend on external systems the AI can’t see, the generated context may be incomplete. Always verify external references.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vague problems without symptoms&lt;/strong&gt;: If you can’t articulate even a rough description of what’s wrong, the AI can’t investigate effectively. “Something feels slow” is too vague — “API response times have increased” gives the AI something to search for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Novel codebases&lt;/strong&gt;: The AI is remarkably good at pattern recognition. In well-structured codebases with consistent patterns (like KSail’s addon system), it excels. In chaotic legacy codebases with no clear patterns, results vary.&lt;/p&gt;
&lt;p&gt;For these situations, I fall back to traditional issue creation, or a mix of both — but I always use issue templates to ensure structure.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;tips-for-getting-the-best-results&quot;&gt;Tips for Getting the Best Results&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;After a month of using this workflow, here’s what I’ve learned:&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-be-specific-about-scope&quot;&gt;1. Be Specific About Scope&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Tell Copilot exactly which files or folders to analyze. The more focused the context, the more accurate the output.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-describe-the-outcome-not-the-solution&quot;&gt;2. Describe the Outcome, Not the Solution&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Say “users can’t access the metrics endpoint” rather than “fix the metrics-server RBAC”. Let the AI investigate and propose solutions.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;3-always-reference-your-templates&quot;&gt;3. Always Reference Your Templates&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Explicitly mention your issue templates. This ensures the output follows your team’s conventions.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;4-review-with-fresh-eyes&quot;&gt;4. Review with Fresh Eyes&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;While the generated issues are usually excellent, always read them as if you were a team member seeing them for the first time. Add context that only you know.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;5-iterate-on-complex-issues&quot;&gt;5. Iterate on Complex Issues&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For large features, start with a high-level issue, then generate sub-issues for specific components.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;GitHub Copilot with Claude Opus 4.5 has transformed how I create issues. What used to take 3-6 hours now takes around 30 minutes, and the quality is often higher than what I’d produce manually.&lt;/p&gt;
&lt;pre&gt;mindmap
  root((AI-Powered Issues))
    Fast
      30 min vs 3-6 hours
      No manual investigation
    Consistent
      Same template every time
      Team conventions followed
    Accurate
      Deep codebase analysis
      Real problems identified
    Team-Friendly
      Ready for refinement
      Clear acceptance criteria&lt;/pre&gt;
&lt;p&gt;The combination of AI analysis and structured templates creates a workflow that’s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;: Minimal time spent on investigation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consistent&lt;/strong&gt;: Every issue follows the same format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accurate&lt;/strong&gt;: The AI understands codebases deeply&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team-friendly&lt;/strong&gt;: Issues arrive at refinement ready to discuss&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re spending too much time writing issues, or if your team’s refinement sessions are slowed down by unclear issue descriptions, give this workflow a try. The upfront investment in good issue templates pays dividends when combined with AI-powered generation, but also if you write issues manually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Want to try this yourself?&lt;/strong&gt; Use my &lt;a href=&quot;https://github.com/devantler-tech/.github/tree/main/.github/ISSUE_TEMPLATE&quot;&gt;issue templates&lt;/a&gt; and adapt them to your team’s conventions. The combination of structured templates and AI-powered generation might just change how you work.&lt;/p&gt;
&lt;p&gt;The future of software development isn’t AI replacing developers — it’s AI handling the tedious parts so developers can focus on what matters. I am personally ecstatic about how this workflow has improved my productivity and the quality of my work. I hope it helps you too!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5 — the motor of the workflow it describes. The content reflects my genuine experiences and opinions; the AI helped structure and articulate them.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>ai</category><category>github</category><category>copilot</category><category>productivity</category><category>developer-experience</category></item><item><title>Building KSail: From Shell Scripts to .NET to Go</title><link>https://devantler.tech/blog/building-ksail-from-shell-to-dotnet-to-go/</link><guid isPermaLink="true">https://devantler.tech/blog/building-ksail-from-shell-to-dotnet-to-go/</guid><description>The journey of building KSail through three major rewrites and what I learned along the way.

</description><pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Building a developer tool is rarely a straight path. &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt; started as a humble shell script in late 2023 and has evolved through three major rewrites to become what it is today: a Go-based CLI that bundles common Kubernetes tooling into a single binary. This post tells the story of that journey.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Setting up and operating Kubernetes clusters is a skill of its own. It often requires juggling multiple CLI tools (kubectl, helm, kind, k3d, flux, argocd, sops), writing bespoke scripts, and dealing with inconsistent developer workflows determined by specific projects.&lt;/p&gt;
&lt;p&gt;This complexity slows down development, makes Kubernetes intimidating for newcomers, and makes it difficult to maintain reproducible environments. I wanted a tool that would remove the tooling overhead so developers could focus on their workloads.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-1-shell-scripts-december-2023&quot;&gt;Phase 1: Shell Scripts (December 2023)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The first commit to KSail was on December 31, 2023. It started as a Bash script called &lt;code dir=&quot;auto&quot;&gt;ksail.sh&lt;/code&gt; that wrapped Talos Linux commands to create local Kubernetes clusters.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Early KSail was literally just a shell script&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./ksail.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# Create a cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./ksail.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;down&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# Destroy it&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This worked, but shell scripts have significant limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No type safety&lt;/strong&gt;: Bugs only surface at runtime&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limited error handling&lt;/strong&gt;: Bash’s error handling is notoriously awkward&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform issues&lt;/strong&gt;: Scripts that work on macOS might fail on Linux&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency management&lt;/strong&gt;: Users needed every tool installed separately&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Within the first week, I added K3d support alongside Talos, and the script was becoming unwieldy. It was clear that a more structured approach was needed.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-2-the-net-rewrite-january-2024&quot;&gt;Phase 2: The .NET Rewrite (January 2024)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;On January 8, 2024 — just over a week after the initial commit — I rewrote KSail as a .NET console application with PR #25: “Rework KSail to .NET Project with wrapped binaries and libraries.”&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;why-net&quot;&gt;Why .NET?&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;At the time, .NET was my primary language. I was comfortable with C#, and .NET 8 had just been released with great cross-platform support. The ecosystem had mature libraries for CLI development (&lt;code dir=&quot;auto&quot;&gt;System.CommandLine&lt;/code&gt;), and I could leverage NuGet for dependency management.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-architecture&quot;&gt;The Architecture&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The .NET version took an interesting approach: it embedded the required binaries (kubectl, kind, k3d, talosctl, etc.) directly into the application and extracted them at runtime. This meant users only needed to install KSail itself — the dependencies came bundled.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Pseudo-code of the .NET approach&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KindClient&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; _binaryPath;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KindClient&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;_binaryPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ExtractEmbeddedBinary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; Task &lt;/span&gt;&lt;span&gt;CreateClusterAsync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; name, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; config)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProcessRunner&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;RunAsync&lt;/span&gt;&lt;span&gt;(_binaryPath, &lt;/span&gt;&lt;span&gt;$&quot;&lt;/span&gt;&lt;span&gt;create cluster --name &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; --config &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Over 2024, the .NET version grew substantially. I presented it at KCD Denmark 2024 in a talk titled &lt;a href=&quot;https://youtu.be/Q-Hfn_-B7p8&quot;&gt;“KSail - a Kubernetes SDK for local GitOps development and CI”&lt;/a&gt;. By the end of 2024, the project had accumulated 788 commits and supported:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple distributions (Kind, K3d, Talos)&lt;/li&gt;
&lt;li&gt;GitOps engines (Flux, ArgoCD)&lt;/li&gt;
&lt;li&gt;Secret management with SOPS&lt;/li&gt;
&lt;li&gt;Declarative configuration via &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mirror registries for avoiding Docker Hub rate limits&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;growing-pains&quot;&gt;Growing Pains&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Despite its functionality, the .NET version had issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Binary size&lt;/strong&gt;: Bundling all those executables made the final binary quite large&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update complexity&lt;/strong&gt;: Every upstream tool update required downloading new binaries, embedding them, and releasing a new version&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform coverage&lt;/strong&gt;: Supporting Linux amd64, Linux arm64, macOS arm64, and Windows meant managing multiple binary variants&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Startup time&lt;/strong&gt;: Extracting binaries on first run added latency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ecosystem mismatch&lt;/strong&gt;: Most Kubernetes tooling is written in Go, and calling out to external processes felt like fighting the ecosystem&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-3-the-go-rewrite-december-2025&quot;&gt;Phase 3: The Go Rewrite (December 2025)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;On December 18, 2025, I merged PR #1448: “BREAKING CHANGE: Migrate ksail from .NET to Go.” This wasn’t a port — it was a complete rewrite that took advantage of Go’s unique position in the Kubernetes ecosystem.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;why-go&quot;&gt;Why Go?&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The Kubernetes ecosystem is built on Go. kubectl, kind, k3d, helm, flux — they’re all Go projects. This means:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Import instead of wrap&lt;/strong&gt;: Instead of shelling out to external binaries, KSail can import these tools as Go libraries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single binary&lt;/strong&gt;: No embedded executables to extract; everything compiles into one binary&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast startup&lt;/strong&gt;: No extraction step, no JIT compilation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First-class Kubernetes support&lt;/strong&gt;: client-go is the reference implementation for Kubernetes clients&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-compilation&lt;/strong&gt;: Go’s cross-compilation is trivial compared to .NET’s AOT publishing&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;the-new-architecture&quot;&gt;The New Architecture&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The Go version embeds tool functionality directly:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Instead of shelling out, we use the libraries directly&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sigs.k8s.io/kind/pkg/cluster&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;helm.sh/helm/v4/pkg/action&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;github.com/fluxcd/flux2/v2/pkg/bootstrap&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;p &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;KindProvisioner) &lt;/span&gt;&lt;span&gt;Create&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ctx&lt;/span&gt;&lt;span&gt; context.Context, &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;KindConfig) &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NewProvider&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CreateWithConfigFile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Path&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CreateWithWaitForReady&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Timeout&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The project structure is clean and idiomatic:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pkg/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── apis/          # Configuration types&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── cli/           # Command implementations&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── client/        # Embedded tool clients (kubectl, helm, flux, etc.)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── di/            # Dependency injection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── io/            # File and config utilities&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── k8s/           # Kubernetes helpers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── svc/           # Services (installers, provisioners, reconcilers)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;└── utils/         # Shared utilities&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;After three rewrites and over 2,200 commits, here’s what I’ve learned:&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-choose-the-right-language-for-the-ecosystem&quot;&gt;1. Choose the Right Language for the Ecosystem&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The Go rewrite wasn’t about Go being “better” than .NET — it was about Go being the right tool for Kubernetes development. When your tool integrates deeply with an ecosystem, speaking its native language matters.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-wrapping-vs-embedding&quot;&gt;2. Wrapping vs. Embedding&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The .NET version wrapped external binaries. The Go version embeds libraries. The difference is profound:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wrapping&lt;/strong&gt;: You’re at the mercy of the tool’s CLI interface, which can change between versions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embedding&lt;/strong&gt;: You have direct access to the tool’s internals, with type safety and proper error handling&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;3-start-simple-then-structure&quot;&gt;3. Start Simple, Then Structure&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The shell script taught me what the tool needed to do. The .NET version taught me how to structure it properly. The Go version combined those lessons with the right technology choice.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;4-dont-fear-the-rewrite&quot;&gt;4. Don’t Fear the Rewrite&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Each rewrite made KSail better. The shell script validated the idea. The .NET version proved the concept and found users. The Go version delivered on the original vision of a single, fast, portable binary.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;where-ksail-is-today&quot;&gt;Where KSail Is Today&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The current Go version of KSail provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One binary&lt;/strong&gt; — No external dependencies except Docker&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple distributions&lt;/strong&gt; — Kind, K3d, and Talos&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customizable stack&lt;/strong&gt; — CNI (Cilium, Calico), CSI, metrics-server, cert-manager&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitOps native&lt;/strong&gt; — Built-in Flux and ArgoCD support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secret management&lt;/strong&gt; — Integrated SOPS encryption&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declarative configuration&lt;/strong&gt; — Everything as code in &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation is simple:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# or&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;go&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com/devantler-tech/ksail/v5@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And usage is straightforward:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Kind&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cni&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cilium&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--gitops-engine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Flux&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./k8s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Opens K9s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail is actively developed with a focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cloud providers&lt;/strong&gt;: Hetzner Cloud support is in progress&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced GitOps workflows&lt;/strong&gt;: Better reconciliation and debugging tools&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced SOPS integration&lt;/strong&gt;: More secret management features for e.g. GitOps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The project is open source under the Apache 2.0 license. You can find:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;github.com/devantler-tech/ksail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;ksail.devantler.tech&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go Package Docs&lt;/strong&gt;: &lt;a href=&quot;https://pkg.go.dev/github.com/devantler-tech/ksail/v5&quot;&gt;pkg.go.dev/github.com/devantler-tech/ksail/v5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re working with Kubernetes locally, give KSail a try. And if you’ve been through similar rewrites with your own projects, I’d love to hear your stories.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5. The content reflects my genuine experiences and journey; the AI helped structure and articulate it.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>ksail</category><category>go</category><category>dotnet</category><category>architecture</category><category>open-source</category></item><item><title>Local Kubernetes Development with KSail and Talos</title><link>https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-talos/</link><guid isPermaLink="true">https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-talos/</guid><description>A guide to creating local Kubernetes development clusters using KSail with Talos Linux in Docker.

</description><pubDate>Sat, 27 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.talos.dev/&quot;&gt;Talos Linux&lt;/a&gt; is a minimal, immutable operating system designed specifically for Kubernetes. While it’s often used in production environments, you can also run Talos locally in Docker for development. Combined with &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt;, you get a security-focused local development experience. This post shows you how.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-talos--ksail&quot;&gt;Why Talos + KSail?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Talos Linux&lt;/strong&gt; takes a radically different approach to running Kubernetes. There’s no SSH, no shell, no package manager — just the Talos API and Kubernetes. The entire OS is immutable and managed declaratively via configuration files.&lt;/p&gt;
&lt;p&gt;This approach provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minimal attack surface&lt;/strong&gt; — No shell means no shell exploits&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No configuration drift&lt;/strong&gt; — Immutable OS prevents unauthorized changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declarative everything&lt;/strong&gt; — OS configuration is versioned and reproducible&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production parity&lt;/strong&gt; — Your local environment matches production Talos clusters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;KSail&lt;/strong&gt; wraps Talos tooling into a single binary, handling cluster provisioning, GitOps setup, and workload management through one consistent interface.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;You need Docker installed and running. Verify with:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If this command works, you’re ready to go.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1-install-ksail&quot;&gt;Step 1: Install KSail&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail is distributed as a single binary. Install via Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or with Go:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;go&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com/devantler-tech/ksail/v5@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Verify the installation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2-scaffold-your-cluster-project&quot;&gt;Step 2: Scaffold Your Cluster Project&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail’s &lt;code dir=&quot;auto&quot;&gt;init&lt;/code&gt; command scaffolds a complete project structure. For a Talos cluster with Cilium CNI:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Talos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cni&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cilium&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This creates &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt; (cluster configuration), &lt;code dir=&quot;auto&quot;&gt;talos/&lt;/code&gt; (Talos configs), and &lt;code dir=&quot;auto&quot;&gt;k8s/&lt;/code&gt; (your Kubernetes manifests).&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;Talos doesn’t include a default CNI, so you should specify one. Cilium is recommended for its eBPF-based networking and observability features.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For all available flags and configuration options, see the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-init&quot;&gt;CLI flags reference&lt;/a&gt; — All &lt;code dir=&quot;auto&quot;&gt;cluster init&lt;/code&gt; options&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/declarative-configuration&quot;&gt;ksail.yaml reference&lt;/a&gt; — Configuration file schema&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/features&quot;&gt;Features overview&lt;/a&gt; — CNI, CSI, GitOps, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3-create-the-cluster&quot;&gt;Step 3: Create the Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Create and start your cluster:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creates Docker containers running Talos Linux&lt;/li&gt;
&lt;li&gt;Bootstraps the Talos control plane&lt;/li&gt;
&lt;li&gt;Initializes etcd and the Kubernetes API server&lt;/li&gt;
&lt;li&gt;Installs your selected CNI, CSI, and other components&lt;/li&gt;
&lt;li&gt;Configures your local kubeconfig and talosconfig&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The process takes 1-2 minutes. Talos clusters take slightly longer than Kind or K3d because of the additional bootstrap steps for the immutable OS.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-4-working-with-your-cluster&quot;&gt;Step 4: Working with Your Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once your cluster is running, KSail provides commands for common operations:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Show cluster status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# List all KSail-managed clusters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# Open K9s for interactive management&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stop&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Stop the cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# Start a stopped cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your kubeconfig is automatically configured, so standard &lt;code dir=&quot;auto&quot;&gt;kubectl&lt;/code&gt; commands work too.&lt;/p&gt;
&lt;p&gt;For the full command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-root&quot;&gt;Cluster Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-5-deploying-workloads&quot;&gt;Step 5: Deploying Workloads&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail wraps kubectl and GitOps operations under the &lt;code dir=&quot;auto&quot;&gt;workload&lt;/code&gt; command:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./k8s&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Apply manifests (kubectl workflow)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;# Push to GitOps source&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reconcile&lt;/span&gt;&lt;span&gt;         &lt;/span&gt;&lt;span&gt;# Trigger GitOps reconciliation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For the full workload command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/workload/workload-root&quot;&gt;Workload Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;cleaning-up&quot;&gt;Cleaning Up&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;When you’re done:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This removes the Docker containers and cleans up kubeconfig and talosconfig entries.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Explore the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt; for advanced topics including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multi-node Talos clusters for testing HA scenarios&lt;/li&gt;
&lt;li&gt;Enabling GitOps with Flux or ArgoCD&lt;/li&gt;
&lt;li&gt;Secret management with SOPS&lt;/li&gt;
&lt;li&gt;Mirror registries to avoid Docker Hub rate limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you’re comfortable with Talos locally, you can deploy to cloud infrastructure. See &lt;a href=&quot;https://devantler.tech/blog/creating-development-kubernetes-clusters-on-hetzner-with-ksail-and-talos/&quot;&gt;Creating Development Clusters on Hetzner with KSail and Talos&lt;/a&gt; for a guide on running Talos in the cloud.&lt;/p&gt;
&lt;p&gt;For simpler local setups, check out &lt;a href=&quot;https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-kind/&quot;&gt;Kind with KSail&lt;/a&gt; for vanilla Kubernetes or &lt;a href=&quot;https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-k3d/&quot;&gt;K3s with KSail&lt;/a&gt; for a batteries-included experience.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;feedback-welcome&quot;&gt;Feedback Welcome&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;KSail is under active development. If you encounter bugs or find missing features, please &lt;a href=&quot;https://github.com/devantler-tech/ksail/issues&quot;&gt;open an issue on GitHub&lt;/a&gt;. Your feedback helps improve the tool for everyone.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>kubernetes</category><category>ksail</category><category>talos</category><category>local-development</category></item><item><title>Local Kubernetes Development with KSail and K3d</title><link>https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-k3d/</link><guid isPermaLink="true">https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-k3d/</guid><description>A guide to creating local Kubernetes development clusters using KSail with K3d (K3s in Docker).

</description><pubDate>Sat, 20 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;K3s is Rancher’s lightweight Kubernetes distribution, and when combined with &lt;a href=&quot;https://k3d.io/&quot;&gt;K3d&lt;/a&gt; and &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt;, you get a fast, batteries-included local development experience. This post shows you how to get started.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-k3d--ksail&quot;&gt;Why K3d + KSail?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;K3s&lt;/strong&gt; is a lightweight, certified Kubernetes distribution designed for resource-constrained environments. It comes with sensible defaults and includes components like Traefik ingress, local-path storage provisioner, and metrics-server out of the box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;K3d&lt;/strong&gt; wraps K3s to run it in Docker containers, making it perfect for local development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KSail&lt;/strong&gt; ties it all together with a single binary that handles cluster provisioning, GitOps setup, and workload management. You get one consistent interface regardless of the underlying distribution.&lt;/p&gt;
&lt;p&gt;Together, they provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast startup&lt;/strong&gt; — Clusters ready in about 30 seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batteries included&lt;/strong&gt; — Storage, ingress, and metrics built in&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low resource usage&lt;/strong&gt; — K3s is optimized for minimal overhead&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declarative configuration&lt;/strong&gt; — Everything as code in &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;You need Docker installed and running. Verify with:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If this command works, you’re ready to go.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1-install-ksail&quot;&gt;Step 1: Install KSail&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail is distributed as a single binary. Install via Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or with Go:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;go&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com/devantler-tech/ksail/v5@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Verify the installation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2-scaffold-your-cluster-project&quot;&gt;Step 2: Scaffold Your Cluster Project&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail’s &lt;code dir=&quot;auto&quot;&gt;init&lt;/code&gt; command scaffolds a complete project structure:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;K3s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This creates &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt; (cluster configuration), &lt;code dir=&quot;auto&quot;&gt;k3d.yaml&lt;/code&gt; (K3d config), and &lt;code dir=&quot;auto&quot;&gt;k8s/&lt;/code&gt; (your Kubernetes manifests).&lt;/p&gt;
&lt;p&gt;For all available flags and configuration options, see the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-init&quot;&gt;CLI flags reference&lt;/a&gt; — All &lt;code dir=&quot;auto&quot;&gt;cluster init&lt;/code&gt; options&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/declarative-configuration&quot;&gt;ksail.yaml reference&lt;/a&gt; — Configuration file schema&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/features&quot;&gt;Features overview&lt;/a&gt; — CNI, CSI, GitOps, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3-create-the-cluster&quot;&gt;Step 3: Create the Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Create and start your cluster:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creates Docker containers as K3s nodes&lt;/li&gt;
&lt;li&gt;Bootstraps the K3s control plane with built-in components&lt;/li&gt;
&lt;li&gt;Installs any additional components you configured (Cilium, Flux, etc.)&lt;/li&gt;
&lt;li&gt;Configures your local kubeconfig&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The process takes about 30 seconds for a single-node cluster.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-4-working-with-your-cluster&quot;&gt;Step 4: Working with Your Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once your cluster is running, KSail provides commands for common operations:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Show cluster status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# List all KSail-managed clusters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# Open K9s for interactive management&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stop&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Stop the cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# Start a stopped cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your kubeconfig is automatically configured, so standard &lt;code dir=&quot;auto&quot;&gt;kubectl&lt;/code&gt; commands work too.&lt;/p&gt;
&lt;p&gt;For the full command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-root&quot;&gt;Cluster Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-5-deploying-workloads&quot;&gt;Step 5: Deploying Workloads&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail wraps kubectl and GitOps operations under the &lt;code dir=&quot;auto&quot;&gt;workload&lt;/code&gt; command:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./k8s&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Apply manifests (kubectl workflow)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;# Push to GitOps source&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reconcile&lt;/span&gt;&lt;span&gt;         &lt;/span&gt;&lt;span&gt;# Trigger GitOps reconciliation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For the full workload command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/workload/workload-root&quot;&gt;Workload Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;cleaning-up&quot;&gt;Cleaning Up&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;When you’re done:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This removes the Docker containers and cleans up kubeconfig entries.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Explore the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt; for advanced topics including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Swapping the default CNI for Cilium or Calico&lt;/li&gt;
&lt;li&gt;Enabling GitOps with Flux or ArgoCD&lt;/li&gt;
&lt;li&gt;Secret management with SOPS&lt;/li&gt;
&lt;li&gt;Mirror registries to avoid Docker Hub rate limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want a vanilla Kubernetes experience, check out the post on &lt;a href=&quot;https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-kind/&quot;&gt;Kind with KSail&lt;/a&gt;. For an immutable, security-focused distribution, see &lt;a href=&quot;https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-talos/&quot;&gt;Talos with KSail&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;feedback-welcome&quot;&gt;Feedback Welcome&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;KSail is under active development. If you encounter bugs or find missing features, please &lt;a href=&quot;https://github.com/devantler-tech/ksail/issues&quot;&gt;open an issue on GitHub&lt;/a&gt;. Your feedback helps improve the tool for everyone.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>kubernetes</category><category>ksail</category><category>k3s</category><category>k3d</category><category>local-development</category></item><item><title>Local Kubernetes Development with KSail and Kind</title><link>https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-kind/</link><guid isPermaLink="true">https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-kind/</guid><description>A guide to creating local Kubernetes development clusters using KSail with Kind (vanilla Kubernetes) in Docker.

</description><pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Getting started with Kubernetes development shouldn’t require cloud infrastructure or complex setup procedures. With &lt;a href=&quot;https://kind.sigs.k8s.io/&quot;&gt;Kind&lt;/a&gt; (Kubernetes in Docker) and &lt;a href=&quot;https://github.com/devantler-tech/ksail&quot;&gt;KSail&lt;/a&gt;, you can have a local cluster running in under a minute. This post shows you how.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-kind--ksail&quot;&gt;Why Kind + KSail?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Kind&lt;/strong&gt; (Kubernetes in Docker) runs Kubernetes clusters using Docker containers as nodes. It’s fast, lightweight, and provides a vanilla Kubernetes experience that closely matches production environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KSail&lt;/strong&gt; wraps Kind and other tools into a single binary, giving you one consistent interface for cluster provisioning, GitOps setup, and workload management. Instead of learning multiple CLIs, you use &lt;code dir=&quot;auto&quot;&gt;ksail cluster&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;ksail workload&lt;/code&gt; commands.&lt;/p&gt;
&lt;p&gt;Together, they provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast iteration&lt;/strong&gt; — Clusters start in under 60 seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero cost&lt;/strong&gt; — Everything runs locally on your machine&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vanilla Kubernetes&lt;/strong&gt; — No distribution-specific quirks to learn&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declarative configuration&lt;/strong&gt; — Everything as code in &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;You need Docker installed and running. Verify with:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If this command works, you’re ready to go.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1-install-ksail&quot;&gt;Step 1: Install KSail&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail is distributed as a single binary. Install via Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or with Go:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;go&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com/devantler-tech/ksail/v5@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Verify the installation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2-scaffold-your-cluster-project&quot;&gt;Step 2: Scaffold Your Cluster Project&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail’s &lt;code dir=&quot;auto&quot;&gt;init&lt;/code&gt; command scaffolds a complete project structure:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--distribution&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vanilla&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This creates &lt;code dir=&quot;auto&quot;&gt;ksail.yaml&lt;/code&gt; (cluster configuration), &lt;code dir=&quot;auto&quot;&gt;kind.yaml&lt;/code&gt; (Kind config), and &lt;code dir=&quot;auto&quot;&gt;k8s/&lt;/code&gt; (your Kubernetes manifests).&lt;/p&gt;
&lt;p&gt;For all available flags and configuration options, see the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-init&quot;&gt;CLI flags reference&lt;/a&gt; — All &lt;code dir=&quot;auto&quot;&gt;cluster init&lt;/code&gt; options&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/configuration/declarative-configuration&quot;&gt;ksail.yaml reference&lt;/a&gt; — Configuration file schema&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ksail.devantler.tech/features&quot;&gt;Features overview&lt;/a&gt; — CNI, CSI, GitOps, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3-create-the-cluster&quot;&gt;Step 3: Create the Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Create and start your cluster:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creates Docker containers as Kubernetes nodes&lt;/li&gt;
&lt;li&gt;Bootstraps the Kubernetes control plane&lt;/li&gt;
&lt;li&gt;Installs your selected CNI, CSI, and other components&lt;/li&gt;
&lt;li&gt;Configures your local kubeconfig&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The process takes under 60 seconds for a single-node cluster.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-4-working-with-your-cluster&quot;&gt;Step 4: Working with Your Cluster&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once your cluster is running, KSail provides commands for common operations:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Show cluster status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# List all KSail-managed clusters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# Open K9s for interactive management&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stop&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# Stop the cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# Start a stopped cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your kubeconfig is automatically configured, so standard &lt;code dir=&quot;auto&quot;&gt;kubectl&lt;/code&gt; commands work too.&lt;/p&gt;
&lt;p&gt;For the full command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/cluster/cluster-root&quot;&gt;Cluster Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-5-deploying-workloads&quot;&gt;Step 5: Deploying Workloads&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;KSail wraps kubectl and GitOps operations under the &lt;code dir=&quot;auto&quot;&gt;workload&lt;/code&gt; command:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./k8s&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Apply manifests (kubectl workflow)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;# Push to GitOps source&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;workload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reconcile&lt;/span&gt;&lt;span&gt;         &lt;/span&gt;&lt;span&gt;# Trigger GitOps reconciliation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For the full workload command reference, see &lt;a href=&quot;https://ksail.devantler.tech/configuration/cli-flags/workload/workload-root&quot;&gt;Workload Commands&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;cleaning-up&quot;&gt;Cleaning Up&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;When you’re done:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ksail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cluster&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This removes the Docker containers and cleans up kubeconfig entries.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Explore the &lt;a href=&quot;https://ksail.devantler.tech&quot;&gt;KSail documentation&lt;/a&gt; for advanced topics including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding Cilium or Calico as your CNI&lt;/li&gt;
&lt;li&gt;Enabling GitOps with Flux or ArgoCD&lt;/li&gt;
&lt;li&gt;Secret management with SOPS&lt;/li&gt;
&lt;li&gt;Mirror registries to avoid Docker Hub rate limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If Kind’s vanilla Kubernetes feels too basic, check out the posts on &lt;a href=&quot;https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-k3d/&quot;&gt;K3s with KSail&lt;/a&gt; for a more batteries-included experience, or &lt;a href=&quot;https://devantler.tech/blog/local-kubernetes-development-with-ksail-and-talos/&quot;&gt;Talos with KSail&lt;/a&gt; for an immutable, security-focused distribution.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;feedback-welcome&quot;&gt;Feedback Welcome&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;KSail is under active development. If you encounter bugs or find missing features, please &lt;a href=&quot;https://github.com/devantler-tech/ksail/issues&quot;&gt;open an issue on GitHub&lt;/a&gt;. Your feedback helps improve the tool for everyone.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>kubernetes</category><category>ksail</category><category>kind</category><category>local-development</category></item><item><title>MacOS as a Developer Machine</title><link>https://devantler.tech/blog/macos-as-a-developer-machine/</link><guid isPermaLink="true">https://devantler.tech/blog/macos-as-a-developer-machine/</guid><description>Setting up MacOS as a developer machine can be a daunting task. In this post, I will share my learnings and experiences to help you get started.

</description><pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post, I will share my experience setting up MacOS as a developer machine. I will cover the following topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#managing-packages&quot;&gt;Managing Packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#managing-dotfiles-and-system-configuration&quot;&gt;Managing Dotfiles and System Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#git-ssh-and-gpg-keys&quot;&gt;Git, SSH, and GPG Keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#passwords-and-secrets&quot;&gt;Passwords and Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#configuring-the-terminal&quot;&gt;Configuring the Terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-up-your-ide&quot;&gt;Setting Up Your IDE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#remote-development&quot;&gt;Remote Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;managing-packages&quot;&gt;Managing Packages&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There are a few options available for managing packages on MacOS. Notable package managers include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://brew.sh&quot;&gt;Homebrew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.macports.org&quot;&gt;MacPorts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Due to Homebrew seemingly having the most popularity and the largest community, I went with Homebrew, and I have been using it extensively, for a few years now. In my experience, Homebrew has been reliable and easy to use, and I have yet to encounter that it is missing a feature that I need, or that it is not working as expected.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;installing-and-using-homebrew&quot;&gt;Installing and Using Homebrew&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Installing Homebrew is straightforward, and is well documented on the &lt;a href=&quot;https://brew.sh&quot;&gt;Homebrew website&lt;/a&gt;. Once installed, you can install packages using the &lt;code dir=&quot;auto&quot;&gt;brew install&lt;/code&gt; command. For example, to install Git, you can run:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It can also be used to install applications (Homebrew calls these casks), such as Visual Studio Code:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;visual-studio-code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Homebrew can also snapshot your installed packages and applications, which can be useful when migrating to a new machine, or when you own multiple machines. You will learn how I prefer to manage my system configuration including my packages and applications in the next section &lt;a href=&quot;#managing-dotfiles-and-system-configuration&quot;&gt;Managing Dotfiles and System Configuration&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;benefits-of-using-homebrew&quot;&gt;Benefits of Using Homebrew&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Easy to install and use&lt;/li&gt;
&lt;li&gt;Extensive library of packages&lt;/li&gt;
&lt;li&gt;Ability to install applications&lt;/li&gt;
&lt;li&gt;Ability to snapshot installed packages and applications&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;managing-dotfiles-and-system-configuration&quot;&gt;Managing Dotfiles and System Configuration&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Many applications and packages require storing configuration files in your home directory. These files are often referred to as dotfiles, as the folders and files are prefixed with a dot (&lt;code dir=&quot;auto&quot;&gt;.&lt;/code&gt;). Examples of dotfiles include &lt;code dir=&quot;auto&quot;&gt;.bashrc&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;.gitconfig&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;.vimrc&lt;/code&gt;, but there are many more.&lt;/p&gt;
&lt;p&gt;When you use valuable time configuring, for example, your Git settings, you probably want to keep these settings when you switch to a new machine. This is where dotfiles come in handy. By storing your dotfiles in a version-controlled repository, you can easily sync your configuration across multiple machines.&lt;/p&gt;
&lt;p&gt;However keeping stuff in sync is no simple task, and there are a multitude of ways to do it. I have personally tried a few different approaches, that varied in complexity and flexibility. It was not until I discovered &lt;a href=&quot;https://github.com/lra/mackup&quot;&gt;MackUp&lt;/a&gt; that I found a solution that worked well for me.&lt;/p&gt;
&lt;p&gt;MackUp is a simple utility that, when executed, will symlink your dotfiles and system configuration to a storage provider of your choice. It supports a variety of storage providers, such as Dropbox, Google Drive, OneDrive, or Git. As I prefer to keep most of my stuff in Git, I of course use the Git storage provider. As such the following examples will also assume that Git is used as the storage provider.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;installing-and-using-mackup&quot;&gt;Installing and Using MackUp&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;To get started with MackUp, you can install it using Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;After installing MackUp, you can configure it by creating a &lt;code dir=&quot;auto&quot;&gt;.mackup.cfg&lt;/code&gt; file in your home directory. Here is an example configuration file:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[storage]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt; = git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span&gt; = ~/dotfiles&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This configuration tells MackUp to store dotfiles in a Git repository located at &lt;code dir=&quot;auto&quot;&gt;~/dotfiles&lt;/code&gt;. You can then run &lt;code dir=&quot;auto&quot;&gt;mackup backup&lt;/code&gt; to symlink your dotfiles to the Git repository. When you switch to a new machine, you can run &lt;code dir=&quot;auto&quot;&gt;mackup restore&lt;/code&gt; to restore your dotfiles.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;storing-brew-bundles-in-mackup&quot;&gt;Storing Brew Bundles in MackUp&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;In addition to storing your dotfiles, you can also store your Homebrew packages and applications in MackUp. This can be done by creating a Brewfile, which can be used to snapshot all the packages and applications you have installed on a machine. You can create a Brewfile of your installed packages and applications by running:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dump&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will create a Brewfile in your home directory that lists all your installed packages and applications. To install these packages and applications on a new machine, you can run:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;The Brewfile is automatically picked up by MackUp when you run &lt;code dir=&quot;auto&quot;&gt;mackup backup&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h3 id=&quot;convenience-scripts&quot;&gt;Convenience Scripts&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;To make it easier to manage your dotfiles and system configuration, you can create convenience scripts that automate the process. Here are the scripts I use for managing my dotfiles:&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;backup-script&quot;&gt;Backup Script&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;This script will backup your dotfiles and system configuration to a Git repository, and store your Homebrew packages and applications in a Brewfile. It supports both macOS and Linux. To run the script:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backup.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# To make the script executable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./backup.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The script must be placed in the root of your dotfiles repository. Run it from your home directory.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/bin/bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;check_os&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Darwin&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;#x26;&amp;#x26; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Linux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;This script is only for macOS and Linux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;create_mackup_config&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;📝 Creating Mackup config&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/.mackup.cfg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[storage]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;engine = file_system&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;path = &lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/dotfiles&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/.mackup.cfg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_homebrew&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🍺 Installing Homebrew&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/bin/bash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;eval &quot;$(/opt/homebrew/bin/brew shellenv)&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;/.zprofile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;/opt/homebrew/bin/brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shellenv&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_rosetta&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;arch&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;📄 Installing Rosetta 2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;softwareupdate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--install-rosetta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--agree-to-license&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_mackup&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;📦 Installing Mackup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;local&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;stop_gpg_agent&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🔒 Stopping GPG agent&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--kill&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;elif&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-connect-agent&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpg-connect-agent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;killagent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/bye&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;GPG tools not found, skipping GPG agent stop&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;start_gpg_agent&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🔑 Starting GPG agent&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--daemon&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;elif&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--reload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;GPG tools not found, skipping GPG agent start&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;backup_homebrew&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🍻 Backing up Homebrew packages&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dump&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--force&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;upgrade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;check_os&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;create_mackup_config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_homebrew&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Darwin&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;install_rosetta&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_mackup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;stop_gpg_agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;backup_homebrew&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--force&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;start_gpg_agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;restore-script&quot;&gt;Restore Script&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;This script will restore your dotfiles and system configuration from a Git repository, and install your Homebrew packages and applications from a Brewfile. It supports both macOS and Linux. To run the script:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restore.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# To make the script executable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./restore.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Run this script from your home directory on a new machine to restore your complete configuration.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/bin/bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;check_os&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Darwin&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;#x26;&amp;#x26; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Linux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;This script is only for macOS and Linux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_homebrew&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🍺 Installing Homebrew&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/bin/bash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;eval &quot;$(/opt/homebrew/bin/brew shellenv)&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;/.zprofile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;/opt/homebrew/bin/brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shellenv&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_rosetta&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;arch&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;📄 Installing Rosetta 2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;softwareupdate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--install-rosetta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--agree-to-license&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;local&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sync_homebrew&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🍻 Sync Homebrew packages&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cleanup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--force&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--force&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;upgrade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;stop_gpg_agent&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🔒 Stopping GPG agent&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--kill&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;elif&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-connect-agent&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpg-connect-agent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;killagent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/bye&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;GPG tools not found, skipping GPG agent stop&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;start_gpg_agent&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;echo_title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🔑 Starting GPG agent&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--daemon&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;elif&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--reload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;GPG tools not found, skipping GPG agent start&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;check_os&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;install_homebrew&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Darwin&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;install_rosetta&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;stop_gpg_agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sync_homebrew&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mackup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--force&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;start_gpg_agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;benefits-of-using-mackup&quot;&gt;Benefits of Using MackUp&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Easy to install and use&lt;/li&gt;
&lt;li&gt;Supports a variety of storage providers&lt;/li&gt;
&lt;li&gt;Supports symlinking dotfiles and system configuration&lt;/li&gt;
&lt;li&gt;Supports restoring dotfiles and system configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;git-ssh-and-gpg-keys&quot;&gt;Git, SSH, and GPG Keys&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Git is the de facto standard for version control, and it is essential for any developer. Setting up Git with SSH and GPG keys is important for secure and authenticated commits.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;installing-git&quot;&gt;Installing Git&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Git can be installed using Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;configuring-git&quot;&gt;Configuring Git&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;After installing Git, you can configure your user settings:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Your Name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;your-email@example.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;core.editor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nano&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I also recommend enabling some useful Git settings:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Enable auto-pruning on fetch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch.prune&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Enable auto-stash on rebase&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git.autoStash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Enable colored diff output with moved lines detection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;diff.colorMoved&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zebra&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;setting-up-ssh-keys&quot;&gt;Setting Up SSH Keys&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;SSH keys are used to authenticate with remote Git repositories. To generate a new SSH key:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Generate an Ed25519 key (recommended)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ed25519&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;your-email@example.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Start the SSH agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;ssh-agent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Add your key to the agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh-add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/id_ed25519&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To use macOS Keychain for SSH key management, create or edit &lt;code dir=&quot;auto&quot;&gt;~/.ssh/config&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Host *&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UseKeychain yes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This configuration ensures that your SSH passphrase is stored securely in the macOS Keychain, so you don’t have to enter it repeatedly.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;setting-up-gpg-keys-for-signed-commits&quot;&gt;Setting Up GPG Keys for Signed Commits&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;GPG signing adds an extra layer of trust to your commits. It verifies that commits are actually made by you.&lt;/p&gt;
&lt;p&gt;First, install GPG and Pinentry for macOS:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gnupg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pinentry-mac&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Generate a new GPG key:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--full-generate-key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Configure GPG to use Pinentry for passphrase entry. Create or edit &lt;code dir=&quot;auto&quot;&gt;~/.gnupg/gpg-agent.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;default-cache-ttl 600&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;max-cache-ttl 7200&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pinentry-program /opt/homebrew/bin/pinentry-mac&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Configure Git to use your GPG key:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Get your key ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-secret-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--keyid-format=long&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Configure Git to use your key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.signingKey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;YOUR_KEY_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit.gpgsign&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg.program&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/homebrew/bin/gpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;benefits-of-ssh-and-gpg&quot;&gt;Benefits of SSH and GPG&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH Keys&lt;/strong&gt;: Secure, passwordless authentication to Git remotes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPG Signing&lt;/strong&gt;: Cryptographic proof that commits are made by you&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keychain Integration&lt;/strong&gt;: Secure storage of passphrases on macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;passwords-and-secrets&quot;&gt;Passwords and Secrets&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Managing passwords and secrets securely is critical for any developer. I use a combination of tools to handle this.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;apple-notes-with-locked-notes&quot;&gt;Apple Notes with Locked Notes&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For storing sensitive values, tokens, and keys, I use Apple’s built-in Notes app with locked notes. This approach is surprisingly effective and has several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Built-in to macOS&lt;/strong&gt;: No additional software to install&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iCloud Sync&lt;/strong&gt;: Seamlessly syncs across all Apple devices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password or Touch ID Protection&lt;/strong&gt;: Notes can be locked and unlocked with your device password or biometrics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;End-to-End Encryption&lt;/strong&gt;: Locked notes are encrypted with your device passcode&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To lock a note in Apple Notes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create or open a note&lt;/li&gt;
&lt;li&gt;Click the lock icon in the toolbar (or right-click and select “Lock Note”)&lt;/li&gt;
&lt;li&gt;Set a password if prompted (or use your device password)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This simple approach keeps my API tokens, SSH passphrases, and other sensitive values readily accessible but secure.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;sops-for-secret-management-in-code&quot;&gt;SOPS for Secret Management in Code&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For managing secrets in code and configuration files, I use &lt;a href=&quot;https://github.com/getsops/sops&quot;&gt;SOPS (Secrets OPerationS)&lt;/a&gt;. SOPS encrypts secrets in YAML, JSON, and other formats while keeping the structure readable.&lt;/p&gt;
&lt;p&gt;Install SOPS with Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sops&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;SOPS integrates well with GitOps workflows, allowing you to store encrypted secrets in Git repositories safely.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;mkcert-for-local-development-certificates&quot;&gt;mkcert for Local Development Certificates&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For local HTTPS development, &lt;a href=&quot;https://github.com/FiloSottile/mkcert&quot;&gt;mkcert&lt;/a&gt; is invaluable. It creates locally-trusted development certificates.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkcert&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Install the local CA&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkcert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Create certificates for localhost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkcert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;127.0.0.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;configuring-the-terminal&quot;&gt;Configuring the Terminal&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A well-configured terminal can significantly boost your productivity. Here’s how I set up mine.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;using-zsh&quot;&gt;Using Zsh&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;macOS comes with Zsh as the default shell. My &lt;code dir=&quot;auto&quot;&gt;.zshrc&lt;/code&gt; is kept simple and focused:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Source shell integrations&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/homebrew/opt/chruby/share/chruby/chruby.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/homebrew/opt/chruby/share/chruby/auto.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chruby&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ruby-3.4.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# VS Code shell integration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[[ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TERM_PROGRAM&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vscode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]] &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;code-insiders&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--locate-shell-integration-path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zsh&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Initialize Starship prompt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;starship&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zsh&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;starship-prompt&quot;&gt;Starship Prompt&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://starship.rs&quot;&gt;Starship&lt;/a&gt; is a minimal, fast, and customizable prompt for any shell. It shows relevant information based on your current directory context (Git status, programming language versions, etc.).&lt;/p&gt;
&lt;p&gt;Install Starship:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;starship&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Add to your &lt;code dir=&quot;auto&quot;&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;starship&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zsh&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Starship automatically detects your project type and shows relevant information without any additional configuration.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;chruby-for-ruby-version-management&quot;&gt;chruby for Ruby Version Management&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For Ruby version management, I use &lt;a href=&quot;https://github.com/postmodern/chruby&quot;&gt;chruby&lt;/a&gt; which is lightweight and simple:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chruby&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ruby-install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Install a Ruby version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ruby-install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ruby&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3.4.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;benefits-of-this-terminal-setup&quot;&gt;Benefits of This Terminal Setup&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minimal Configuration&lt;/strong&gt;: Keep things simple and maintainable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast Startup&lt;/strong&gt;: No heavy frameworks slowing down terminal launch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context-Aware Prompts&lt;/strong&gt;: Starship shows relevant info automatically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy Version Management&lt;/strong&gt;: chruby for Ruby, similar tools for other languages&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;setting-up-your-ide&quot;&gt;Setting Up Your IDE&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Visual Studio Code (or VS Code Insiders in my case) is my editor of choice. Here’s how I configure it for maximum productivity.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;installing-vs-code&quot;&gt;Installing VS Code&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;visual-studio-code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Or for the Insiders build:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;visual-studio-code@insiders&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;essential-settings&quot;&gt;Essential Settings&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Here are some of the key settings from my configuration:&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;font-configuration&quot;&gt;Font Configuration&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;I use the &lt;a href=&quot;https://monaspace.githubnext.com&quot;&gt;Monaspace&lt;/a&gt; font family with font ligatures enabled:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.fontFamily&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&apos;Monaspace Krypton&apos;, monospace&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.fontLigatures&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&apos;ss01&apos;, &apos;ss02&apos;, &apos;ss03&apos;, &apos;ss04&apos;, &apos;ss05&apos;, &apos;ss06&apos;, &apos;ss07&apos;, &apos;ss08&apos;, &apos;calt&apos;, &apos;dlig&apos;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.fontSize&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;terminal.integrated.fontFamily&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&apos;Monaspace Krypton&apos;, monospace&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Install the font with Homebrew:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;font-monaspace&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;editor-behavior&quot;&gt;Editor Behavior&lt;/h4&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.formatOnSave&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.formatOnSaveMode&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;modificationsIfAvailable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.tabSize&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.rulers&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;120&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.minimap.enabled&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.guides.bracketPairs&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.smoothScrolling&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.cursorBlinking&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;smooth&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.cursorSmoothCaretAnimation&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;git-integration&quot;&gt;Git Integration&lt;/h4&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.enableCommitSigning&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.alwaysSignOff&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.autofetch&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.pruneOnFetch&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.rebaseWhenSync&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.branchProtection&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;trunk&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;file-explorer&quot;&gt;File Explorer&lt;/h4&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;explorer.fileNesting.enabled&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;explorer.sortOrder&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;explorer.confirmDelete&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;files.autoSave&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;afterDelay&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;essential-extensions&quot;&gt;Essential Extensions&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Here are some of the extensions I use, categorized by purpose:&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;ai-and-productivity&quot;&gt;AI and Productivity&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Copilot&lt;/li&gt;
&lt;li&gt;GitHub Copilot Chat&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;languages-and-frameworks&quot;&gt;Languages and Frameworks&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Go (golang.go)&lt;/li&gt;
&lt;li&gt;C# Dev Kit (ms-dotnettools.csdevkit)&lt;/li&gt;
&lt;li&gt;Docker (ms-azuretools.vscode-docker)&lt;/li&gt;
&lt;li&gt;Kubernetes Tools (ms-kubernetes-tools.vscode-kubernetes-tools)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;git-and-github&quot;&gt;Git and GitHub&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Pull Requests and Issues&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;code-quality&quot;&gt;Code Quality&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;ESLint&lt;/li&gt;
&lt;li&gt;Prettier&lt;/li&gt;
&lt;li&gt;EditorConfig&lt;/li&gt;
&lt;li&gt;Markdownlint&lt;/li&gt;
&lt;li&gt;ShellCheck&lt;/li&gt;
&lt;li&gt;Code Spell Checker&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;markdown-and-documentation&quot;&gt;Markdown and Documentation&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Markdown All in One&lt;/li&gt;
&lt;li&gt;Markdown Preview GitHub Styles&lt;/li&gt;
&lt;li&gt;Mermaid diagrams for Markdown&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;remote-development&quot;&gt;Remote Development&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Remote - SSH&lt;/li&gt;
&lt;li&gt;Remote Repositories&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h4 id=&quot;theme-and-icons&quot;&gt;Theme and Icons&lt;/h4&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Vira Theme (my preferred dark theme)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;editorconfig-for-consistent-formatting&quot;&gt;EditorConfig for Consistent Formatting&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;I use EditorConfig to maintain consistent coding styles across different editors and IDEs:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.editorconfig&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;root = true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[*]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;indent_style = space&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;indent_size = 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end_of_line = lf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;charset = utf-8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trim_trailing_whitespace = true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;insert_final_newline = true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;benefits-of-this-ide-setup&quot;&gt;Benefits of This IDE Setup&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consistent Formatting&lt;/strong&gt;: EditorConfig and format-on-save ensure consistent code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integrated Git&lt;/strong&gt;: Signed commits and branch protection built-in&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI Assistance&lt;/strong&gt;: GitHub Copilot for intelligent code suggestions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Language Support&lt;/strong&gt;: First-class support for Go, C#, TypeScript, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;remote-development-1&quot;&gt;Remote Development&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Modern development often involves working with remote machines, containers, and cloud environments. Here’s how I handle remote development.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;vs-code-remote-ssh&quot;&gt;VS Code Remote SSH&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The Remote SSH extension allows you to develop on remote machines as if they were local:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# In VS Code, press Cmd+Shift+P and type:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# &quot;Remote-SSH: Connect to Host...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;My SSH config (&lt;code dir=&quot;auto&quot;&gt;~/.ssh/config&lt;/code&gt;) is kept simple with Keychain integration:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Host *&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UseKeychain yes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;docker-desktop&quot;&gt;Docker Desktop&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Docker is essential for containerized development. Install it with:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker-desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Docker provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Local Containers&lt;/strong&gt;: Run applications in isolated environments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt;: Built-in single-node Kubernetes cluster for testing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VS Code Integration&lt;/strong&gt;: Seamless integration with the Docker extension&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;kubernetes-tools&quot;&gt;Kubernetes Tools&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For Kubernetes development, I use several tools:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Kubernetes CLI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;kubernetes-cli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Helm for package management&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;helm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Flux for GitOps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fluxcd/tap/flux&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# KSail for local cluster management (my own project!)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;devantler-tech/tap/ksail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;cloud-clis&quot;&gt;Cloud CLIs&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;For cloud development, I have the major cloud provider CLIs installed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# AWS CLI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;awscli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Azure CLI (via kubelogin for AKS)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;kubelogin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Hetzner Cloud CLI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hcloud&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;benefits-of-remote-development&quot;&gt;Benefits of Remote Development&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consistent Environments&lt;/strong&gt;: Docker ensures the same environment everywhere&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalable Resources&lt;/strong&gt;: Develop on powerful remote machines when needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitOps Workflows&lt;/strong&gt;: Flux and other tools enable declarative infrastructure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud-Native Development&lt;/strong&gt;: First-class support for Kubernetes and cloud services&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Setting up macOS as a developer machine requires thoughtful configuration, but the investment pays off in productivity and consistency. The key takeaways from my setup are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Use Homebrew&lt;/strong&gt; for package management — it’s reliable and comprehensive&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manage dotfiles with MackUp&lt;/strong&gt; — keeps configurations in sync across machines&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure your identity&lt;/strong&gt; with SSH and GPG keys for authenticated commits&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in your terminal&lt;/strong&gt; — a good prompt and shell configuration boosts productivity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure your IDE thoroughly&lt;/strong&gt; — VS Code with the right extensions is incredibly powerful&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace remote development&lt;/strong&gt; — Docker, Kubernetes, and cloud tools are essential for modern development&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I hope this guide helps you set up your own macOS developer machine. Feel free to adapt these configurations to your own needs and preferences.&lt;/p&gt;</content:encoded><category>macos</category><category>developer-experience</category><category>tooling</category></item><item><title>Welcome!</title><link>https://devantler.tech/blog/welcome/</link><guid isPermaLink="true">https://devantler.tech/blog/welcome/</guid><description>A welcome post to introduce myself and the site.

</description><pubDate>Wed, 24 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello everyone! Welcome to my site 👋🏻&lt;/p&gt;
&lt;p&gt;I am Nikolai Emil Damm AKA Devantler. I am a software engineer with a strong passion for open-source, and I believe that working transparently and collaboratively is the best way to create high-quality software. I have been working in the software industry for many years, and I have experience in a wide range of technologies and domains.&lt;/p&gt;
&lt;p&gt;I created this site to share my learnings and experiences with the broader community. I will be writing about software development, open-source, cloud computing, and other topics that I find interesting. I hope that you find my posts useful and informative, and I welcome any feedback or suggestions that you may have.&lt;/p&gt;
&lt;p&gt;Peace ✌🏻&lt;/p&gt;</content:encoded><category>personal</category></item></channel></rss>