<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Homelab - Tag - arleo.eu</title><link>https://www.arleo.eu/en/tags/homelab/</link><description>Homelab - Tag - arleo.eu</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 29 May 2026 20:30:00 +0200</lastBuildDate><atom:link href="https://www.arleo.eu/en/tags/homelab/" rel="self" type="application/rss+xml"/><item><title>Hugo SEO: Googlebot 404s, noindex aliases and sitemap normalization</title><link>https://www.arleo.eu/en/posts/debug-seo-404-broken-links/</link><pubDate>Fri, 29 May 2026 20:30:00 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/debug-seo-404-broken-links/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/debug-seo-404-broken-links-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="context">Context</h2>
<p>Google Search Console was reporting four categories of issues on arleo.eu:</p>
<ul>
<li><strong>Googlebot 404s</strong>: <code>/fr/tag/cloudflare</code>, <code>/en/tag/nginx</code>, <code>/fr/tag/javascript</code>… URLs with <code>/fr/</code> prefix or singular <code>/tag/</code> never served by nginx</li>
<li><strong>16 &ldquo;Excluded by noindex tag&rdquo; pages</strong>: all redirect pages generated by <code>aliases:</code> in Hugo frontmatter</li>
<li><strong>Robots tag</strong>: <code>noodp</code> hardcoded in the LoveIt theme</li>
<li><strong>FR/EN sitemap</strong>: 104 vs 105 URLs — a duplicate FR tag and two missing tags</li>
</ul>
<hr>
<h2 id="act-1-hugo-aliases--nginx-301-redirects">Act 1: Hugo aliases → nginx 301 redirects</h2>
<h3 id="why-hugo-generates-noindex-pages">Why Hugo generates noindex pages</h3>
<p>Hugo generates <code>aliases:</code> frontmatter entries as static HTML files:</p>]]></description></item><item><title>CSP A+ on Hugo + Cloudflare: from hash-based to origin allowlist, auto-monitoring and hardening</title><link>https://www.arleo.eu/en/posts/csp-a-plus-hugo-cloudflare-origin-allowlist/</link><pubDate>Thu, 28 May 2026 23:45:41 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/csp-a-plus-hugo-cloudflare-origin-allowlist/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/csp-a-plus-hugo-cloudflare-origin-allowlist-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="context">Context</h2>
<p>arleo.eu runs on Hugo (KVM VM) → OpenResty (NUC) → Cloudflare (CDN/WAF). Goal: <strong>A+ score on Mozilla Observatory</strong> with a strict CSP that resists edge-side injections.</p>
<p>The journey went through three strategies over a few weeks — nonces, hashes, then origin allowlist — before landing on a stable, automated solution.</p>
<hr>
<h2 id="act-1-why-hash-based-failed">Act 1: why hash-based failed</h2>
<p>The initial idea seemed solid: Hugo Pipes externalizes all JS with SRI, we list the hashes in the CSP, clean result. In practice, two problems made the approach impossible.</p>]]></description></item><item><title>CrowdSec AppSec + OpenResty: Modern WAF Without ModSecurity</title><link>https://www.arleo.eu/en/posts/crowdsec-appsec-openresty/</link><pubDate>Mon, 18 May 2026 00:07:55 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/crowdsec-appsec-openresty/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/crowdsec-appsec-openresty-featured.jpg" referrerpolicy="no-referrer">
            </div><p>After years running ModSecurity + OWASP CRS on nginx, I migrated arleo.eu to a more modern stack: <strong>CrowdSec AppSec on OpenResty</strong>. The result is a tighter inline WAF architecture — better integrated, easier to maintain, and fully coherent with the rest of the security stack.</p>
