<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>homelab &amp;mdash; Jerry of the Week</title>
    <link>https://write.in0rdr.ch/tag:homelab</link>
    <description>ˈdʒɛri - Individual who sends life against the grain no matter the consequences</description>
    <pubDate>Tue, 28 Apr 2026 13:01:43 +0000</pubDate>
    <item>
      <title>Nomad authentication with OpenBao</title>
      <link>https://write.in0rdr.ch/nomad-authentication-with-openbao</link>
      <description>&lt;![CDATA[I started to use OpenBao as OpenID connect provider to authenticate my Nomad home lab.&#xA;&#xA;#nomad #openbao #jenkins #homelab&#xA;!--more--&#xA;&#xA;I have two automatic/system jobs that require a Token.&#xA;&#xA;The backup cron job is taking Nomad snapshots in regular intervals. The snapshot API requires a management token and the Nomad policy capability for snapshots with the operator are not implemented yet.&#xA;The Jenkins server runs Nomad jobs using the Nomad cloud plugin. This system needs a Token to access the Nomad API (AppRole not compatible, see below).&#xA;&#xA;I still keep my bootstrapping token around just in case I ever need it. That&#39;s ok unlike to procedures with OpenBao root tokens..&#xA;&#xA;  The bootstrap token can be deleted and is like any other token, care should be taken to not revoke all management tokens.&#xA;&#xA;Name             Type        Global  Accessor ID  Expired&#xA;Bootstrap Token  management  true              false&#xA;Snapshot         management  false             false&#xA;OIDC-vault       client      true              false&#xA;Jenkins          client      false             false&#xA;&#xA;The auth method in Nomad is still called Vault, never mind..&#xA;&#xA;The access for human users is authenticated by an OIDC provider in my OpenBao server.&#xA;&#xA;Because I already had an identity and alias setup (userpass authentication) including a group, I only needed to configure the provider and the assignment to the group to allow authentication with the new provider.&#xA;&#xA;In the OpenBao server, the default provider and the allowall assignment cannot be deleted. I assume it is similar to the “master” realm in a Keycloak instance 🤔.&#xA;&#xA;bao-openidconnect-vault.svg&#xA;&#xA;I had to define a NOMADTOKEN as “Jenkins credential”, because the Nomad cloud plugin for Jenkins cannot read secrets from OpenBao using an AppRole (the Nomad jobs spawned by the plugin can do this, just not the plugin itself).&#xA;&#xA;When I type nomad login in the shell, the browser opens and I can authenticate with OpenBao. What could be improved is outputting the OIDC redirect URI in the terminal. This is helpful when you need to login from disconnected machines (i.e., not the shell on your local machine).&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>I started to use <a href="https://openbao.org/docs/secrets/identity/oidc-provider">OpenBao as OpenID connect provider</a> to authenticate my Nomad home lab.</p>

<p><a href="https://write.in0rdr.ch/tag:nomad" class="hashtag"><span>#</span><span class="p-category">nomad</span></a> <a href="https://write.in0rdr.ch/tag:openbao" class="hashtag"><span>#</span><span class="p-category">openbao</span></a> <a href="https://write.in0rdr.ch/tag:jenkins" class="hashtag"><span>#</span><span class="p-category">jenkins</span></a> <a href="https://write.in0rdr.ch/tag:homelab" class="hashtag"><span>#</span><span class="p-category">homelab</span></a>
</p>

<p>I have two automatic/system jobs that require a Token.</p>
<ul><li>The backup cron job is taking Nomad snapshots in regular intervals. The <a href="https://developer.hashicorp.com/nomad/api-docs/operator/snapshot">snapshot API</a> requires a management token and the <a href="https://github.com/hashicorp/nomad/issues/23614">Nomad policy capability for snapshots with the operator</a> are not implemented yet.</li>
<li>The Jenkins server runs Nomad jobs using the <a href="https://github.com/jenkinsci/nomad-plugin">Nomad cloud plugin</a>. This system needs a Token to access the Nomad API (AppRole not compatible, see below).</li></ul>

<p>I still keep my bootstrapping token around just in case I ever need it. That&#39;s <a href="https://developer.hashicorp.com/nomad/tutorials/archive/access-control-bootstrap">ok</a> unlike to procedures with OpenBao root tokens..</p>

<blockquote><p>The bootstrap token can be deleted and is like any other token, care should be taken to not revoke all management tokens.</p></blockquote>

<pre><code>Name             Type        Global  Accessor ID  Expired
Bootstrap Token  management  true    ***          false
Snapshot         management  false   ***          false
OIDC-vault       client      true    ***          false
Jenkins          client      false   ***          false
</code></pre>

<p>The auth method in Nomad is still called Vault, never mind..</p>

<p>The access for human users is authenticated by an <a href="https://developer.hashicorp.com/nomad/tutorials/archive/sso-oidc-vault">OIDC provider in my OpenBao server</a>.</p>

<p>Because I already had an identity and alias setup (userpass authentication) including a group, I only needed to configure the provider and the assignment to the group to allow authentication with the new provider.</p>

<p>In the OpenBao server, the <a href="https://openbao.org/docs/concepts/oidc-provider/#oidc-providers"><code>default</code> provider</a> and the <a href="https://openbao.org/docs/concepts/oidc-provider/#assignments"><code>allow_all</code> assignment</a> cannot be deleted. I assume it is similar to the “master” realm in a Keycloak instance 🤔.</p>

<p><img src="https://code.in0rdr.ch/pub/blog/bao-openidconnect-vault.svg" alt="bao-openidconnect-vault.svg"></p>

<p>I had to define a <code>NOMAD_TOKEN</code> as “Jenkins credential”, because the Nomad cloud plugin for Jenkins cannot read secrets from OpenBao using an AppRole (the Nomad jobs spawned by the plugin can do this, just not the plugin itself).</p>

<p>When I type <code>nomad login</code> in the shell, the browser opens and I can authenticate with OpenBao. What could be improved is outputting the OIDC redirect URI in the terminal. This is helpful when you need to login from disconnected machines (i.e., not the shell on your local machine).</p>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/nomad-authentication-with-openbao</guid>
      <pubDate>Sat, 15 Nov 2025 20:22:29 +0000</pubDate>
    </item>
    <item>
      <title>Emulate Raspberry Pi4 on QEMU</title>
      <link>https://write.in0rdr.ch/emulate-raspberry-pi4-on-qemu</link>
      <description>&lt;![CDATA[I was looking into emulating the Raspberry Pi OS on QEMU. This short post summarizes my findings.&#xA;&#xA;#raspberry #homelab #debian #qemu&#xA;!--more--&#xA;&#xA;I needed a virtual host to test Ansible scripts for my Raspberry Pi in the home lab. I found my way around this task by reading through the many great posts and examples online.&#xA;&#xA;Extract kernel and device tree&#xA;&#xA;First, you have to get the image (in my case, I used a modified build of the bookworm image) and extract the &#34;kernel&#34; and the &#34;device tree&#34; (to be honest, this was new for me when I read about this today).&#xA;&#xA;We do this by mounting the boot partition and extracting the relevant files.&#xA;&#xA;Check image partitions&#xA;$ fdisk -l ./HashiPi-pi0.img&#xA;&#xA;Setup loop device with image, scan partitions. This is easier than fiddling with fdisk and partition offsets for mounting.&#xA;&#xA;$ sudo losetup -P /dev/loop0 HashiPi-pi0.img&#xA;$ sudo mount /dev/loop0p1 /mnt&#xA;&#xA;copy kernel and device tree binary (dtb)&#xA;$ cp /mnt/kernel8.img .&#xA;$ cp /mnt/bcm2711-rpi-4-b.dtb .&#xA;&#xA;unmount&#xA;$ sudo umount /mnt&#xA;$ sudo losetup --detach /dev/loop0&#xA;&#xA;Patch device tree to enable USB controller&#xA;&#xA;The &#34;device tree binary&#34; (.dtb) needs to be translated into a readable &#34;device tree source&#34; (.dts) file.&#xA;&#xA;$ dtc -I dtb -O dts -o bcm2711-rpi-4-b.dts bcm2711-rpi-4-b.dtb&#xA;&#xA;The .dts file can be patched to enable the usb controller. This is required, if we want to boot later using the usbnet device and port-forward (hostfwd) the ssh port.&#xA;&#xA;--- bcm2711-rpi-4-b.dts.orig    2025-09-21 15:05:59.304575294 +0200&#xA;+++ bcm2711-rpi-4-b.dts 2025-09-21 15:04:56.709581742 +0200&#xA;@@ -1450,7 +1450,7 @@&#xA;                        phy-names = &#34;usb2-phy&#34;;&#xA;                        interrupt-names = &#34;usb&#34;, &#34;soft&#34;;&#xA;                        power-domains = 0x10 0x06;&#xA;status = &#34;disabled&#34;;&#xA;status = &#34;okay&#34;;&#xA;                        phandle = 0xbf;&#xA;                };&#xA;&#xA;Unfortunately, we need to use this usb device, because all other emulated network devices are pci based which is not supported by QEMU for the Raspberry Pi&#xA;&#xA;$ qemu-system-aarch64 -device help&#xA;...&#xA;Network devices:&#xA;name &#34;e1000&#34;, bus PCI, alias &#34;e1000-82540em&#34;, desc &#34;Intel Gigabit Ethernet&#34;&#xA;name &#34;e1000-82544gc&#34;, bus PCI, desc &#34;Intel Gigabit Ethernet&#34;&#xA;name &#34;e1000-82545em&#34;, bus PCI, desc &#34;Intel Gigabit Ethernet&#34;&#xA;name &#34;e1000e&#34;, bus PCI, desc &#34;Intel 82574L GbE Controller&#34;&#xA;name &#34;i82550&#34;, bus PCI, desc &#34;Intel i82550 Ethernet&#34;&#xA;name &#34;i82551&#34;, bus PCI, desc &#34;Intel i82551 Ethernet&#34;&#xA;name &#34;i82557a&#34;, bus PCI, desc &#34;Intel i82557A Ethernet&#34;&#xA;name &#34;i82557b&#34;, bus PCI, desc &#34;Intel i82557B Ethernet&#34;&#xA;name &#34;i82557c&#34;, bus PCI, desc &#34;Intel i82557C Ethernet&#34;&#xA;name &#34;i82558a&#34;, bus PCI, desc &#34;Intel i82558A Ethernet&#34;&#xA;name &#34;i82558b&#34;, bus PCI, desc &#34;Intel i82558B Ethernet&#34;&#xA;name &#34;i82559a&#34;, bus PCI, desc &#34;Intel i82559A Ethernet&#34;&#xA;name &#34;i82559b&#34;, bus PCI, desc &#34;Intel i82559B Ethernet&#34;&#xA;name &#34;i82559c&#34;, bus PCI, desc &#34;Intel i82559C Ethernet&#34;&#xA;name &#34;i82559er&#34;, bus PCI, desc &#34;Intel i82559ER Ethernet&#34;&#xA;name &#34;i82562&#34;, bus PCI, desc &#34;Intel i82562 Ethernet&#34;&#xA;name &#34;i82801&#34;, bus PCI, desc &#34;Intel i82801 Ethernet&#34;&#xA;name &#34;igb&#34;, bus PCI, desc &#34;Intel 82576 Gigabit Ethernet Controller&#34;&#xA;name &#34;ne2kpci&#34;, bus PCI&#xA;name &#34;pcnet&#34;, bus PCI&#xA;name &#34;rocker&#34;, bus PCI, desc &#34;Rocker Switch&#34;&#xA;name &#34;rtl8139&#34;, bus PCI&#xA;name &#34;tulip&#34;, bus PCI&#xA;-  name &#34;usb-net&#34;, bus usb-bus&#xA;name &#34;virtio-net-device&#34;, bus virtio-bus # No &#39;virtio-bus&#39; bus found for device &#39;virtio-net-device&#39;&#xA;name &#34;virtio-net-pci&#34;, bus PCI, alias &#34;virtio-net&#34;&#xA;name &#34;virtio-net-pci-non-transitional&#34;, bus PCI&#xA;name &#34;virtio-net-pci-transitional&#34;, bus PCI&#xA;name &#34;vmxnet3&#34;, bus PCI, desc &#34;VMWare Paravirtualized Ethernet v3&#34;&#xA;&#xA;We really need the usb device (virtio-net-device does not work either I tried), that&#39;s the reason for the patch.&#xA;&#xA;A comment of the &#34;interrupt.memfault blog&#34; describes it nicely (very helpful community):&#xA;&#xA;  Note that for raspi4, the bcm2711-rpi-4-b.dtb devicetree file has disabled the USB controller.&#xA;So, to enable USB keyboard &amp; mouse, the .dtb file must be decompiled to .dts,&#xA;patched, and recompiled back to .dtb.&#xA;&#xA;Unfortunately, it&#39;s not only needed for keyboard and mouse, but also to make our network adapter work (for ssh port-forwarding).&#xA;&#xA;Thaa patching.. Then recompiling into binary form with dtc:&#xA;$ dtc -I dtb -O dts -o bcm2711-rpi-4-b.dts bcm2711-rpi-4-b.dtb&#xA;&#xA;Would be really nice to have this usb controller enabled by default in the next Trixie Pi OS 🤞&#xA;&#xA;Boot the image&#xA;&#xA;For booting the image, I had to ensure two things for the kernel arguments for the Raspberry 4:&#xA;&#xA;Use the ttyAMA1 console&#xA;Use the root partition mmcblk1p2&#xA;&#xA;Note that I&#39;m not enabling the keyboard &amp; mouse devices (as suggested in the references online), because I&#39;m mainly interested to connect to the machine remotely via the ssh port fowarding:&#xA;&#xA;$ sudo qemu-system-aarch64 \&#xA;  -machine raspi4b -cpu cortex-a72 \&#xA;  -dtb bcm2711-rpi-4-b-mod.dtb \ # use mod device tree&#xA;  -m 2G -smp 4 \&#xA;  -kernel kernel8.img -sd HashiPi-pi0.img \&#xA;  -append &#34;rw earlyprintk loglevel=8 console=ttyAMA1,115200 dwcotg.lpm_enable=0 root=/dev/mmcblk1p2 rootdelay=1&#34; \&#xA;  -device usb-net,netdev=net0 \&#xA;  -netdev user,id=net0,hostfwd=tcp::2222-:22&#xA;&#xA;That&#39;s it. Is still a bit slow, but good enough to throw some Ansible against the wall and see if it sticks.&#xA;&#xA;My idea here is to do less with the HashiCorp packer scripts and go back to more Ansible, because that might be more sustainable in the long run.. Let&#39;s see. Next I&#39;m probably also going to give the Trixie (nightly image) a try.&#xA;&#xA;References&#xA;&#xA;https://interrupt.memfault.com/blog/emulating-raspberry-pi-in-qemu&#xA;https://www.qemu.org/docs/master/system/qemu-manpage.html&#xA;https://www.qemu.org/docs/master/system/arm/raspi.html&#xA;https://github.com/trinitronx/qemu-raspbian/blob/main/run-raspi4.sh&#xA;https://github.com/trinitronx/qemu-raspbian/blob/main/bcm2711-rpi-4-b.dts.patch&#xA;https://downloads.raspberrypi.org&#xA;https://www.man7.org/linux/man-pages/man8/losetup.8.html&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>I was looking into emulating the Raspberry Pi OS on QEMU. This short post summarizes my findings.</p>

<p><a href="https://write.in0rdr.ch/tag:raspberry" class="hashtag"><span>#</span><span class="p-category">raspberry</span></a> <a href="https://write.in0rdr.ch/tag:homelab" class="hashtag"><span>#</span><span class="p-category">homelab</span></a> <a href="https://write.in0rdr.ch/tag:debian" class="hashtag"><span>#</span><span class="p-category">debian</span></a> <a href="https://write.in0rdr.ch/tag:qemu" class="hashtag"><span>#</span><span class="p-category">qemu</span></a>
</p>

<p>I needed a virtual host to test Ansible scripts for my Raspberry Pi in the home lab. I found my way around this task by reading through the many great posts and examples online.</p>

<h2 id="extract-kernel-and-device-tree">Extract kernel and device tree</h2>

<p>First, you have to get the <a href="https://downloads.raspberrypi.org">image</a> (in my case, I used a modified build of the bookworm image) and extract the “kernel” and the <a href="https://www.kernel.org/doc/html/latest/devicetree/usage-model.html">“device tree”</a> (to be honest, this was new for me when I read about this today).</p>

<p>We do this by mounting the boot partition and extracting the relevant files.</p>

<pre><code class="language-bash"># Check image partitions
$ fdisk -l ./HashiPi-pi0.img
</code></pre>

<p>Setup loop device with image, scan partitions. This is easier than fiddling with fdisk and partition offsets for mounting.</p>

<pre><code class="language-bash">$ sudo losetup -P /dev/loop0 HashiPi-pi0.img
$ sudo mount /dev/loop0p1 /mnt

# copy kernel and device tree binary (dtb)
$ cp /mnt/kernel8.img .
$ cp /mnt/bcm2711-rpi-4-b.dtb .

# unmount
$ sudo umount /mnt
$ sudo losetup --detach /dev/loop0
</code></pre>

<h2 id="patch-device-tree-to-enable-usb-controller">Patch device tree to enable USB controller</h2>

<p>The “device tree binary” (.dtb) needs to be translated into a readable <a href="https://www.kernel.org/doc/html/latest/devicetree/bindings/dts-coding-style.html">“device tree source” (.dts)</a> file.</p>

<pre><code class="language-bash">$ dtc -I dtb -O dts -o bcm2711-rpi-4-b.dts bcm2711-rpi-4-b.dtb
</code></pre>

<p>The .dts file can be <a href="https://github.com/trinitronx/qemu-raspbian">patched</a> to enable the usb controller. This is required, if we want to boot later using the <a href="https://en.wikipedia.org/wiki/Ethernet_over_USB">usbnet</a> device and port-forward (<a href="https://www.qemu.org/docs/master/system/qemu-manpage.html"><code>hostfwd</code></a>) the ssh port.</p>

<pre><code class="language-diff">--- bcm2711-rpi-4-b.dts.orig    2025-09-21 15:05:59.304575294 +0200
+++ bcm2711-rpi-4-b.dts 2025-09-21 15:04:56.709581742 +0200
@@ -1450,7 +1450,7 @@
                        phy-names = &#34;usb2-phy&#34;;
                        interrupt-names = &#34;usb&#34;, &#34;soft&#34;;
                        power-domains = &lt;0x10 0x06&gt;;
-                       status = &#34;disabled&#34;;
+                       status = &#34;okay&#34;;
                        phandle = &lt;0xbf&gt;;
                };
</code></pre>

<p>Unfortunately, we need to use this usb device, because all other emulated network devices are pci based which is <a href="https://www.qemu.org/docs/master/system/arm/raspi.html">not supported by QEMU for the Raspberry Pi</a></p>

<pre><code class="language-bash">$ qemu-system-aarch64 -device help
...
Network devices:
name &#34;e1000&#34;, bus PCI, alias &#34;e1000-82540em&#34;, desc &#34;Intel Gigabit Ethernet&#34;
name &#34;e1000-82544gc&#34;, bus PCI, desc &#34;Intel Gigabit Ethernet&#34;
name &#34;e1000-82545em&#34;, bus PCI, desc &#34;Intel Gigabit Ethernet&#34;
name &#34;e1000e&#34;, bus PCI, desc &#34;Intel 82574L GbE Controller&#34;
name &#34;i82550&#34;, bus PCI, desc &#34;Intel i82550 Ethernet&#34;
name &#34;i82551&#34;, bus PCI, desc &#34;Intel i82551 Ethernet&#34;
name &#34;i82557a&#34;, bus PCI, desc &#34;Intel i82557A Ethernet&#34;
name &#34;i82557b&#34;, bus PCI, desc &#34;Intel i82557B Ethernet&#34;
name &#34;i82557c&#34;, bus PCI, desc &#34;Intel i82557C Ethernet&#34;
name &#34;i82558a&#34;, bus PCI, desc &#34;Intel i82558A Ethernet&#34;
name &#34;i82558b&#34;, bus PCI, desc &#34;Intel i82558B Ethernet&#34;
name &#34;i82559a&#34;, bus PCI, desc &#34;Intel i82559A Ethernet&#34;
name &#34;i82559b&#34;, bus PCI, desc &#34;Intel i82559B Ethernet&#34;
name &#34;i82559c&#34;, bus PCI, desc &#34;Intel i82559C Ethernet&#34;
name &#34;i82559er&#34;, bus PCI, desc &#34;Intel i82559ER Ethernet&#34;
name &#34;i82562&#34;, bus PCI, desc &#34;Intel i82562 Ethernet&#34;
name &#34;i82801&#34;, bus PCI, desc &#34;Intel i82801 Ethernet&#34;
name &#34;igb&#34;, bus PCI, desc &#34;Intel 82576 Gigabit Ethernet Controller&#34;
name &#34;ne2k_pci&#34;, bus PCI
name &#34;pcnet&#34;, bus PCI
name &#34;rocker&#34;, bus PCI, desc &#34;Rocker Switch&#34;
name &#34;rtl8139&#34;, bus PCI
name &#34;tulip&#34;, bus PCI
-&gt; name &#34;usb-net&#34;, bus usb-bus
name &#34;virtio-net-device&#34;, bus virtio-bus # No &#39;virtio-bus&#39; bus found for device &#39;virtio-net-device&#39;
name &#34;virtio-net-pci&#34;, bus PCI, alias &#34;virtio-net&#34;
name &#34;virtio-net-pci-non-transitional&#34;, bus PCI
name &#34;virtio-net-pci-transitional&#34;, bus PCI
name &#34;vmxnet3&#34;, bus PCI, desc &#34;VMWare Paravirtualized Ethernet v3&#34;
</code></pre>

<p>We really need the usb device (virtio-net-device does not work either I tried), that&#39;s the reason for the patch.</p>

<p>A <a href="https://community.memfault.com/t/emulating-a-raspberry-pi-in-qemu-interrupt/684/10">comment</a> of the “interrupt.memfault blog” describes it nicely (very helpful community):</p>

<blockquote><p>Note that for raspi4, the bcm2711-rpi-4-b.dtb devicetree file has disabled the USB controller.
So, to enable USB keyboard &amp; mouse, the .dtb file must be decompiled to .dts,
patched, and recompiled back to .dtb.</p></blockquote>

<p>Unfortunately, it&#39;s not only needed for keyboard and mouse, but also to make our network adapter work (for ssh port-forwarding).</p>

<p>Thaa patching.. Then recompiling into binary form with <code>dtc</code>:</p>

<pre><code class="language-bash">$ dtc -I dtb -O dts -o bcm2711-rpi-4-b.dts bcm2711-rpi-4-b.dtb
</code></pre>

<p>Would be really nice to have this usb controller enabled by default in the next Trixie Pi OS 🤞</p>

<h2 id="boot-the-image">Boot the image</h2>

<p>For booting the image, I had to ensure two things for the kernel arguments for the Raspberry 4:</p>
<ul><li>Use the <code>ttyAMA1</code> console</li>
<li>Use the root partition <code>mmcblk1p2</code></li></ul>

<p>Note that I&#39;m not enabling the keyboard &amp; mouse devices (as suggested in the references online), because I&#39;m mainly interested to connect to the machine remotely via the ssh port fowarding:</p>

<pre><code class="language-bash">$ sudo qemu-system-aarch64 \
  -machine raspi4b -cpu cortex-a72 \
  -dtb bcm2711-rpi-4-b-mod.dtb \ # use mod device tree
  -m 2G -smp 4 \
  -kernel kernel8.img -sd HashiPi-pi0.img \
  -append &#34;rw earlyprintk loglevel=8 console=ttyAMA1,115200 dwc_otg.lpm_enable=0 root=/dev/mmcblk1p2 rootdelay=1&#34; \
  -device usb-net,netdev=net0 \
  -netdev user,id=net0,hostfwd=tcp::2222-:22
</code></pre>

<p>That&#39;s it. Is still a bit slow, but good enough to throw some Ansible against the wall and see if it sticks.</p>

<p>My idea here is to do less with the HashiCorp packer scripts and go back to more Ansible, because that might be more sustainable in the long run.. Let&#39;s see. Next I&#39;m probably also going to give the Trixie (<a href="https://downloads.raspberrypi.org/nightlies/">nightly</a> image) a try.</p>

<h2 id="references">References</h2>
<ul><li><a href="https://interrupt.memfault.com/blog/emulating-raspberry-pi-in-qemu">https://interrupt.memfault.com/blog/emulating-raspberry-pi-in-qemu</a></li>
<li><a href="https://www.qemu.org/docs/master/system/qemu-manpage.html">https://www.qemu.org/docs/master/system/qemu-manpage.html</a></li>
<li><a href="https://www.qemu.org/docs/master/system/arm/raspi.html">https://www.qemu.org/docs/master/system/arm/raspi.html</a></li>
<li><a href="https://github.com/trinitronx/qemu-raspbian/blob/main/run-raspi4.sh">https://github.com/trinitronx/qemu-raspbian/blob/main/run-raspi4.sh</a></li>
<li><a href="https://github.com/trinitronx/qemu-raspbian/blob/main/bcm2711-rpi-4-b.dts.patch">https://github.com/trinitronx/qemu-raspbian/blob/main/bcm2711-rpi-4-b.dts.patch</a></li>
<li><a href="https://downloads.raspberrypi.org">https://downloads.raspberrypi.org</a></li>
<li><a href="https://www.man7.org/linux/man-pages/man8/losetup.8.html">https://www.man7.org/linux/man-pages/man8/losetup.8.html</a></li></ul>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/emulate-raspberry-pi4-on-qemu</guid>
      <pubDate>Sun, 21 Sep 2025 13:20:54 +0000</pubDate>
    </item>
    <item>
      <title>Docker pull through HAProxy</title>
      <link>https://write.in0rdr.ch/docker-pull-through-haproxy</link>
      <description>&lt;![CDATA[This is a story about pulling Docker images through HAProxy in my home lab.&#xA;&#xA;#selfhosting #homelab #docker #haproxy&#xA;!--more--&#xA;&#xA;I observed an interesting issue in my Jenkins pipeline. The image pull aborted with the following error message:&#xA;&#xA;Error: writing blob: storing blob to file &#34;/var/tmp/storage1360560957/1&#34;: happened during read: unexpected EOF&#xA;&#xA;First I thought it has something to do with the storage. But I was wrong. The culprit was the network.&#xA;&#xA;More specifically, I noticed that pulling through my HAProxy instance was the issue, but pulling through the nodes registry port (directly) was fine.&#xA;&#xA;When looking into the HAProxy logs, I noticed that the requests fail with the particular error flags cD:&#xA;&#xA;Sep 02 02:23:29 haproxy haproxy[2836]: 10.0.0.102:34982 [02/Sep/2025:02:22:47.563] registryfront registry/pi3 0/0/0/87/42156 200 466612119 - - cD-- 6/1/0/0/0 0/0 {haproxy.lan:5000} &#34;GET /v2/texlive/blobs/sha256:2fde6c0b50af2b1fda7ed0092ad1f1cc6897d7cb723dfcb0d2bc15201bbd7191 HTTP/1.1&#34;&#xA;&#xA;The HAProxy docs on stream states:&#xA;&#xA;     cD   The client did not send nor acknowledge any data for as long as the&#xA;          &#34;timeout client&#34; delay. This is often caused by network failures on&#xA;          the client side, or the client simply leaving the net uncleanly.&#xA;&#xA;First flag c:&#xA;  On the first character, a code reporting the first event which caused the&#xA;    stream to terminate :&#xA;&#xA;        c : the client-side timeout expired while waiting for the client to&#xA;            send or receive data.&#xA;&#xA;Second flag D:&#xA;  on the second character, the TCP or HTTP stream state when it was closed :&#xA;&#xA;        D : the stream was in the DATA phase.&#xA;&#xA;That was useful - &#34;the client-side timeout expired&#34;. It simply means that I need to bump the client timeouts (to 30m from 5s in this example) in the HAProxy frontend for my Docker registry.&#xA;&#xA;frontend registryfront&#xA;    bind                 :5000&#xA;    timeout              client 30m # was 5s&#xA;    timeout              client-fin 30m # was 30s&#xA;    mode                 http&#xA;    option               httplog&#xA;                         # display host header in logs&#xA;    capture              request header Host len 30&#xA;&#xA;    default_backend      registry&#xA;&#xA;The pull request through the proxy afterwards show no error flags (----):&#xA;Sep 02 02:30:27 haproxy haproxy[2850]: 10.0.0.102:53506 [02/Sep/2025:02:27:03.674] registryfront registry/pi3 0/0/0/15/203648 200 2334919898 - - ---- 7/1/0/0/0 0/0 {haproxy.lan:5000} &#34;GET /v2/texlive/blobs/sha256:2fde6c0b50af2b1fda7ed0092ad1f1cc6897d7cb723dfcb0d2bc15201bbd7191 HTTP/1.1&#34;&#xA;&#xA;Podman pull succeeds 🎉&#xA;&#xA;Ping me in chat or Fediverse if you have more suggestions regarding HAProxy configuration for private Docker registries. Happy self-hosting!&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>This is a story about pulling Docker images through HAProxy in my home lab.</p>

<p><a href="https://write.in0rdr.ch/tag:selfhosting" class="hashtag"><span>#</span><span class="p-category">selfhosting</span></a> <a href="https://write.in0rdr.ch/tag:homelab" class="hashtag"><span>#</span><span class="p-category">homelab</span></a> <a href="https://write.in0rdr.ch/tag:docker" class="hashtag"><span>#</span><span class="p-category">docker</span></a> <a href="https://write.in0rdr.ch/tag:haproxy" class="hashtag"><span>#</span><span class="p-category">haproxy</span></a>
</p>

<p>I observed an interesting issue in my Jenkins pipeline. The image pull aborted with the following error message:</p>

<pre><code>Error: writing blob: storing blob to file &#34;/var/tmp/storage1360560957/1&#34;: happened during read: unexpected EOF
</code></pre>

<p>First I thought it has something to do with the storage. But I was wrong. The culprit was the network.</p>

<p>More specifically, I noticed that pulling through my HAProxy instance was the issue, but pulling through the nodes registry port (directly) was fine.</p>

<p>When looking into the HAProxy logs, I noticed that the requests fail with the particular error flags <code>cD</code>:</p>

<pre><code>Sep 02 02:23:29 haproxy haproxy[2836]: 10.0.0.102:34982 [02/Sep/2025:02:22:47.563] registryfront registry/pi3 0/0/0/87/42156 200 466612119 - - cD-- 6/1/0/0/0 0/0 {haproxy.lan:5000} &#34;GET /v2/texlive/blobs/sha256:2fde6c0b50af2b1fda7ed0092ad1f1cc6897d7cb723dfcb0d2bc15201bbd7191 HTTP/1.1&#34;
</code></pre>

<p>The <a href="https://www.haproxy.com/documentation/haproxy-configuration-manual/latest/#8.5">HAProxy docs</a> on stream states:</p>

<pre><code>     cD   The client did not send nor acknowledge any data for as long as the
          &#34;timeout client&#34; delay. This is often caused by network failures on
          the client side, or the client simply leaving the net uncleanly.
</code></pre>

<p>First flag <code>c</code>:</p>

<pre><code>  - On the first character, a code reporting the first event which caused the
    stream to terminate :

        c : the client-side timeout expired while waiting for the client to
            send or receive data.
</code></pre>

<p>Second flag <code>D</code>:</p>

<pre><code>  - on the second character, the TCP or HTTP stream state when it was closed :

        D : the stream was in the DATA phase.
</code></pre>

<p>That was useful – “the client-side timeout expired”. It simply means that I need to bump the client timeouts (to 30m from 5s in this example) in the HAProxy frontend for my Docker registry.</p>

<pre><code>frontend registryfront
    bind                 :5000
    timeout              client 30m # was 5s
    timeout              client-fin 30m # was 30s
    mode                 http
    option               httplog
                         # display host header in logs
    capture              request header Host len 30

    default_backend      registry
</code></pre>

<p>The pull request through the proxy afterwards show no error flags (<code>----</code>):</p>

<pre><code>Sep 02 02:30:27 haproxy haproxy[2850]: 10.0.0.102:53506 [02/Sep/2025:02:27:03.674] registryfront registry/pi3 0/0/0/15/203648 200 2334919898 - - ---- 7/1/0/0/0 0/0 {haproxy.lan:5000} &#34;GET /v2/texlive/blobs/sha256:2fde6c0b50af2b1fda7ed0092ad1f1cc6897d7cb723dfcb0d2bc15201bbd7191 HTTP/1.1&#34;
</code></pre>

<p>Podman pull succeeds 🎉</p>

<p>Ping me in chat or Fediverse if you have more suggestions regarding HAProxy configuration for private Docker registries. Happy self-hosting!</p>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/docker-pull-through-haproxy</guid>
      <pubDate>Wed, 03 Sep 2025 14:17:02 +0000</pubDate>
    </item>
  </channel>
</rss>