<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>haproxy &amp;mdash; Jerry of the Week</title>
    <link>https://write.in0rdr.ch/tag:haproxy</link>
    <description>ˈdʒɛri - Individual who sends life against the grain no matter the consequences</description>
    <pubDate>Sat, 19 Sep 2026 10:19:21 +0000</pubDate>
    <item>
      <title>HAProxy ingress with Wireguard</title>
      <link>https://write.in0rdr.ch/haproxy-ingress-with-wireguard</link>
      <description>&lt;![CDATA[Recently I switched my homelab ingress from an implementation with autossh reverse proxy to Wireguard.&#xA;&#xA;#wireguard #haproxy #homelab&#xA;!--more--&#xA;&#xA;Up until today I had an SSH reverse proxy (autossh) running for routing traffic from the Internet to my applications in the homelab. That was a poor mans solution which worked fine for most of the time. See Slide 6.&#xA;&#xA;After reading Thibaults blog post A cheap VPS is a good front I decided to ditch the SSH reverse proxy in favor of a proper Wireguard connection.&#xA;&#xA;As in the previous implementation, the ingress network traffic flows through a jumphost in the cloud. I still use the Oracle cloud free tier, it works reliably for my purposes.&#xA;&#xA;To accept the network traffic on the jumphost, I installed a second HAProxy instance, which does simple &#34;tcp passthrough&#34; to the actual router (also a HAProxy, on LXC) in the internal network.&#xA;&#xA;The amd64 node on the internal network is the “Wireguard client peer”&#xA;The jumphost on Oracle Cloud is the “Wireguard server peer”&#xA;&#xA;haproxy-wireguard.jpg&#xA;&#xA;I have PersistentKeepalive setup with 25 seconds as suggested to keep the tunnel alive. Wireguard connection is established from the internal network to the jumphost, as was the case for the SSH reverse proxy.&#xA;&#xA;Jumphost HAProxy example config:&#xA;backend haproxylanhttp&#xA;    server               haproxy $wg0:80 check send-proxy-v2&#xA;backend haproxylanhttps&#xA;    server               haproxy $wg0:443 check send-proxy-v2&#xA;backend haproxylanssh&#xA;    server               haproxy $wg0:22 check send-proxy-v2&#xA;&#xA;LXC HAProxy example config (internal network):&#xA;frontend httpfront&#xA;    bind                 :80 accept-proxy&#xA;    ...&#xA;frontend httpsfront&#xA;    bind                 :443 accept-proxy&#xA;    ...&#xA;frontend sshfront&#xA;    bind                 :22 accept-proxy&#xA;&#xA;The proxy protocol allows me to see the real/original IP in the Anubis logs.&#xA;&#xA;Instead of connecting the jumphost with the internal network via SSH reverse proxy (autossh), I now route the ingress through the Wireguard overlay network. I create Wirguard keypairs for each relevant node to establish connections.&#xA;&#xA;I hope these kind of descriptions encourage you on your own self-hosting and homelab journeys 😁&#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>Recently I switched my homelab ingress from an implementation with autossh reverse proxy to Wireguard.</p>

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

<p>Up until today I had an SSH reverse proxy (autossh) running for routing traffic from the Internet to my applications in the homelab. That was a poor mans solution which worked fine for most of the time. See <a href="https://code.in0rdr.ch/pub/impacthub/sess2/techradar-2-homelab.pdf">Slide 6</a>.</p>

<p>After reading Thibaults blog post <a href="https://ergaster.org/thoughts/front-server">A cheap VPS is a good front</a> I decided to ditch the SSH reverse proxy in favor of a proper Wireguard connection.</p>

<p>As in the previous implementation, the ingress network traffic flows through a jumphost in the cloud. I still use the <a href="https://www.oracle.com/cloud/free">Oracle cloud free tier</a>, it works reliably for my purposes.</p>

<p>To accept the network traffic on the jumphost, I installed a second HAProxy instance, which does simple “tcp passthrough” to the actual router (also a HAProxy, on LXC) in the internal network.</p>
<ul><li>The amd64 node on the internal network is the “Wireguard client peer”</li>
<li>The jumphost on Oracle Cloud is the “Wireguard server peer”</li></ul>

<p><img src="https://code.in0rdr.ch/pub/blog/haproxy-wireguard.jpg" alt="haproxy-wireguard.jpg"></p>

<p>I have <a href="https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence">PersistentKeepalive</a> setup with 25 seconds as suggested to keep the tunnel alive. Wireguard connection is established from the internal network to the jumphost, as was the case for the SSH reverse proxy.</p>

<p>Jumphost HAProxy example config:</p>

<pre><code>backend haproxy_lan_http
    server               haproxy $wg0:80 check send-proxy-v2
backend haproxy_lan_https
    server               haproxy $wg0:443 check send-proxy-v2
backend haproxy_lan_ssh
    server               haproxy $wg0:22 check send-proxy-v2
</code></pre>

<p>LXC HAProxy example config (internal network):</p>

<pre><code>frontend httpfront
    bind                 :80 accept-proxy
    ...
frontend httpsfront
    bind                 :443 accept-proxy
    ...
frontend sshfront
    bind                 :22 accept-proxy
</code></pre>

<p>The <a href="https://www.haproxy.com/documentation/haproxy-configuration-tutorials/proxying-essentials/client-ip-preservation/enable-proxy-protocol/">proxy protocol</a> allows me to see the <a href="https://anubis.techaro.lol/docs/admin/caveats-xff">real/original IP</a> in the Anubis logs.</p>

<p>Instead of connecting the jumphost with the internal network via SSH reverse proxy (autossh), I now route the ingress through the Wireguard overlay network. I create Wirguard keypairs for each relevant node to establish connections.</p>

<p>I hope these kind of descriptions encourage you on your own self-hosting and homelab journeys 😁</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/haproxy-ingress-with-wireguard</guid>
      <pubDate>Fri, 18 Sep 2026 04:57:48 +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>