<h2 id="why-drop-modsecurity">Why Drop ModSecurity?</h2>
<p>ModSecurity v2 is in maintenance mode. Managing OWASP CRS rules on classic nginx generates friction: frequent false positives, logs that are hard to correlate with CrowdSec, and a configuration spread across multiple tools with no unified view.</p>]]></description></item><item><title>SRI on Hugo: automated hashes, auto-update and BetterStack alerting</title><link>https://www.arleo.eu/en/posts/sri-cdn-hugo-automate/</link><pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/sri-cdn-hugo-automate/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/sri-cdn-hugo-automate-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="why-sri">Why SRI?</h2>
<p>When your site loads resources from a third-party CDN — FontAwesome, Mermaid, Animate.css — you&rsquo;re trusting an external party you have no control over. If jsdelivr.net gets compromised, or if a supposedly immutable version is silently mutated, your site can become an attack vector.</p>
<p><strong>Subresource Integrity</strong> (SRI) solves this cleanly: every <code>&lt;link&gt;</code> or <code>&lt;script&gt;</code> tag carries an <code>integrity=&quot;sha256-…&quot;</code> attribute that the browser verifies before executing the resource. If the hash doesn&rsquo;t match, the browser blocks the load.</p>]]></description></item><item><title>NUC Security Audit: ModSecurity Removed, 500 MB Recovered</title><link>https://www.arleo.eu/en/posts/audit-securite-modsecurity-crowdsec/</link><pubDate>Thu, 14 May 2026 05:32:19 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/audit-securite-modsecurity-crowdsec/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/audit-securite-modsecurity-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-tldr">⚡ TL;DR</h2>
<p>A security stack audit on the homelab NUC reveals <strong>redundant double WAF inspection</strong>: ModSecurity + OWASP CRS load 11,872 rules into memory despite <code>SecRuleEngine Off</code>, running in parallel with CrowdSec AppSec which already covers the same surface. After removing the ModSecurity nginx module and five other targeted fixes, nginx drops from <strong>~520 MB to ~27 MB PSS</strong>. Same security, memory footprint divided by 20.</p>
<hr>
<h2 id="-architecture-before-the-audit">🏗️ Architecture Before the Audit</h2>
<p>The security stack had six stacked layers:</p>]]></description></item><item><title>Post-mortem: 3 MCP timeouts — IPAddressDeny + Cloudflare + NFS</title><link>https://www.arleo.eu/en/posts/postmortem-mcp-timeouts-cloudflare/</link><pubDate>Sat, 09 May 2026 13:04:20 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/postmortem-mcp-timeouts-cloudflare/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/postmortem-mcp-timeouts-cloudflare-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="context">Context</h2>
<p>I deployed a Hugo MCP Server (FastAPI, 7 tools) that lets me edit arleo.eu from Claude.ai. Architecture: <code>claude.ai → mcp-oauth-proxy NUC → hugo-mcp-proxy NUC → MCP server VM</code>.</p>]]></description></item><item><title>Strategy 4: separating content (MCP) from structure (Git)</title><link>https://www.arleo.eu/en/posts/strategie-4-mcp-vs-git/</link><pubDate>Sat, 09 May 2026 13:03:25 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/strategie-4-mcp-vs-git/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/strategie-4-mcp-vs-git-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="the-problem">The problem</h2>
<p>You have a Hugo site. You want to:</p>
<ol>
<li><strong>Edit content via Claude.ai</strong> (publish an article, fix a typo, update a draft) without touching an SSH terminal.</li>
<li><strong>Version the structure</strong> (layouts, themes, hugo.toml, deploy scripts) in Git, like a serious dev.</li>
</ol>
<p>First instinct: &ldquo;everything in Git&rdquo;. Articles too. The MCP commits, pushes, GitHub webhook triggers a rebuild. Clean, GitOps-philosophy.</p>
<p>Except it doesn&rsquo;t work that well. Here&rsquo;s why, and the simple solution I call <strong>Strategy 4</strong>.</p>
<h2 id="why-everything-in-git-breaks-in-practice">Why &ldquo;everything in Git&rdquo; breaks in practice</h2>
<p>Imagine your MCP <code>git commit</code>s on every <code>create_page</code>. Naive strategy, often suggested. Here are the problems:</p>
<h3 id="problem-1--mcp--git-conflict">Problem 1 — MCP ↔ Git conflict</h3>
<p>You push a new layout from your laptop (<code>layouts/index.html</code> modified). At the same time, the MCP is committing a new article version. Race condition: the MCP might <code>git pull --rebase</code> and fail, or worse, overwrite your local commit.</p>
<h3 id="problem-2--mcps-git-identity">Problem 2 — MCP&rsquo;s Git identity</h3>
<p>Whose commits is the MCP making? With which GPG key? If you have a &ldquo;signed commits required&rdquo; policy, the MCP needs to manage a GPG key, which has to be secured, rotated, etc.</p>
<h3 id="problem-3--unwanted-auto-commits">Problem 3 — Unwanted auto-commits</h3>
<p>You&rsquo;re testing, you create a draft article to experiment, you delete it. But the MCP already committed. Now you have a &ldquo;wip test&rdquo; commit in history, to rebase or squash manually.</p>
<h3 id="problem-4--asymmetric-reversibility">Problem 4 — Asymmetric reversibility</h3>
<p>A <code>git revert</code> repo-side has no effect on files the MCP already created. You end up with a desynchronized repo and filesystem state.</p>
<h2 id="strategy-4-separate-the-zones">Strategy 4: separate the zones</h2>
<p>The idea: <strong>MCP and Git never write to the same files</strong>.</p>
<table>
  <thead>
      <tr>
          <th>Zone</th>
          <th>Who edits</th>
          <th>Versioned in Git?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>content/**/*.md</code></td>
          <td><strong>MCP only</strong></td>
          <td>❌ NO (<code>.gitignore</code>)</td>
      </tr>
      <tr>
          <td><code>layouts/</code>, <code>themes/</code>, <code>static/</code>, <code>hugo.toml</code>, <code>deploy.sh</code></td>
          <td><strong>Git push only</strong></td>
          <td>✅ YES</td>
      </tr>
  </tbody>
