<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>bismigel</title><link>https://bismigel.dev/</link><description>Recent content on bismigel</description><generator>Hugo</generator><language>en-us</language><copyright>bismigel</copyright><lastBuildDate>Mon, 12 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://bismigel.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>Secrets without plaintext</title><link>https://bismigel.dev/posts/2026-10-12-secrets-without-plaintext/</link><pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate><guid>https://bismigel.dev/posts/2026-10-12-secrets-without-plaintext/</guid><description/><content:encoded><![CDATA[<p>No secret sits in Git as readable text. Two tools handle the two halves.</p>
<h1 id="in-the-cluster-sealed-secrets">In the cluster: Sealed Secrets</h1>
<p>A controller in the cluster holds a private key. The matching public key encrypts:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl create secret generic app <span class="se">\
</span></span></span><span class="line"><span class="cl">  --from-literal<span class="o">=</span><span class="nv">KEY</span><span class="o">=</span>value --dry-run<span class="o">=</span>client -o yaml <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> kubeseal --format yaml &gt; sealed-secret.yaml</span></span></code></pre></div>
</div>
<p>The output is safe to commit. On sync the controller decrypts it into a real Secret.</p>
<h1 id="on-the-host-sops-with-age">On the host: SOPS with age</h1>
<p>Host secrets, such as tunnel credentials, stay encrypted in the NixOS repo and are decrypted at activation. The age identity is the machine's own SSH host key.</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sops secrets/homelab.yaml    <span class="c1"># edit; re-encrypts on save</span></span></span></code></pre></div>
</div>
<h1 id="the-rule">The rule</h1>
<p>Plaintext never enters Git. If a value has to live in the repo, it is encrypted first.</p>
<blockquote>
<p>Back up the sealing key. Without it, sealed secrets cannot be opened again.</p>
</blockquote>
]]></content:encoded></item><item><title>How this site is built</title><link>https://bismigel.dev/posts/2026-10-02-how-this-site-is-built/</link><pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate><guid>https://bismigel.dev/posts/2026-10-02-how-this-site-is-built/</guid><description/><content:encoded><![CDATA[<p>I write in Org, convert with pandoc, and build with Hugo. Here is the whole pipeline in one screen.</p>
<h1 id="writing">Writing</h1>
<p>Every post is a file in <code>org/</code>{.verbatim}. This one starts like:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">org</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-org" data-lang="org"><span class="line"><span class="cl"><span class="cs">#+TITLE</span><span class="c">: How this site is built</span>
</span></span><span class="line"><span class="cl"><span class="cs">#+DATE</span><span class="c">: 2026-10-02</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Body text in Org.</span></span></code></pre></div>
</div>
<p>Org gives me headings, tables, source blocks, footnotes, and export keywords. I never hand-write HTML.</p>
<h1 id="converting">Converting</h1>
<p>A small script turns each Org file into Hugo Markdown with front matter:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pandoc -s -f org -t markdown --wrap<span class="o">=</span>none org/post.org <span class="se">\
</span></span></span><span class="line"><span class="cl">  -o content/posts/post/index.md</span></span></code></pre></div>
</div>
<p>Pandoc maps <code>#+TITLE</code>{.verbatim} and <code>#+DATE</code>{.verbatim} to YAML front matter. One extra step maps <code>#+FILETAGS</code>{.verbatim} to Hugo tags.</p>
<h1 id="building-and-shipping">Building and shipping</h1>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">hugo --minify    <span class="c1"># Org -&gt; HTML into public/</span></span></span></code></pre></div>
</div>
<p>CI runs the same steps on every push:</p>
<ol>
<li>convert Org to Markdown</li>
<li>run <code>hugo --minify</code>{.verbatim}</li>
<li>build a small nginx image that contains <code>public/</code>{.verbatim}</li>
<li>push the image to GHCR and bump its tag</li>
</ol>
<p>ArgoCD watches the manifest and rolls out the new image. I never deploy by hand.</p>
<h1 id="four-jobs-four-tools">Four jobs, four tools</h1>
<p>Job          Tool</p>
<hr>
<p>writing      Org and Emacs
conversion   pandoc
build        Hugo
deploy       GHCR and ArgoCD</p>
<p>Each tool does one thing, which means I can replace any of them without touching the rest.</p>
]]></content:encoded></item><item><title>The design of the homelab</title><link>https://bismigel.dev/posts/2026-09-15-homelab-design/</link><pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate><guid>https://bismigel.dev/posts/2026-09-15-homelab-design/</guid><description/><content:encoded><![CDATA[<p>I finally have the homelab shaped the way I want, so this first post is a tour of how it's put together and <strong>why</strong> the pieces are where they are.</p>
<h1 id="two-machines-two-jobs">Two machines, two jobs</h1>
<p>There are two physical boxes:</p>
<ul>
<li>
<p>The hosting box: an AMD Ryzen 5 4600G (6c/12t). This was leftover parts from my old pc I replaced a gen 2 ryzen with 4600g (which I heard was better for power. bless the AM4 platform) It serves the websites and any experimental apps that I want to deploy.</p>
</li>
<li>
<p>The media box: an Intel i5-7500T. I bought this for 100$ from a guy from facebook marketplace. I've read somewhere that this generation supports quicksync and can support hardware decoding. It will run my media stack.</p>
</li>
</ul>
<p>Based on that, I deliberately have split their apps The 4600g having more cores means its better for more general app and experiment and the 7500t supporting quicksync will contain my whole media stack.</p>
<h1 id="everything-is-nixos">Everything is NixOS</h1>
<p>Initially this was deployed in proxmox lxc but i didnt like the additional layer that I have to work with before getting to the container. Using baremetal will remove that layer. I chose nixos specifically for the ease of configuration and avoiding drift from spec.</p>
<p>Both run NixOS, declared in a single flake. The OS is code: hosts, modules, disko disk layouts, and a <code>deploy-rs</code>{.verbatim} hook so I can push a rebuild over SSH:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">nix run github:serokell/deploy-rs -- .#&lt;host&gt;</span></span></code></pre></div>
</div>
<p>There is no half-configured box drifting over time. If I need to rebuild a machine, I reinstall from the same flake and I am back to a known state.</p>
<h1 id="kubernetes-but-boring">Kubernetes, but boring</h1>
<p>This would have worked with just docker instead of a single node k3s but I specifically chose this in order to experiment and troubleshoot and experience managing a kubernetes architecture. This would also mean I can use existing gitops tools like Flux and ArgoCd instead of having something like an ansible job that deploys to servers.</p>
<p>Each box runs a single-node <code>k3s</code>. I disabled Traefik and the built-in service load balancer on purpose and use <strong>ingress-nginx</strong> instead, so ingress is explicit and identical on both machines.</p>
<h1 id="gitops-with-argocd">GitOps with ArgoCD</h1>
<p>Cluster state lives in git and <strong>ArgoCD</strong> reconciles it. One repo (<code>bakuran-gitops</code>) is the source of truth: an app-of-apps root points at a folder of <code>Application</code>{.verbatim} definitions, and infra add-ons (ingress-nginx, sealed-secrets) sit alongside the apps.</p>
<p>I do not run deploys from CI. Instead:</p>
<ol>
<li>I push application code.</li>
<li>CI builds a container image and pushes it to GHCR.</li>
<li>CI bumps the image tag in the manifests.</li>
<li>ArgoCD notices and rolls it out.</li>
</ol>
<p>CI never holds cluster credentials. It only pushes a tag. That is the whole trick, and it is why a compromised build cannot touch the cluster.</p>
<h1 id="applications-are-reproducible">Applications are reproducible</h1>
<p>Every app repo ships a <code>deploy/k8s/</code>{.verbatim} folder (Deployment, Service, Ingress) and a GitHub Actions workflow that builds and pushes its image. To start something new I use <code>homelab-app-template</code>, which wires all of that up. I just write the app.</p>
<h1 id="the-edge-is-one-cloudflare-tunnel">The edge is one Cloudflare tunnel</h1>
<p>Nothing is port-forwarded. A single <code>cloudflared</code> tunnel connects out to Cloudflare, and public hostnames (like this one) route to <code>ingress-nginx</code> on the hosting box. TLS terminates at Cloudflare.</p>
<h1 id="secrets">Secrets</h1>
<p>Cluster secrets are <strong>Sealed Secrets</strong>: encrypted in git, decrypted by a controller in the cluster. Host level secrets, such as the tunnel credentials, are *SOPS*/age encrypted in the NixOS repo and decrypted at activation, keyed off each machine's SSH host key.</p>
<h1 id="what-it-looks-like-end-to-end">What it looks like end to end</h1>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">example</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <pre tabindex="0"><code class="language-example" data-lang="example">push -&gt; CI (build+push image+tag bump) -&gt; ArgoCD -&gt; k3s -&gt; ingress-nginx
                                                        -&gt; cloudflared -&gt; internet</code></pre>
</div>
]]></content:encoded></item></channel></rss>