<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on Luis Sousa Blog</title>
    <link>https://89393b0c.nuvai-blog.pages.dev/posts/</link>
    <description>Recent content in Posts on Luis Sousa Blog</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Sun, 08 Mar 2026 10:00:00 +0000</lastBuildDate>
    <atom:link href="https://89393b0c.nuvai-blog.pages.dev/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Fixing &#39;Robots.txt Unreachable&#39; in Google Search Console with Traefik and Docker</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/fixing-robots-txt-unreachable-traefik-docker/</link>
      <pubDate>Sun, 08 Mar 2026 10:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/fixing-robots-txt-unreachable-traefik-docker/</guid>
      <description>&lt;p&gt;We run a travel blog (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) on Docker Compose with Traefik v3 and Cloudflare. One morning Google Search Console showed every page blocked from indexing with &amp;ldquo;Failed: Robots.txt unreachable&amp;rdquo;. The site was working fine in a browser, so what was going on?&lt;/p&gt;&#xA;&lt;p&gt;Two separate issues were conspiring to break Googlebot&amp;rsquo;s ability to fetch &lt;code&gt;/robots.txt&lt;/code&gt;. Here&amp;rsquo;s what we found and how we fixed both.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-setup&#34;&gt;&#xA;  The setup&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-setup&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Our WordPress service sits on two Docker networks: &lt;code&gt;app-network&lt;/code&gt; (shared with Traefik, Redis, imgproxy) and &lt;code&gt;db-network&lt;/code&gt; (shared with MariaDB). We run two scaled WordPress containers behind Traefik&amp;rsquo;s load balancer.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Why Datadog Apache Metrics Showed 404 (And When to Disable the Check)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/datadog-apache-404-wordpress-docker/</link>
      <pubDate>Sat, 07 Mar 2026 12:12:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/datadog-apache-404-wordpress-docker/</guid>
      <description>&lt;p&gt;We run &lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; with Datadog for observability. After setting up the Datadog Agent, we noticed the Apache integration was logging 404 errors every 15 seconds. Here&amp;rsquo;s why—and two ways to fix it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem&#34;&gt;&#xA;  The problem&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-problem&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Datadog&amp;rsquo;s Apache integration polls &lt;code&gt;http://localhost/server-status?auto&lt;/code&gt; to scrape metrics (requests per second, busy workers, etc.). The official &lt;code&gt;wordpress:latest&lt;/code&gt; Docker image does not enable Apache&amp;rsquo;s &lt;code&gt;mod_status&lt;/code&gt; module. So the endpoint doesn&amp;rsquo;t exist, and every poll returns 404.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Protecting EDD Downloads from URL Guessing</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/protecting-edd-downloads-from-url-guessing/</link>
      <pubDate>Sat, 07 Mar 2026 12:10:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/protecting-edd-downloads-from-url-guessing/</guid>
      <description>&lt;p&gt;We sell digital products on &lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; using Easy Digital Downloads. By default, EDD stores files in &lt;code&gt;wp-content/uploads/&lt;/code&gt;—the same directory as every other WordPress upload. That means anyone who can guess the filename can download the file directly, bypassing purchase validation entirely.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem&#34;&gt;&#xA;  The problem&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-problem&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;EDD download URLs contain a token that validates the purchase. But the actual file sits in a publicly accessible directory. If someone guesses or discovers the file path (e.g. from a cached CDN URL or a predictable naming pattern), they can download it without paying.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Automated WordPress Backups to Hetzner with Docker (Zero Plugins)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/wordpress-docker-automated-backups-hetzner/</link>
      <pubDate>Sat, 07 Mar 2026 12:08:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/wordpress-docker-automated-backups-hetzner/</guid>
      <description>&lt;p&gt;We run a travel blog at &lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; on a single VPS with Docker Compose. Managed backup plugins add overhead, cost money, and only back up what WordPress can see. We wanted to back up everything—volumes, configs, database—automatically, with offsite copies. Here&amp;rsquo;s how we did it with zero WordPress plugins.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-backup-service&#34;&gt;&#xA;  The backup service&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-backup-service&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;We use &lt;a href=&#34;https://github.com/offen/docker-volume-backup&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;offen/docker-volume-backup&lt;/a&gt; as a sidecar container in our Docker Compose stack. It runs on a cron schedule, tars up the mounted volumes, and ships the archive to a remote Hetzner Storage Box via SFTP.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Self-hosted image optimization for WordPress with imgproxy (no premium plugins)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/self-hosted-wordpress-imgproxy/</link>
      <pubDate>Sat, 07 Mar 2026 12:06:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/self-hosted-wordpress-imgproxy/</guid>
      <description>&lt;p&gt;We run a travel blog (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) on a budget VPS with Docker Compose and free Cloudflare. Premium image plugins like ShortPixel charge for resize and optimize—we wanted dynamic images without recurring costs. Here’s how we did it with imgproxy.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-imgproxy&#34;&gt;&#xA;  Why imgproxy&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#why-imgproxy&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;imgproxy runs in Docker, supports signed URLs and allowlisted sources, and outputs WebP/AVIF. We use the official image &lt;code&gt;ghcr.io/imgproxy/imgproxy&lt;/code&gt; and pin a version tag for stable deploys. No post edits needed: we rewrite image URLs at runtime via a WordPress MU-plugin so existing content and our Bricksy Pro FSE output stay unchanged.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Running WordPress Cron the Right Way When Your Site Lives in Docker</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/running-wordpress-cron-right-way-docker/</link>
      <pubDate>Sat, 07 Mar 2026 12:05:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/running-wordpress-cron-right-way-docker/</guid>
      <description>&lt;p&gt;We host a travel blog (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) on a low-cost VPS with Docker Compose and Cloudflare in front. WordPress’s built-in cron was causing intermittent timeouts in our logs. Here’s how we fixed it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem-with-wordpress-cron-in-docker&#34;&gt;&#xA;  The problem with WordPress cron in Docker&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-problem-with-wordpress-cron-in-docker&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;WordPress cron normally “pings itself” on each page load—an HTTP POST to the site from inside the container. Behind a CDN and in Docker, that ping can time out, fail, or hit the wrong instance. We saw cURL timeouts in Datadog and unreliable scheduled tasks.&lt;/p&gt;</description>
    </item>
    <item>
      <title>When &#34;There Has Been an Error Cropping Your Image&#34; Isn&#39;t About the Image</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/when-image-crop-error-isnt-about-the-image/</link>
      <pubDate>Sat, 07 Mar 2026 12:04:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/when-image-crop-error-isnt-about-the-image/</guid>
      <description>&lt;p&gt;We run a travel blog on a budget stack (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) with Docker, Cloudflare, and imgproxy for images. When we hit “There has been an error cropping your image” in the WordPress media editor, the message pointed at the image—but the real cause was elsewhere.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-misleading-error&#34;&gt;&#xA;  The misleading error&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-misleading-error&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The error suggests missing image libraries (GD or Imagick). We confirmed both were installed and working. The actual problem: the crop request to &lt;code&gt;admin-ajax.php&lt;/code&gt; was returning &lt;strong&gt;403 Forbidden&lt;/strong&gt;. That can come from a security plugin, WAF, or nonce validation—not from the image itself.&lt;/p&gt;</description>
    </item>
    <item>
      <title>SEO Housekeeping: Focus Keywords and Sitemaps That Match</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/seo-housekeeping-focus-keywords-sitemaps/</link>
      <pubDate>Sat, 07 Mar 2026 12:03:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/seo-housekeeping-focus-keywords-sitemaps/</guid>
      <description>&lt;p&gt;We run a travel blog on a budget VPS (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) that helps travellers plan itineraries. Keeping SEO clean without premium plugins is part of the game. Here’s how we fixed mixed signals between focus keywords, noindex, and sitemaps using Rank Math’s free tier.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-focus-keywords-matter&#34;&gt;&#xA;  Why focus keywords matter&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#why-focus-keywords-matter&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Each post should have one clear focus phrase—the main search term you want it to rank for. Rank Math stores this in &lt;code&gt;rank_math_focus_keyword&lt;/code&gt; post meta. We filled gaps in bulk using WP-CLI, deriving keywords from post titles and search intent (e.g. “cherry blossoms in Paris”, “5 day itinerary Budapest”). Skip utility pages like checkout, receipt, and privacy—they don’t need focus keywords.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Fixing header and footer overlap on WordPress checkout pages (EDD &#43; block theme)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/fixing-edd-checkout-header-footer-overlap/</link>
      <pubDate>Sat, 07 Mar 2026 12:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/fixing-edd-checkout-header-footer-overlap/</guid>
      <description>&lt;p&gt;We sell downloadable travel itineraries on &lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; using Easy Digital Downloads (EDD). With a sticky header and a block theme (Bricksy Pro), the checkout and receipt pages had content sitting under the header and footer. Here&amp;rsquo;s how we fixed it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem&#34;&gt;&#xA;  The problem&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-problem&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;With a sticky or fixed header, the main content area starts at the top of the viewport. The header overlays it, so the first lines of the checkout form are hidden. Similarly, a fixed footer can cover the bottom of the page. FSE block themes don&amp;rsquo;t always add padding for fixed elements on custom post types like EDD checkout.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How We Sped Up Our Travel Blog (Without Changing Hosts)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/how-we-sped-up-our-travel-blog/</link>
      <pubDate>Sat, 07 Mar 2026 12:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/how-we-sped-up-our-travel-blog/</guid>
      <description>&lt;p&gt;We run a travel blog at &lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; that helps travellers plan itineraries—hosted on a low-cost VPS with Docker Compose and Cloudflare’s free tier. Here are four changes that improved speed without changing hosts or paying for premium plugins.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-give-the-database-more-memory&#34;&gt;&#xA;  1. Give the database more memory&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#1-give-the-database-more-memory&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;On a 24 GB VPS, MariaDB’s default InnoDB buffer pool (~128 MB) was too small. We increased it to 2 GB and set a 4 GB memory limit on the DB container. Redis’s &lt;code&gt;maxmemory&lt;/code&gt; was raised from 1 GB to 2 GB.&lt;/p&gt;</description>
    </item>
    <item>
      <title>How We Speeded Up Our Travel Blog (Without Changing Hosts)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/how-we-speeded-up-our-travel-blog/</link>
      <pubDate>Sat, 07 Mar 2026 12:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/how-we-speeded-up-our-travel-blog/</guid>
      <description>&lt;p&gt;We run a travel blog at &lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; that helps travellers plan itineraries—hosted on a low-cost VPS with Docker Compose and Cloudflare’s free tier. Here are four changes that improved speed without changing hosts or paying for premium plugins.&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-give-the-database-more-memory&#34;&gt;&#xA;  1. Give the database more memory&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#1-give-the-database-more-memory&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;On a 24 GB VPS, MariaDB’s default InnoDB buffer pool (~128 MB) was too small. We increased it to 2 GB and set a 4 GB memory limit on the DB container. Redis’s &lt;code&gt;maxmemory&lt;/code&gt; was raised from 1 GB to 2 GB.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Managing Cloudflare www-to-apex redirects with Terraform for WordPress SEO</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/cloudflare-www-to-apex-redirects-terraform/</link>
      <pubDate>Sat, 07 Mar 2026 12:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/cloudflare-www-to-apex-redirects-terraform/</guid>
      <description>&lt;p&gt;For SEO, &lt;code&gt;www.example.com&lt;/code&gt; should 301 redirect to &lt;code&gt;example.com&lt;/code&gt; so search engines see one canonical host. We manage our travel blog&amp;rsquo;s infrastructure with Terraform, including Cloudflare. When we tried to add the www-to-apex redirect, we hit &amp;ldquo;exceeded maximum number of zone rulesets.&amp;rdquo; Here&amp;rsquo;s how we fixed it and manage redirects as code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-terraform-for-cloudflare&#34;&gt;&#xA;  Why Terraform for Cloudflare&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#why-terraform-for-cloudflare&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Terraform gives you versioned, repeatable config. No manual dashboard drift, no &amp;ldquo;who changed what&amp;rdquo; surprises. Redirects live in &lt;code&gt;main.tf&lt;/code&gt; and are applied consistently.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Rank Math sitemap not loading with Traefik (and how to fix it)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/rank-math-sitemap-not-loading-traefik/</link>
      <pubDate>Sat, 07 Mar 2026 12:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/rank-math-sitemap-not-loading-traefik/</guid>
      <description>&lt;p&gt;We run a travel blog (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) on a budget VPS with Docker Compose, Cloudflare, and Traefik. When we added Rank Math for SEO, the sitemap worked in the admin but returned 404 on the public URL. Here&amp;rsquo;s what was going on and how we fixed it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem&#34;&gt;&#xA;  The problem&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-problem&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Rank Math generates sitemaps at runtime via &lt;code&gt;index.php?sitemap=1&lt;/code&gt;. On Apache or Nginx with &lt;code&gt;.htaccess&lt;/code&gt;/rewrite rules, the pretty URL &lt;code&gt;/sitemap_index.xml&lt;/code&gt; gets routed to WordPress automatically. With Traefik, that routing doesn&amp;rsquo;t exist by default—Traefik doesn&amp;rsquo;t use &lt;code&gt;.htaccess&lt;/code&gt;, so &lt;code&gt;/sitemap_index.xml&lt;/code&gt; never reaches WordPress and you get a 404.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Why Our Site Went Down for an Hour (And What We Fixed)</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/why-our-site-went-down-for-an-hour/</link>
      <pubDate>Sat, 07 Mar 2026 10:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/why-our-site-went-down-for-an-hour/</guid>
      <description>&lt;p&gt;We run a travel blog (&lt;a href=&#34;https://joyofexploringtheworld.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt;) that helps people plan itineraries—hosted on a low-cost VPS with Docker Compose and Cloudflare. One small performance tweak took the whole site offline for an hour. Here&amp;rsquo;s what happened and how we fixed it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-went-wrong&#34;&gt;&#xA;  What Went Wrong&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#what-went-wrong&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;We added a Traefik compression middleware to speed up responses. The new config lived in a &lt;strong&gt;separate YAML file&lt;/strong&gt;. Traefik was configured to load only a &lt;strong&gt;single file&lt;/strong&gt; (e.g. &lt;code&gt;real-ip.yaml&lt;/code&gt;), not the whole directory. The router referenced &lt;code&gt;compress@file&lt;/code&gt;, but that file was never loaded. Every request failed.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Running a WordPress Travel Blog on a Budget VPS: The Full Docker Stack</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/wordpress-docker-compose-production-stack/</link>
      <pubDate>Sat, 07 Mar 2026 09:00:00 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/wordpress-docker-compose-production-stack/</guid>
      <description>&lt;p&gt;We run &lt;a href=&#34;https://joyofexploringtheworld.com&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;joyofexploringtheworld.com&lt;/a&gt; — a travel blog — on a single 24 GB VPS using Docker Compose and free-tier Cloudflare. No managed WordPress hosting, no premium plugins, no surprise invoices. Here is the full stack, laid out so you can steal whatever is useful.&lt;/p&gt;&#xA;&lt;h2 id=&#34;architecture-overview&#34;&gt;&#xA;  Architecture overview&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#architecture-overview&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The request path looks like this:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Internet&#xA;  -&amp;gt; Cloudflare (CDN / APO / edge SSL)&#xA;    -&amp;gt; Traefik v3 (reverse proxy, TLS termination for origin)&#xA;      -&amp;gt; 2 WordPress containers (round-robin with sticky session cookies)&#xA;        -&amp;gt; MariaDB 11 (single-writer database)&#xA;        -&amp;gt; Redis (persistent object cache)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alongside the main request path we run a handful of supporting services: &lt;strong&gt;imgproxy&lt;/strong&gt; on a dedicated subdomain for on-the-fly image resizing and format conversion, a &lt;strong&gt;wp-cron sidecar&lt;/strong&gt; that hits &lt;code&gt;wp-cron.php&lt;/code&gt; every five minutes so we can disable the default front-end cron, an &lt;strong&gt;automated backup&lt;/strong&gt; container that dumps the database and syncs uploads to Hetzner Storage Box nightly, and the &lt;strong&gt;Datadog agent&lt;/strong&gt; for logs, traces, and container metrics.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Hello World</title>
      <link>https://89393b0c.nuvai-blog.pages.dev/posts/hello-world/</link>
      <pubDate>Wed, 01 Jun 2022 07:04:55 +0000</pubDate>
      <guid>https://89393b0c.nuvai-blog.pages.dev/posts/hello-world/</guid>
      <description>&lt;p&gt;First Blog post followed by comments.&lt;/p&gt;&#xA;&lt;p&gt;#TODO write-up&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;hugo setup&lt;/li&gt;&#xA;&lt;li&gt;Cloudflare deployment with github shenanigans&lt;/li&gt;&#xA;&lt;li&gt;Disqus - submodule chenanigans &lt;a href=&#34;https://github.com/adityatelange/hugo-PaperMod/discussions/508&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://github.com/adityatelange/hugo-PaperMod/discussions/508&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;adobe free logo maker&lt;/li&gt;&#xA;&lt;li&gt;favicon generator&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;#TODO - homelab wishlist&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/jareware/docker-volume-backupxs&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://github.com/jareware/docker-volume-backupxs&lt;/a&gt;&#xA;&amp;ndash; db backups &lt;a href=&#34;https://simplebackups.com/blog/docker-postgres-backup-restore-guide-with-examples/#back-up-a-docker-postgresql-database&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://simplebackups.com/blog/docker-postgres-backup-restore-guide-with-examples/#back-up-a-docker-postgresql-database&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;email delivery: &lt;a href=&#34;https://www.oracle.com/cloud/free/#always-free&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://www.oracle.com/cloud/free/#always-free&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Content Management Starter Edition: 5000 assets per month - oracle&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
    </item>
  </channel>
</rss>