</table>
<p>Repo-side <code>.gitignore</code>:</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>content/
public/
resources/</code></pre></div>
<p>Implications:</p>
<ul>
<li>The MCP can write to <code>content/</code> whenever. No Git conflict possible.</li>
<li>You can <code>git reset --hard</code> repo-side with confidence — <code>content/</code> stays intact.</li>
<li>No need for the MCP to manage a Git identity.</li>
<li>No &ldquo;commit pollution&rdquo;.</li>
</ul>
<h2 id="trade-off-no-content-versioning">Trade-off: no content versioning</h2>
<p>You lose Git versioning of content. That&rsquo;s a real loss:</p>
<ul>
<li>No <code>git blame</code> on an article to see who wrote what.</li>
<li>No <code>git log content/csp-nonce/index.fr.md</code> for history.</li>
<li>No PR review for articles.</li>
</ul>
<p><strong>Mitigation</strong>: VM snapshots + daily encrypted <code>content/</code> backup to QNAP. That covers <strong>disaster recovery</strong>, but not fine-grained versioning (who-changed-what-when).</p>
<p>For my personal homelab (single author, technical articles, no editorial validation workflow), it&rsquo;s an acceptable trade-off. For a 10-contributor team blog, I&rsquo;d reconsider.</p>
<h2 id="final-architecture">Final architecture</h2>]]></description></item><item><title>Hugo MCP Server: Connecting Claude.ai to a Static Hugo Site</title><link>https://www.arleo.eu/en/posts/hugo-mcp-server/</link><pubDate>Sun, 03 May 2026 19:00:00 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/hugo-mcp-server/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/hugo-mcp-server-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-in-short">⚡ In short</h2>
<p>Connect Claude.ai to a <strong>Hugo</strong> site hosted in a KVM VM in 30 minutes: a FastAPI server exposes 6 MCP tools (read, create, modify, delete pages, rebuild the site) via JSON-RPC 2.0, an OAuth proxy reuses existing infrastructure, and every modification automatically triggers a Hugo rebuild + Cloudflare cache purge.</p>
<p>The code is available on GitHub:</p>
<ul>
<li>🔌 Hugo MCP Server: <a href="https://github.com/jmrGrav/hugo-mcp" target="_blank" rel="noopener noreffer ">jmrGrav/hugo-mcp</a></li>
<li>🔐 OAuth Proxy: <a href="https://github.com/jmrGrav/mcp-oauth-proxy" target="_blank" rel="noopener noreffer ">jmrGrav/mcp-oauth-proxy</a></li>
</ul>
<h2 id="-why">🧠 Why</h2>
<p>Anthropic&rsquo;s <a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreffer ">MCP (Model Context Protocol)</a> allows Claude.ai to connect to external data sources via standardized tools. Unlike Grav CMS which is dynamic (PHP), Hugo generates pure static HTML — making content management via MCP even more powerful: every modification is compiled and deployed instantly.</p>]]></description></item><item><title>KVM Media VM: migrating Sonarr, Radarr and SABnzbd into an isolated VM</title><link>https://www.arleo.eu/en/posts/media-vm-migration/</link><pubDate>Sun, 03 May 2026 18:00:00 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/media-vm-migration/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/media-vm-migration-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-tldr">⚡ TL;DR</h2>
<p>Migrating the media stack (Sonarr, Radarr, SABnzbd) into a dedicated KVM VM running Ubuntu 24.04: security isolation, easy snapshots, QNAP NFS mounted inside the VM, nginx reverse proxy on the host. The migration fully preserves SQLite databases and existing configuration.</p>
<p><strong>Target stack:</strong></p>
<ul>
<li>🖥️ <strong>Host</strong>: NUC8i3BEH, Ubuntu Server, nginx reverse proxy, Plex (GPU transcoding)</li>
<li>📦 <strong>VM media-vm</strong>: Ubuntu 24.04, 2 vCPU, 8 GB RAM, 120 GB (X5 NVMe), QNAP NFS</li>
<li>🎬 <strong>Migrated services</strong>: Sonarr (port 8989), Radarr (port 7878), SABnzbd (port 6789)</li>
</ul>
<h2 id="-why-isolate-the-media-stack-in-a-vm">🧠 Why isolate the media stack in a VM</h2>
<p>Sonarr, Radarr and SABnzbd present a significant attack surface: network calls to external indexers, post-download script execution, filesystem access to the media library. Confining them in a VM provides:</p>]]></description></item><item><title>Hugo on KVM: Installing an Ubuntu VM for a Static Site</title><link>https://www.arleo.eu/en/posts/hugo-vm-installation/</link><pubDate>Sun, 03 May 2026 09:00:00 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/posts/hugo-vm-installation/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/hugo-vm-installation-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-in-short">⚡ In short</h2>
<p>Progressive migration from <strong>Grav CMS</strong> to <strong>Hugo</strong> — a static site generator. The goal is to isolate Hugo in a dedicated KVM VM on the NUC8i3BEH, with nginx on the host as a reverse proxy. The generated static site is served by nginx inside the VM — no PHP, no database, no application attack surface.</p>
<ul>
<li>🖥️ <strong>Host</strong>: NUC8i3BEH Ubuntu 24.04 — nginx proxy + KVM</li>
<li>🗄️ <strong>VM disk</strong>: Samsung X5 external NVMe (exFAT → ext4 loop image)</li>
<li>🌐 <strong>Access</strong>: <a href="https://hugo-test.arleo.eu" target="_blank" rel="noopener noreffer ">hugo-test.arleo.eu</a></li>
<li>🎨 <strong>Theme</strong>: <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="noopener noreffer ">LoveIt</a></li>
</ul>
<h2 id="-why">🧠 Why</h2>
<p>Grav CMS is excellent but relies on PHP — a non-negligible attack surface. Hugo generates pure static HTML: no PHP, no database, no application vulnerability. Performance is also radically better — HTML is served directly by nginx without dynamic processing.</p>]]></description></item></channel></rss>