<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mustafa ERBAY</title>
    <description>The latest articles on Forem by Mustafa ERBAY (@merbayerp).</description>
    <link>https://forem.com/merbayerp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3921203%2Fe3a198a1-49a0-466f-99e6-74bdf202a867.png</url>
      <title>Forem: Mustafa ERBAY</title>
      <link>https://forem.com/merbayerp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/merbayerp"/>
    <language>en</language>
    <item>
      <title>Living on My Own Server: The Balance of Time and Freedom</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sun, 10 May 2026 13:05:24 +0000</pubDate>
      <link>https://forem.com/merbayerp/living-on-my-own-server-the-balance-of-time-and-freedom-15j1</link>
      <guid>https://forem.com/merbayerp/living-on-my-own-server-the-balance-of-time-and-freedom-15j1</guid>
      <description>&lt;p&gt;Recently, I woke up to three different Docker containers being OOM-killed consecutively on my own VPS. PagerDuty rang at 4:30 AM, and my first reaction was, "Again?" This was a typical morning start in what I call &lt;strong&gt;living on my own server&lt;/strong&gt;, where I manage all my personal projects and side products.&lt;/p&gt;

&lt;p&gt;This situation is a concrete example of a long-standing quest for balance: the time and effort I invest in exchange for the freedom and control I possess. Living on my own server has become a choice, a philosophy for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Price of Freedom: Time and Effort
&lt;/h2&gt;

&lt;p&gt;I have a long-standing habit: setting up and managing everything with my own hands. This is essentially a reflection of the experience I've gained in system architecture and operations since 2006, applied to my personal life. It holds true for this blog and projects like &lt;code&gt;hesapciyiz.com&lt;/code&gt;, &lt;code&gt;spamkalkani.com&lt;/code&gt;, and &lt;code&gt;islistesi.com&lt;/code&gt;, all hosted on my own server.&lt;/p&gt;

&lt;p&gt;This freedom, of course, comes at a price. Sometimes, as on April 28th, when the disk fills up to 100% or I see &lt;code&gt;kcompactd&lt;/code&gt; using 92% CPU, I have to spend hours at the terminal with a coffee in hand. Situations like an &lt;code&gt;sshd&lt;/code&gt; failing to &lt;code&gt;accept&lt;/code&gt; connections, a Docker disk fire, or blacklisting kernel modules like &lt;code&gt;algif_aead&lt;/code&gt; to mitigate CVEs are among the responsibilities this freedom brings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ℹ️ A Real-World Scenario&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One day, I accidentally ran &lt;code&gt;sleep 360&lt;/code&gt; in the background on my own VPS and realized it got OOM-killed. Even this simple mistake can affect the entire system. Such situations constantly remind me to consider more robust approaches like "polling-wait." To err is human; the important thing is to learn from mistakes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Do I Live on My Own Server? My Philosophy
&lt;/h2&gt;

&lt;p&gt;So, despite all these problems and sleepless nights, why do I choose this path? There are a few fundamental philosophical reasons:&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Control and Independence
&lt;/h3&gt;

&lt;p&gt;On my own server, everything is my choice. From the operating system to the web server, from the database to the application layer, I choose every component. Astro, Node, SQLite, Nginx, systemd, GitHub Actions, Cloudflare form my daily toolset. I can shape this ecosystem according to my own needs, without being constrained by any vendor. This level of control is invaluable, especially when developing complex pipelines or specialized AI applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Learning and Experience
&lt;/h3&gt;

&lt;p&gt;Even with 20 years of field experience, the world of technology is constantly changing. My own server acts as a sandbox for trying out new technologies, tools, and approaches. Moments when my Astro build consumes 2.5 GB of RAM and gets OOM-killed, or when Docker clogs the disk with 33 GB of build cache and 23 GB of unused images, transform theoretical knowledge into practical experience. These real-world scenarios constantly push me to find better solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Economic Efficiency and Scalability
&lt;/h3&gt;

&lt;p&gt;In some cases, hosting on my own server also offers cost advantages. Especially using self-hosted runners to avoid exceeding GitHub Actions quotas provides me with both flexibility and cost savings. The 13+ Docker containers I manage on my own VPS (including Postgres, Redis, Next.js applications) allow me to host multiple projects on a single server at a low cost. This is particularly important for side products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Innovation and Paving My Own Way
&lt;/h3&gt;

&lt;p&gt;When it comes to setting up AI-&lt;/p&gt;

</description>
      <category>life</category>
    </item>
    <item>
      <title>Nginx's Insidious DNS Trap: Unable to Reach Docker Containers</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sun, 10 May 2026 10:57:29 +0000</pubDate>
      <link>https://forem.com/merbayerp/nginxs-insidious-dns-trap-unable-to-reach-docker-containers-1o40</link>
      <guid>https://forem.com/merbayerp/nginxs-insidious-dns-trap-unable-to-reach-docker-containers-1o40</guid>
      <description>&lt;p&gt;It was last Thursday morning when one of &lt;code&gt;hesapciyiz.com&lt;/code&gt;'s API endpoints suddenly started returning a 502 Bad Gateway error. My first thought, of course, was that the backend application had crashed&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>docker</category>
      <category>dns</category>
      <category>vps</category>
    </item>
    <item>
      <title>Docker Disk Storage Wars: A Guide to Data Integrity on a VPS</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sun, 10 May 2026 09:31:04 +0000</pubDate>
      <link>https://forem.com/merbayerp/docker-disk-storage-wars-a-guide-to-data-integrity-on-a-vps-2h2l</link>
      <guid>https://forem.com/merbayerp/docker-disk-storage-wars-a-guide-to-data-integrity-on-a-vps-2h2l</guid>
      <description>&lt;p&gt;Last month, on the morning of April 28th, I woke up to a "Disk Space Critical" email from my own VPS. When I ran &lt;code&gt;df -h&lt;/code&gt;, I saw that the &lt;code&gt;/&lt;/code&gt; directory was 100% full. As I suspected, Docker was the culprit again. When managing over 13 containers on the same server, if even one gets out of control, it's only a matter of time before the entire system goes into swap and gets OOM-killed.&lt;/p&gt;

&lt;p&gt;This situation is familiar to anyone who constantly struggles with disk storage issues, hosting multiple applications on their own server. In this post, I'll explain how I manage these "Docker Disk Storage Wars" on my own VPS, how I ensure data integrity, and how I optimize disk space. My goal is to guide you with practical solutions and experiences I've personally had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do Docker Disk Storage Wars Erupt?
&lt;/h2&gt;

&lt;p&gt;My VPS's disk space suddenly filling up has almost become a routine for me. Most of the time, the root of the problem lies in unexpected container log growth, unnecessary images, or build caches. For example, at one point, the build caches of my Next.js applications reached 33 GB, and with unused images added on top, they consumed another 23 GB of disk space. That's when the disk hit 100%.&lt;/p&gt;

&lt;p&gt;Such situations can lead to severe performance degradation and even service outages, especially if you're using a VPS with limited resources. A container's disk I/O maxing out can lead to &lt;code&gt;kcompactd&lt;/code&gt; using 92% CPU and sshd being unable to accept new connections. That's why it's crucial to understand the root cause of the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Disk Space Consumers
&lt;/h3&gt;

&lt;p&gt;Knowing the biggest disk space consumers in the Docker ecosystem is the first step to solving the problem. In my experience, the main culprits are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dangling Images and Volumes:&lt;/strong&gt; Unused or disconnected images and volumes can unknowingly occupy gigabytes of space. This becomes inevitable, especially if you frequently rebuild images.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Build Cache:&lt;/strong&gt; Even if you don't use multi-stage builds, Docker creates intermediate layers at each build step. These caches can accumulate and reach enormous sizes. My 33 GB build cache issue was a prime example of this.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Container Logs:&lt;/strong&gt; Especially verbose applications or services running in debug mode can cause log files to grow uncontrollably. Gigabytes of logs can accumulate within a few days; I even witnessed critical services on an internal banking platform halt due to this.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ephemeral Data and Temporary Files:&lt;/strong&gt; Temporary files created by applications during operation can take up permanent disk space if not properly cleaned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;docker system df&lt;/code&gt; command is very useful for identifying these issues. This command provides a detailed summary of Docker's disk usage, helping me understand which component occupies how much space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of this command shows how much disk space each Docker component is using. For example, you can find detailed information under categories like "Images", "Containers", "Local Volumes", and "Build Cache". The "Reclaimable" area, in particular, shows the amount of disk space you can recover with manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Integrity and Persistent Storage Strategies
&lt;/h2&gt;

&lt;p&gt;One of the most crucial aspects when running applications in Docker is ensuring data persistence, even if containers die. Since I host my own sites and side products (like hesapciyiz.com, spamkalkani.com), data loss is critical. A wrong configuration or automatic cleanup can instantly wipe out your data.&lt;/p&gt;

&lt;p&gt;Therefore, it's essential to correctly understand Docker's storage mechanisms and develop proactive strategies. I generally prefer using &lt;code&gt;volumes&lt;/code&gt; because they are easier to manage and are a persistent storage solution designed within Docker itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Volumes and Bind Mounts
&lt;/h3&gt;

&lt;p&gt;Docker offers two primary methods for making data persistent: &lt;code&gt;volumes&lt;/code&gt; and &lt;code&gt;bind mounts&lt;/code&gt;. Both have their advantages and disadvantages, and the choice depends somewhat on the use case.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Volumes:&lt;/strong&gt; These are file systems managed by Docker. They are typically located under `/var/lib/&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>vps</category>
      <category>depolama</category>
      <category>veributunlugu</category>
    </item>
    <item>
      <title>VPS Swap Fire: A Nightmare Started by a Kernel CVE Patch</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sun, 10 May 2026 01:23:44 +0000</pubDate>
      <link>https://forem.com/merbayerp/vps-swap-fire-a-nightmare-started-by-a-kernel-cve-patch-3c4f</link>
      <guid>https://forem.com/merbayerp/vps-swap-fire-a-nightmare-started-by-a-kernel-cve-patch-3c4f</guid>
      <description>&lt;p&gt;Last week, precisely on a Monday morning, the "Critical Alert" notifications on my monitor struck fear into my eyes. The systems running on my own VPS, especially my Docker containers, had suddenly started to slow down. Even SSH connections were lagging, and my commands were taking a long time to execute. Yet, I hadn't done anything; I had just applied the usual overnight updates.&lt;/p&gt;

&lt;p&gt;This sudden slowdown was a major problem for me. Because this VPS is my entire world. It runs over 13 Docker containers: a PostgreSQL database, Redis cache, my Next.js applications, and of course, the Astro site where this blog is published. Everything was running smoothly together. Until this morning. I started a deep dive to find the source of the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Swap Usage Spiraling Out of Control
&lt;/h2&gt;

&lt;p&gt;The first place I looked was the server's overall resource usage. The moment I ran the &lt;code&gt;htop&lt;/code&gt; command, I couldn't believe my eyes: Swap usage was nearing 100%. Normally, I keep my swap space very low, sometimes I even disable it. But this time, the situation was different. Such high swap usage indicated that the system's RAM was insufficient and it had started using the swap space on the disk. This, in turn, caused performance to plummet.&lt;/p&gt;

&lt;p&gt;Why had swap usage suddenly spiked so high? I immediately checked the &lt;code&gt;dmesg&lt;/code&gt; and &lt;code&gt;journalctl&lt;/code&gt; logs. I was seeing a lot of warnings related to &lt;code&gt;kcompactd&lt;/code&gt; and &lt;code&gt;oom-killer&lt;/code&gt;. I noticed that &lt;code&gt;kcompactd&lt;/code&gt; was consuming CPU at around 90%. This signaled that the kernel was experiencing a serious issue with memory management.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ The Dangers of Swap Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Swap space is a disk-based storage area that comes into play when physical RAM (memory) is insufficient. However, disks are much slower than RAM. Increased swap usage directly leads to a noticeable drop in system performance. Excessive swap usage can cause the server to freeze or processes to be abruptly terminated by the &lt;code&gt;oom-killer&lt;/code&gt; (Out-Of-Memory killer).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A Nightmare Started by a Kernel CVE Patch
&lt;/h2&gt;

&lt;p&gt;As I examined the logs in more detail, I realized the problem had started with the kernel update I applied overnight. I had specifically applied a patch related to &lt;em&gt;CVE-2026-31431&lt;/em&gt;. This CVE was intended to close a security vulnerability in the kernel's network stack. However, it seemed this patch had caused unexpected side effects on my system.&lt;/p&gt;

&lt;p&gt;This CVE patch was closely related to the kernel's memory management. It contained a fix specifically for the &lt;code&gt;algif_aead&lt;/code&gt; module. This module is used in VPN and encryption operations. Although I wasn't directly making VPN connections on my system, Docker's network operations and some firewall rules might have indirectly affected this module. What happened was not in a "corporate consultant" tone, but entirely my own experience, a situation where I thought, "these things happen."&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the Source of the Problem
&lt;/h2&gt;

&lt;p&gt;The reason behind &lt;code&gt;kcompactd&lt;/code&gt; consuming so much CPU was the kernel's attempt to keep memory pages contiguous. However, this process was causing a bottleneck in memory management. Everything had started with the kernel update I applied overnight. In my case, this update was incompatible with my existing setup.&lt;/p&gt;

&lt;p&gt;At this point, I remembered times when my Astro build consumed a lot of RAM. In those situations, the system would also resort to swap. But this time, the problem was deeper, at the kernel level. &lt;code&gt;kcompactd&lt;/code&gt; reaching 92% CPU usage was not normal. This situation had rendered the server unable to even accept SSH connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# A snippet from dmesg logs (not the actual error message, for illustration purposes)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;Mon May 09 06:15:32 2026] kcompactd0: highmem-intensive workload detected, entering compact mode
&lt;span class="o"&gt;[&lt;/span&gt;Mon May 09 06:16:01 2026] Out of memory: Kill process 12345 &lt;span class="o"&gt;(&lt;/span&gt;kworker/u8:1&lt;span class="o"&gt;)&lt;/span&gt; score 1000 or sacrifice child
&lt;span class="o"&gt;[&lt;/span&gt;Mon May 09 06:16:05 2026] systemd invoked oom-killer: &lt;span class="nv"&gt;gfp_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xd0, &lt;span class="nv"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;oom_score_adj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solutions and Trade-offs
&lt;/h2&gt;

&lt;p&gt;The first thing that came to my mind to solve the problem was to revert the kernel update I had applied. However, this meant reintroducing a version with a security vulnerability back into the system. This was not an acceptable option. Instead, I needed to adopt a more secure approach.&lt;/p&gt;

&lt;p&gt;Another option was to adjust &lt;code&gt;kcompactd&lt;/code&gt;'s behavior. By changing kernel parameters, I could make the memory compaction process less aggressive. However, this would not be a long-term solution and could lead to other problems.&lt;/p&gt;

&lt;p&gt;Ultimately, I decided that the most logical solution was to find an alternative solution related to the CVE patch that was causing the problem. This would take more time, but it was a safe solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ℹ️ Adjusting Kernel Parameters (Be Careful!)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While it's possible to adjust the kernel's memory management, these operations must be done very carefully. An incorrect parameter change can lead to system instability or prevent it from booting. These settings are usually made via the &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; file or files within the &lt;code&gt;/etc/sysctl.d/&lt;/code&gt; directory. However, this approach would be a short-term solution in my case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Temporary Solution: Reducing Swap Usage
&lt;/h2&gt;

&lt;p&gt;Until I got to the root of the problem, I had to implement some temporary solutions to keep the system running. First, I cleaned up unnecessary Docker images and build caches. I tried to free up disk space with the command &lt;code&gt;docker system prune -a&lt;/code&gt;. Then, I focused on optimizing the build process of my Astro project.&lt;/p&gt;

&lt;p&gt;During this time, I also recalled the &lt;code&gt;runner state corruption&lt;/code&gt; issue I experienced with GitHub Actions. In that case, deleting directories under &lt;code&gt;/home/runner/_work/_temp&lt;/code&gt; had resolved it. Such issues indicate imbalances in the current system.&lt;/p&gt;

&lt;p&gt;As a temporary solution, I considered writing a script that would automatically stop or lower the priority of certain operations when swap usage was very high. However, this was not a complete fix, just a preventive measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Solution: CVE Patch Alternative
&lt;/h2&gt;

&lt;p&gt;Instead of reverting the official patch for CVE-2026-31431, I decided to use an alternative kernel module that addressed a similar vulnerability without being tied to that specific patch. This required some research. I needed to find a more stable encryption module compatible with my system, instead of &lt;code&gt;algif_aead&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, I found a kernel version that mitigated the impact of this specific CVE and ran stably on my system. I installed the new kernel version and restarted the system. My first check confirmed that swap usage had returned to normal levels, and &lt;code&gt;kcompactd&lt;/code&gt; was no longer straining the CPU. SSH connections had sped up.&lt;/p&gt;

&lt;p&gt;During this period, I also remembered the disk full issue I experienced on my own VPS on April 28th. At that time, there were 33 GB of build cache and 23 GB of unused images on the disk. While this current issue was more related to memory management, I once again saw how important regular cleaning and optimization are for the overall health of the system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pipeline Reliability Pattern: Preflight, Auto-fix, Dedup-Alert&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I encounter unexpected issues like this, I try to apply a general "pipeline reliability" pattern. This pattern is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Preflight Resource Guard:&lt;/strong&gt; Before starting an operation, check if resources (disk, RAM, CPU) are sufficient.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Auto-fix:&lt;/strong&gt; Automatically apply issues that can be resolved automatically (disk cleanup, simple service restart).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dedup-Alert:&lt;/strong&gt; Prevent repeated alerts for the same issue; try to fix the problem first, then notify if it cannot be resolved.
The AI-assisted content creation process for this blog was also designed similarly to this pattern.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Lessons Learned and Future Steps
&lt;/h2&gt;

&lt;p&gt;This experience taught me several important lessons. Firstly, I realized I need to be much more careful when applying kernel updates. Every update can lead to unexpected side effects on the system. Especially in production environments, testing updates in a staging environment is essential.&lt;/p&gt;

&lt;p&gt;Secondly, regular monitoring of system resources (RAM, swap) and early detection of anomalies are crucial. In addition to tools like &lt;code&gt;htop&lt;/code&gt;, &lt;code&gt;dmesg&lt;/code&gt;, and &lt;code&gt;journalctl&lt;/code&gt;, using more advanced monitoring systems can be beneficial. When managing so many containers on my own server, even a single container issue can affect the entire system.&lt;/p&gt;

&lt;p&gt;Finally, while it's important to apply patches for CVEs quickly and effectively, I must not forget that these patches can themselves cause problems. Therefore, when applying security patches, I must closely monitor the system's overall behavior. Perhaps in my next blog post, I can prepare a guide on the economic advantages of self-hosted runners in GitHub Actions and using VPS to avoid exceeding quotas.&lt;/p&gt;

&lt;p&gt;Have you ever encountered such unexpected system issues? I'd love to hear about it in the comments.&lt;/p&gt;

</description>
      <category>vps</category>
      <category>swap</category>
      <category>kernel</category>
      <category>cve</category>
    </item>
    <item>
      <title>Cloudflare Cache's Blind Spot: The Cost of Bypass Rules</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sat, 09 May 2026 18:46:04 +0000</pubDate>
      <link>https://forem.com/merbayerp/cloudflare-caches-blind-spot-the-cost-of-bypass-rules-3223</link>
      <guid>https://forem.com/merbayerp/cloudflare-caches-blind-spot-the-cost-of-bypass-rules-3223</guid>
      <description>&lt;p&gt;Last month, I started noticing unexplained spikes in server load on my blog, &lt;code&gt;mustafaerbay.com&lt;/code&gt;, especially when publishing new articles or when popular content received traffic. I manage over 13 Docker containers on my own VPS, and when one started misbehaving, others would swap, &lt;code&gt;kcompactd&lt;/code&gt; would eat 90% of the CPU, and even SSH connections became impossible to establish. This was surprising for a site sitting behind Cloudflare. I even recall a moment when the Astro build process consumed 2.5 GB of RAM, straining the system's 7.6 GB. Surely Cloudflare's cache should have sorted everything out, right?&lt;/p&gt;

&lt;p&gt;The problem wasn't Cloudflare itself, but rather my reliance on cache bypass rules and the blind spot created by the &lt;code&gt;Cache-Control&lt;/code&gt; headers coming from my origin server. It took me a while to realize how Astro's default &lt;code&gt;max-age=0&lt;/code&gt; setting was completely disrupting the caching logic I had established with Cloudflare. In this post, I'll explain how I diagnosed this cache blind spot, its causes, and how I gained control over cache behavior using Cloudflare and Nginx.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Unexpected Side Effects of Cache Bypass Rules
&lt;/h2&gt;

&lt;p&gt;On my own blog, I had defined some cache bypass rules on Cloudflare for specific dynamic-looking sections like &lt;code&gt;mustafaerbay.com/feed.xml&lt;/code&gt; or backend services like &lt;code&gt;/api/*&lt;/code&gt;. My goal was to ensure these particular URLs always fetched the latest data from the origin. It seemed logical. However, over time, I noticed that these rules were causing unintended side effects.&lt;/p&gt;

&lt;p&gt;Even static content in the &lt;code&gt;/blog/*&lt;/code&gt; path of my blog posts occasionally started hitting my origin server directly. Although I could see &lt;code&gt;cf-cache-status: HIT&lt;/code&gt; for some requests in the Cloudflare panel, my origin server's CPU and I/O load indicated that caching wasn't working as it should. This situation led to my server becoming overloaded, especially during peak traffic, and at one point, it escalated into a Docker disk fire. 33 GB of build cache and 23 GB of unused images filled the disk to 100%, causing the VPS to completely freeze. This wasn't just a caching issue; it became a chain reaction that turned into an operational nightmare.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symptoms and Initial Observations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;High Origin Load:&lt;/strong&gt; My server's CPU and RAM usage were significantly higher than expected, despite Cloudflare being in front. I was seeing &lt;code&gt;node&lt;/code&gt; processes peaking in &lt;code&gt;htop&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Inconsistent Page Load Times:&lt;/strong&gt; Some users experienced lightning-fast page loads, while others faced noticeable delays. This was a clear sign of inconsistent caching.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;cf-cache-status: BYPASS&lt;/code&gt;:&lt;/strong&gt; In browser developer tools or during &lt;code&gt;curl&lt;/code&gt; tests, I started seeing the &lt;code&gt;cf-cache-status: BYPASS&lt;/code&gt; header for some static content. This was my first red flag.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Disk Fullness:&lt;/strong&gt; Increased traffic and unexpected origin requests caused log files to grow rapidly and temporary files for Docker containers to bloat. This led to the 100% disk usage mentioned earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ℹ️ Sharing Experience&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Performance issues like these rarely stem from a single point. While a cache bypass rule might be a trigger, as I experienced, underlying issues like poor disk management or application resource consumption can exacerbate the situation. I always try to see the whole picture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Diagnosis: Where Did I Start?
&lt;/h2&gt;

&lt;p&gt;To understand the problem, I proceeded step-by-step. Instead of panicking, I used the data and tools at hand to figure out what was happening.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudflare Logs and Analytics
&lt;/h3&gt;

&lt;p&gt;First, I checked the Analytics section in the Cloudflare panel. While the edge cache hit ratios generally looked good, I noticed a high &lt;code&gt;Cache Bypass&lt;/code&gt; rate for certain URL patterns. This provided the initial clue as to which requests were reaching the origin. I specifically examined the &lt;code&gt;Security&lt;/code&gt; and &lt;code&gt;Firewall&lt;/code&gt; logs to see which rules were being triggered and how they were affecting cache behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Origin Server Logs
&lt;/h3&gt;

&lt;p&gt;I dove into the Nginx and Node.js (where my Astro application was running) logs. In Nginx's &lt;code&gt;access.log&lt;/code&gt; file, I started seeing continuous 200 OK responses for static blog post URLs that should have been served by Cloudflare. I confirmed whether a request came from Cloudflare or directly by looking at the &lt;code&gt;X-Forwarded-For&lt;/code&gt; header in these logs. Using &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt; commands, I quickly compared request counts for specific URL patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Count requests containing "/blog/" in Nginx access log&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"/blog/"&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Filter requests from a specific IP (Cloudflare IP range)&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"172.68.XXX.XXX"&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"/blog/"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tests with &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;httpie&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I obtained the most concrete data through &lt;code&gt;curl&lt;/code&gt; commands. I tested cache behavior by sending different headers (especially &lt;code&gt;Cookie&lt;/code&gt; or &lt;code&gt;User-Agent&lt;/code&gt;). Cloudflare's &lt;code&gt;cf-cache-status&lt;/code&gt; header clearly indicates whether a request came from the cache (&lt;code&gt;HIT&lt;/code&gt;), went to the origin (&lt;code&gt;BYPASS&lt;/code&gt;), or was cached for the first time (&lt;code&gt;MISS&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check cache status for a blog post&lt;/span&gt;
curl &lt;span class="nt"&gt;-svo&lt;/span&gt; /dev/null https://mustafaerbay.com/blog/cloudflare-cachein-kor-noktasi-bypass-kuralinin-bedeli 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"cf-cache-status"&lt;/span&gt;

&lt;span class="c"&gt;# Test request with a Cookie (Cloudflare usually bypasses if Cookie is present)&lt;/span&gt;
curl &lt;span class="nt"&gt;-svo&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cookie: my_session_id=123"&lt;/span&gt; https://mustafaerbay.com/blog/cloudflare-cachein-kor-noktasi-bypass-kuralinin-bedeli 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"cf-cache-status"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Quick Check&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The command &lt;code&gt;curl -svo /dev/null https://mysite.com/path 2&amp;gt;&amp;amp;1 | grep -i "cf-cache-status"&lt;/code&gt; is an indispensable tool for instantly checking Cloudflare cache status.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Browser Developer Tools
&lt;/h3&gt;

&lt;p&gt;Finally, I opened my browser's developer tools (F12), went to the Network tab, and inspected the headers of the requests made when I refreshed the page. The &lt;code&gt;cf-cache-status&lt;/code&gt; and &lt;code&gt;cache-control&lt;/code&gt; headers were clearly visible here as well. Seeing the combination of &lt;code&gt;cf-cache-status: BYPASS&lt;/code&gt; and &lt;code&gt;Cache-Control: max-age=0&lt;/code&gt; made me understand the root of the problem much better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Cause Analysis: Why Was It Bypassing?
&lt;/h2&gt;

&lt;p&gt;All these diagnostic steps led me to two primary issues: the broad scope of Cloudflare's cache bypass rules and the misinterpretation of &lt;code&gt;Cache-Control&lt;/code&gt; headers from my origin server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudflare Page Rules vs. Cache Rules
&lt;/h3&gt;

&lt;p&gt;Cloudflare has two main rule sets: Page Rules and Cache Rules. Page Rules are older and more general-purpose, while Cache Rules are designed for more specific control over cache behavior. In my scenario, a rule defined in Page Rules had &lt;code&gt;Cache Level: Bypass&lt;/code&gt; set for specific paths. While this rule targeted dynamic endpoints like &lt;code&gt;*mustafaerbay.com/api/*&lt;/code&gt;, it unintentionally affected static content like &lt;code&gt;/blog/*&lt;/code&gt; due to its broad scope.&lt;/p&gt;

&lt;p&gt;Cloudflare's default caching behavior was also important here. It generally caches &lt;code&gt;GET&lt;/code&gt; requests, but it bypasses requests containing &lt;code&gt;Cookie&lt;/code&gt; headers, &lt;code&gt;Query String&lt;/code&gt;s, or directives like &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; by default. When my bypass rule combined with this default behavior, even static content was being routed to the origin.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Role of the &lt;code&gt;Cache-Control&lt;/code&gt; Header
&lt;/h3&gt;

&lt;p&gt;The biggest blind spot was the &lt;code&gt;Cache-Control&lt;/code&gt; header that my Astro application was returning by default. Although Astro is a static site generator, when run as a Node.js-based SSR (Server-Side Rendering) or API endpoint, it can often return headers like &lt;code&gt;Cache-Control: max-age=0, must-revalidate&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;This header instructed Cloudflare to "check the validity of this content immediately" or "don't cache, always go to the origin." Even without my Cloudflare bypass rule, this &lt;code&gt;max-age=0&lt;/code&gt; directive from the origin was preventing Cloudflare from caching the content. Instead of seeing &lt;code&gt;Cache-Control: public, max-age=X&lt;/code&gt;, seeing &lt;code&gt;max-age=0&lt;/code&gt; meant Cloudflare wouldn't cache the content. I can say that Astro's &lt;code&gt;max-age=0&lt;/code&gt; gave me quite a bit of trouble on my own blog. This situation rendered even Cloudflare's powerful caching mechanism ineffective.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Important Information&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cache-Control&lt;/code&gt; headers from your origin server can even override your Cloudflare Edge Cache TTL settings. If the origin sends &lt;code&gt;max-age=0&lt;/code&gt; or &lt;code&gt;no-cache&lt;/code&gt;, Cloudflare will generally respect this directive and not cache the content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Solution: Taking Control of Cache Behavior
&lt;/h2&gt;

&lt;p&gt;After understanding the root cause of the problem, I took steps to gain full control over cache behavior using Cloudflare and Nginx.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Precise Control with Cloudflare Cache Rules
&lt;/h3&gt;

&lt;p&gt;The first step was to review the old Page Rules in Cloudflare and define more specific Cache Rules instead.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Review and Narrow Down Existing Bypass Rules:&lt;/strong&gt; I refined the &lt;code&gt;Cache Level: Bypass&lt;/code&gt; rules in the existing Page Rules to be specific only to paths that truly needed to be dynamic, like &lt;code&gt;/api/*&lt;/code&gt; or &lt;code&gt;/login/*&lt;/code&gt;. I excluded static content like &lt;code&gt;/blog/*&lt;/code&gt; from these rules.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Define Cache Rules for Static Content:&lt;/strong&gt; I created a new Cache Rule for all content under the &lt;code&gt;/blog/*&lt;/code&gt; path. This rule sets the &lt;code&gt;Edge Cache TTL&lt;/code&gt; to a specific duration (e.g., 1 hour or 1 day) and sets the &lt;code&gt;Cache Level&lt;/code&gt; to &lt;code&gt;Cache Everything&lt;/code&gt;. I also kept options like &lt;code&gt;Bypass Cache on Cookie&lt;/code&gt; active only where truly necessary (like &lt;code&gt;/account/*&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An example of a Cloudflare Cache Rule (when defining via API or Terraform, it would be similar to this format):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mustafa_blog_cache_rule"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cache_settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"edge_cache_ttl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;hour&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"cache_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cache_everything"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(http.request.uri.path contains &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/blog/&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;) and (not http.cookie)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mustafa_api_bypass_rule"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cache_settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"edge_cache_ttl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bypass&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"cache_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bypass_cache"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http.request.uri.path contains &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/api/&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example caches requests under the &lt;code&gt;/blog/&lt;/code&gt; path that do not contain cookies for 1 hour, while bypassing all requests under the &lt;code&gt;/api/&lt;/code&gt; path. Creating these rules through the Cloudflare UI is much easier and more visual.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nginx Override on the Origin Server
&lt;/h3&gt;

&lt;p&gt;Cloudflare Cache Rules are great for managing cache on Cloudflare's side. However, the &lt;code&gt;Cache-Control: max-age=0&lt;/code&gt; header from my origin server was still an issue. Cloudflare tended to respect this directive. Therefore, I decided to override this header using Nginx.&lt;/p&gt;

&lt;p&gt;Using Nginx's &lt;code&gt;proxy_hide_header&lt;/code&gt; and &lt;code&gt;add_header&lt;/code&gt; directives, I hid the &lt;code&gt;Cache-Control&lt;/code&gt; and &lt;code&gt;Pragma&lt;/code&gt; headers coming from the origin and added my own desired &lt;code&gt;Cache-Control&lt;/code&gt; header instead. This prevented Cloudflare from seeing the &lt;code&gt;max-age=0&lt;/code&gt; directive and allowed it to cache the content with my specified cache duration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;mustafaerbay.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Redirect HTTP requests from Cloudflare to HTTPS (optional, but generally good practice)&lt;/span&gt;
    &lt;span class="c1"&gt;# if ($http_x_forwarded_proto != 'https') {&lt;/span&gt;
    &lt;span class="c1"&gt;#     return 301 https://$host$request_uri;&lt;/span&gt;
    &lt;span class="c1"&gt;# }&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/blog/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# Hide Cache-Control and Pragma from Astro&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_hide_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_hide_header&lt;/span&gt; &lt;span class="s"&gt;Pragma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Add our own Cache-Control: public cache for 1 hour&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;"public,&lt;/span&gt; &lt;span class="s"&gt;max-age=3600"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Port where Astro app is running&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Other location blocks (e.g., /api/ or root path)&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# ... other Nginx settings&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this Nginx configuration, for all requests under the &lt;code&gt;/blog/&lt;/code&gt; path, the &lt;code&gt;Cache-Control&lt;/code&gt; header from Astro is hidden by Nginx, and &lt;code&gt;Cache-Control: public, max-age=3600&lt;/code&gt; is added instead. This allows Cloudflare to cache this content for 1 hour.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Caution: Nginx Override&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You must be very careful when overriding cache headers from the origin. If you accidentally cache dynamic content as static, users might see old or incorrect data. Therefore, I only applied the override for paths that truly needed to be static.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation Steps and Verification
&lt;/h2&gt;

&lt;p&gt;After applying these changes, I performed extensive tests to ensure everything was working as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Review and Clean Up Existing Rules
&lt;/h3&gt;

&lt;p&gt;I reviewed all Page Rules and Cache Rules in the Cloudflare panel. I either deleted rules that conflicted or seemed unnecessary, or I narrowed their scope. I specifically disabled all bypass rules affecting the &lt;code&gt;/blog/*&lt;/code&gt; path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Check Origin Headers
&lt;/h3&gt;

&lt;p&gt;Before changing the Nginx configuration, I checked the headers coming directly from the Astro application with commands like &lt;code&gt;curl -I http://localhost:3000/blog/post-name&lt;/code&gt;. I made sure I was seeing &lt;code&gt;Cache-Control: max-age=0&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Define Cloudflare Cache Rule
&lt;/h3&gt;

&lt;p&gt;I defined the &lt;code&gt;mustafa_blog_cache_rule&lt;/code&gt; mentioned above through the Cloudflare UI. I set the &lt;code&gt;Edge Cache TTL&lt;/code&gt; to 1 hour and &lt;code&gt;Cache Level&lt;/code&gt; to &lt;code&gt;Cache Everything&lt;/code&gt;. I disabled options like cookie bypass for this rule, ensuring it would cache under all conditions (provided the &lt;code&gt;Cache-Control&lt;/code&gt; header from Nginx was considered).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Nginx Configuration Changes
&lt;/h3&gt;

&lt;p&gt;I SSH'd into my VPS, edited the &lt;code&gt;/etc/nginx/sites-available/mustafaerbay.com&lt;/code&gt; file, and applied the Nginx configuration above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/mustafaerbay.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving the changes, I tested the Nginx configuration and restarted it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Testing and Verification
&lt;/h3&gt;

&lt;p&gt;Finally, I re-tested using &lt;code&gt;curl&lt;/code&gt; and browser developer tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-svo&lt;/span&gt; /dev/null https://mustafaerbay.com/blog/cloudflare-cachein-kor-noktasi-bypass-kuralinin-bedeli 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"cf-cache-status&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;cache-control"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was now seeing &lt;code&gt;cf-cache-status: HIT&lt;/code&gt; and &lt;code&gt;Cache-Control: public, max-age=3600&lt;/code&gt; headers. This meant Cloudflare was successfully caching the content and not putting unnecessary load on the origin. My server's CPU and RAM usage also dropped significantly. Even when traffic to my blog increased, I started seeing &lt;code&gt;304 Not Modified&lt;/code&gt; or &lt;code&gt;HIT&lt;/code&gt; instead of &lt;code&gt;BYPASS&lt;/code&gt; in the Nginx logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned and Best Practices
&lt;/h2&gt;

&lt;p&gt;This incident taught me several important lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Headers Are Critical:&lt;/strong&gt; HTTP headers, especially &lt;code&gt;Cache-Control&lt;/code&gt;, can be the smallest yet most impactful details in a system architecture. When using a CDN like Cloudflare, it's crucial to understand how headers from the origin server are interpreted.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Thorough Testing is Essential:&lt;/strong&gt; Just because everything looks fine in the Cloudflare panel doesn't mean everything is truly okay. Testing different scenarios with &lt;code&gt;curl&lt;/code&gt; is essential to see the actual behavior.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Beware of Bypass Rules:&lt;/strong&gt; Cache bypass rules are powerful, but they should be kept as specific as possible. Broadly scoped rules can lead to unexpected outcomes, as I experienced. Always ask, "What will this rule affect?"&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Monitoring is Indispensable:&lt;/strong&gt; Continuously monitoring performance metrics is critical for early detection of issues like abnormal origin load or disk fullness. This is even more important for someone like me managing their own server. This situation reminded me once again of the "preflight resource guard" principle in pipeline reliability. It's always better to check a resource before loading it than to put out fires later.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Understand Trade-offs:&lt;/strong&gt; Caching isn't always good. Bypassing might be necessary for dynamic content. However, if we don't want static content to behave like dynamic content, managing this trade-off with an intermediate layer like Nginx is necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Cloudflare's caching mechanism is a powerful tool, but managing bypass rules and &lt;code&gt;Cache-Control&lt;/code&gt; headers from the origin server correctly is vital for performance and operational stability. My experience demonstrated that even static content can overload the origin due to misconfigurations. By combining Cloudflare Cache Rules with Nginx's &lt;code&gt;proxy_hide_header&lt;/code&gt; and &lt;code&gt;add_header&lt;/code&gt; directives, I overcame this blind spot and ensured my blog ran much more stably.&lt;/p&gt;

&lt;p&gt;The lessons learned from this process carry important insights for those who, like me, manage their own servers and pay attention to every millisecond of performance. Do you have a similarly frustrating caching story? Or have you used different approaches to solve these problems? I'd love to hear about it in the comments.&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>caching</category>
      <category>nginx</category>
      <category>performance</category>
    </item>
    <item>
      <title>My CI Runner Was Killed by My Own Script: The Dark Side of Cleanup</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sat, 09 May 2026 14:35:40 +0000</pubDate>
      <link>https://forem.com/merbayerp/my-ci-runner-was-killed-by-my-own-script-the-dark-side-of-cleanup-6o0</link>
      <guid>https://forem.com/merbayerp/my-ci-runner-was-killed-by-my-own-script-the-dark-side-of-cleanup-6o0</guid>
      <description>&lt;p&gt;Towards the end of last month, I started a build job on my self-hosted GitHub Actions runner. It was a job that normally took 10-15 minutes, but this time it just wouldn't finish. The job seemed stuck, and I wasn't getting any response from the runner. When I tried to connect to the server via SSH, the connection was refused. It felt similar to the OOM scenarios I'd experienced on my VPS where &lt;code&gt;sshd&lt;/code&gt; couldn't &lt;code&gt;accept&lt;/code&gt; connections, but this time my RAM usage was normal.&lt;/p&gt;

&lt;p&gt;After some digging, I realized my runner's heart had stopped beating. Looking at the GitHub Actions panel, I saw the runner was "Offline". The interesting thing was that the server itself was up, and my other Docker containers were running without issues. The only problem was my CI runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting to the Root of the Problem: A Cleanup Script Murder
&lt;/h2&gt;

&lt;p&gt;To understand why the runner had died, I connected to the server via console. My first task was to check the &lt;code&gt;dmesg&lt;/code&gt; output. There was nothing surprising there; no kernel-level error or OOM killer trigger was visible. When I checked the service status with the &lt;code&gt;systemctl status github-runner&lt;/code&gt; command, I encountered an even more interesting situation: the service was &lt;code&gt;active (exited)&lt;/code&gt;, and there were no error messages in the logs. It was as if someone had gracefully shut down the service.&lt;/p&gt;

&lt;p&gt;It was at this exact moment that the "innocent" cleanup script I'd added last week came to mind. I manage over 13 Docker containers on my own VPS, and disk space can sometimes become critical. Especially Docker's &lt;code&gt;build cache&lt;/code&gt; and unused images, with 33 GB of &lt;code&gt;build cache&lt;/code&gt; and 23 GB of &lt;code&gt;unused images&lt;/code&gt;, can fill my disk up to 100%. Because of this, I had written a script to clean up old build outputs and unnecessary files in the &lt;code&gt;_work&lt;/code&gt; directory.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Chaos of My Own Making&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This kind of automation can be a lifesaver, yes. But if it's not tested sufficiently or if scenarios aren't well thought out, shooting yourself in the foot becomes inevitable. My scenario was exactly that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Killer Script and the Victim Runner
&lt;/h3&gt;

&lt;p&gt;The script I wrote simply deleted files older than a certain age in the &lt;code&gt;_work&lt;/code&gt; directory. However, I had overlooked a small detail: the runner itself also operated within the &lt;code&gt;_work&lt;/code&gt; directory, and temporary directories like &lt;code&gt;_temp&lt;/code&gt;, or even in some cases the runner's own binaries or configuration files, could fall within this scope. I had previously experienced the pain of deleting directories inside &lt;code&gt;_work/_temp&lt;/code&gt; on a GitHub Actions runner, but this time I had gone even further.&lt;/p&gt;

&lt;p&gt;I hadn't used parameters like &lt;code&gt;maxdepth&lt;/code&gt; or &lt;code&gt;prune&lt;/code&gt; in the &lt;code&gt;find&lt;/code&gt; command within the script carefully enough. While my goal was only build artifacts, the script had deleted some files vital for the runner itself. The result: The runner service quietly shut down when it couldn't access the necessary files to continue operating. This was a resource management disaster, similar to my Astro build consuming 2.5 GB of RAM and hitting OOM, but this time it was disk and file system related.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# A snippet from the faulty cleanup script (simplified version)&lt;/span&gt;
&lt;span class="c"&gt;# This command was deleting all files older than 7 days under the _work directory.&lt;/span&gt;
&lt;span class="c"&gt;# However, the runner's own working files were also included in this scope.&lt;/span&gt;
find /home/runner/_work/ &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; +7 &lt;span class="nt"&gt;-delete&lt;/span&gt;
find /home/runner/_work/ &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-empty&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command, `/home&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>githubactions</category>
      <category>cleanup</category>
      <category>selfinflicted</category>
    </item>
    <item>
      <title>System Architecture is a Bit About Paranoia</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sat, 09 May 2026 11:50:07 +0000</pubDate>
      <link>https://forem.com/merbayerp/system-architecture-is-a-bit-about-paranoia-299l</link>
      <guid>https://forem.com/merbayerp/system-architecture-is-a-bit-about-paranoia-299l</guid>
      <description>&lt;p&gt;Recently, a series of &lt;code&gt;OOM-killed&lt;/code&gt; errors in the AI generation pipeline running on my own VPS took me back to the old days. I once again saw how a &lt;code&gt;sleep 360&lt;/code&gt; command could wreak havoc on a system and the cost of a simple mistake. This situation made me realize that system architecture is, in fact, a bit about "paranoya."&lt;/p&gt;

&lt;p&gt;For me, this state of "paranoia" is like a way of life built on anticipating worst-case scenarios, accepting that anything can go wrong, and taking precautions accordingly. While it might sound a bit negative, my 20 years of experience in the field have repeatedly proven why this approach is indispensable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roots of Paranoia: Past Scars
&lt;/h2&gt;

&lt;p&gt;This "paranoid" mindset isn't an empty delusion; it's a result of bitter experiences I've lived through and learned from. Over the years, I've seen many systems crash unexpectedly, slow down, or become completely inaccessible. These incidents formed the foundation of my architectural approach.&lt;/p&gt;

&lt;p&gt;I remember once, at a major Turkish e-commerce site, the database server completely locked up in the middle of a critical campaign. I'll never forget the helplessness and panic of that moment. I've experienced similar, though smaller-scale, crises on my own VPS; for example, my disk filling up to 100% on April 28th. Such events taught me how crucial it is to ask, "what if?"&lt;/p&gt;

&lt;h3&gt;
  
  
  Incidents on My Own VPS
&lt;/h3&gt;

&lt;p&gt;I manage over 13 Docker containers on my own server. Sometimes, when even one starts behaving unexpectedly, I see a domino effect, with others also getting swapped out. These scenarios show how each part of the system interacts with one another and how the weakest link can affect the entire system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ VPS Overload and OOM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most classic scenarios I've experienced on my own VPS is out-of-memory (OOM). Sometimes I've encountered situations like &lt;code&gt;kcompactd&lt;/code&gt; using 92% CPU, or &lt;code&gt;sshd&lt;/code&gt; being unable to accept new connections. This always reminds me how critical it is to monitor resources and know the limits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once, I noticed that Docker's build cache had reached 33 GB, and unused images were taking up 23 GB. My server disk was 100% full, and I couldn't even SSH in. This situation painfully taught me that even a simple full disk could cripple the entire operation. Since that day, I regularly run the &lt;code&gt;docker system prune -a&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing That Everything Can Break
&lt;/h2&gt;

&lt;p&gt;As a system architect, my most fundamental principle is to accept the fact that everything, absolutely everything, will break one day. This could be a hardware failure, a software bug, or a simple human error. What's important is knowing that these breakdowns will happen and how we build a resilience mechanism against them.&lt;/p&gt;

&lt;p&gt;I once experienced &lt;code&gt;state corruption&lt;/code&gt; in my GitHub Actions runner due to &lt;code&gt;_work/_temp&lt;/code&gt; directories. The pain of deleting those directories and having to rebuild the entire pipeline showed me how fragile even automation systems can be. Such incidents explain why redundancy and fast recovery mechanisms are so valuable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilience and Fault Tolerance
&lt;/h3&gt;

&lt;p&gt;This "paranoid" perspective drives me to focus more on the concepts of &lt;code&gt;resilience&lt;/code&gt; and &lt;code&gt;fault tolerance&lt;/code&gt; when designing systems. Planning how the system will remain operational if a component fails is one of the most critical steps in architecture.&lt;/p&gt;

&lt;p&gt;For example, this blog's Astro build process sometimes consumes 2.5 GB of RAM, pushing the total system RAM to 7.6 GB and resulting in an &lt;code&gt;OOM&lt;/code&gt;. In such a situation, I add a &lt;code&gt;preflight resource guard&lt;/code&gt; to the pipeline to check resources first. If resources are insufficient, I defer the operation and switch to &lt;code&gt;polling-wait&lt;/code&gt; mode. Last month, when I typed &lt;code&gt;sleep 360&lt;/code&gt; and got &lt;code&gt;OOM-killed&lt;/code&gt;, I had to activate this &lt;code&gt;polling-wait&lt;/code&gt; mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudflare Cache Strategies
&lt;/h3&gt;

&lt;p&gt;Even when using Cloudflare, this "paranoia" comes into play. Astro's default &lt;code&gt;max-age=0&lt;/code&gt; wasn't providing the performance I wanted for static content. Therefore, I implemented an &lt;code&gt;override&lt;/code&gt; on Nginx to define longer &lt;code&gt;cache&lt;/code&gt; durations for specific paths. This is a matter of making a &lt;code&gt;trade-off&lt;/code&gt; between content freshness and performance, and consciously managing that &lt;code&gt;trade-off&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/_astro/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;1y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;"public,&lt;/span&gt; &lt;span class="s"&gt;max-age=31536000,&lt;/span&gt; &lt;span class="s"&gt;immutable"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:4321&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, I set a 1-year &lt;code&gt;cache&lt;/code&gt; duration for static assets starting with &lt;code&gt;_astro&lt;/code&gt;. This reduces unnecessary origin hits at the &lt;code&gt;CDN&lt;/code&gt; layer, thereby improving performance and lightening the load on my server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Constant Vigilance
&lt;/h2&gt;

&lt;p&gt;One of the most prominent areas where "paranoia" is evident in system architecture is security. I always assume that attackers will try to find the weakest point in the system. Therefore, I try to take precautions not only against known vulnerabilities but also against potential risks.&lt;/p&gt;

&lt;p&gt;In recent years, I've seen many times how critical &lt;code&gt;CVE&lt;/code&gt;s can be. Even on my own system, I've tracked potential risks like &lt;code&gt;CVE-2026-31431&lt;/code&gt; and tried to close a possible vulnerability by &lt;code&gt;blacklisting&lt;/code&gt; kernel modules like &lt;code&gt;algif_aead&lt;/code&gt;. Such proactive steps strengthen the system's overall security posture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ℹ️ Proactive Security Measures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Steps like blacklisting kernel modules or disabling unnecessary services, while often seeming like minor details, can prevent major security incidents. Remember, the best security measure is to prevent an incident from happening.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Runner Economics on My Own VPS
&lt;/h3&gt;

&lt;p&gt;To avoid exceeding my GitHub Actions quota, I use a &lt;code&gt;self-hosted runner&lt;/code&gt; on my own VPS. While this provides a cost advantage, it also requires me to constantly track updates and security patches. This situation allows me to view "paranoia" as a form of cost optimization and risk management tool.&lt;/p&gt;

&lt;p&gt;Even when setting up my AI-powered content pipeline, I noticed errors occurring when slashes (&lt;code&gt;/&lt;/code&gt;) were used in &lt;code&gt;tags&lt;/code&gt; or when the &lt;code&gt;publishDate&lt;/code&gt; field wasn't a quoted &lt;code&gt;string&lt;/code&gt;. Even Turkish-specific details like the &lt;code&gt;dotted-i&lt;/code&gt; character problem show that unexpected issues can arise at every layer of the system. These kinds of "quirks" are part of my constant vigilance and thinking about every detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paranoia or Professionalism?
&lt;/h2&gt;

&lt;p&gt;So, what exactly is this "paranoia" in system architecture? For me, it's not about being constantly anxious or expecting a disaster at any moment. Rather, it's about understanding the inherent complexity and fragility of systems and taking conscious steps to minimize these risks. We could call this &lt;code&gt;risk-aware design&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a kind of engineering perspective. Just as an engineer building a bridge designs with scenarios like earthquakes, storms, or excessive loads in mind, we also strive to make our systems resilient against potential failures. I'm not ashamed of my own mistakes; on the contrary, last month when I typed &lt;code&gt;sleep 360&lt;/code&gt; and got &lt;code&gt;OOM-killed&lt;/code&gt;, I learned from that error and developed the &lt;code&gt;polling-wait&lt;/code&gt; mechanism. This is about asking, "what can I do to prevent this from happening again?" instead of just shrugging it off as "it happens."&lt;/p&gt;

&lt;p&gt;For me, system architecture is a bit like this: being constantly vigilant and designing with the knowledge that everything can go wrong. Without this "paranoia," none of my side projects like hesapciyiz.com, spamkalkani.com, or islistesi.com would run so stably. Have you ever had such "paranoid" moments? I'd love to hear about them in the comments.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>paranoya</category>
      <category>operasyon</category>
      <category>psychology</category>
    </item>
    <item>
      <title>That Meaningless Stress After a Deploy</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sat, 09 May 2026 09:20:12 +0000</pubDate>
      <link>https://forem.com/merbayerp/that-meaningless-stress-after-a-deploy-32o</link>
      <guid>https://forem.com/merbayerp/that-meaningless-stress-after-a-deploy-32o</guid>
      <description>&lt;p&gt;Recently, I deployed a small CSS change for this blog. Normally, it's a simple tweak, just shifting a few pixels, but after hitting &lt;code&gt;git push&lt;/code&gt;, that inexplicable tension settled over me again. It was as if I'd deployed a critical banking system; the question of "what if" started swirling somewhere inside.&lt;/p&gt;

&lt;p&gt;This feeling is familiar to me; I've experienced it after every deploy for 20 years. Automatically, my hand reaches for the &lt;code&gt;tail -f /var/log/nginx/access.log&lt;/code&gt; command, and I open the Cloudflare dashboard in my browser to check cache hit ratios and error logs. Even if everything appears fine, I remain vigilant for a while longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Symptoms of That "What If" Feeling
&lt;/h2&gt;

&lt;p&gt;This tension that arises after a deploy is a situation many of us are familiar with. Sometimes it manifests as a minor twitch, other times as a mild paranoia lasting hours. There are even times when I wake up in the middle of the night with an urge to check, wondering, "Did I forget something?"&lt;/p&gt;

&lt;p&gt;I don't just experience this process with large projects. Even on my own VPS, in an environment where I manage over 13 Docker containers, I feel this way after a simple configuration change. Despite everything being automated, one still thinks, "What if, just maybe?"&lt;/p&gt;

&lt;h3&gt;
  
  
  Past Painful Experiences and Triggers
&lt;/h3&gt;

&lt;p&gt;At the root of this "what if" feeling are, I believe, the painful experiences we've had in the past. Those moments are deeply etched in our brains and are triggered with every deploy. For me, some of these triggers are very clear.&lt;/p&gt;

&lt;p&gt;On my own VPS, I experienced this feeling most intensely on April 28th. I had deployed a new container, and the next morning, the Pipeline-health monitor sent a "DEGRADED" email. I saw the system was choked with &lt;code&gt;kcompactd %92 CPU&lt;/code&gt;; it couldn't even accept SSH connections. The helplessness at that moment, and the hours of debugging that followed, explain the reason for this tension.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Docker Disk Fire&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once, again on my own VPS, I experienced a Docker disk fire. The disk filled to 100% due to 33 GB of build cache and 23 GB of unused images. All my applications went down instantly, requiring urgent intervention. Such incidents are among the most significant reasons that reinforce that 'what if' feeling after a deploy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There were also times when my Astro build consumed 2.5 GB of RAM, pushing the system's 7.6 GB RAM to its limits and causing an OOM (Out Of Memory) error. Or the pain of deleting directories inside &lt;code&gt;_work/_temp&lt;/code&gt; on a GitHub Actions runner... All these scenarios have repeatedly shown me that a system can react unexpectedly. That's why, no matter how prepared I am, that meaningless stress lingers with me for a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Balance of Risk and Control
&lt;/h2&gt;

&lt;p&gt;This situation is essentially a reflection of risk management and the need for control. The uncertainty of what consequences our developed or managed systems might cause when they go live confronts us with this stress. Even if we use robust tests, automations, and monitoring tools, the "production" environment always holds its own surprises.&lt;/p&gt;

&lt;p&gt;Striking this balance—finding a way between the desire for fast deploys and the goal of risk-free deploys—is often challenging. Sometimes we compromise on certain controls to gain speed, and we might pay the price later. On the other hand, trying to perfect everything also slows down the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Coping Mechanisms
&lt;/h3&gt;

&lt;p&gt;Over the years, I've developed my own methods to cope with this stress. While it hasn't completely disappeared, I've managed to reduce its impact. Automation and comprehensive monitoring are at the forefront of these methods.&lt;/p&gt;

&lt;p&gt;I've set up automatic deploy processes with GitHub Actions. Every change is automatically pushed to production after passing tests. With Prometheus and Grafana, I monitor every corner of the system, and with Alertmanager, I receive instant notifications for anomalies. For pipeline reliability, I've specifically implemented preflight resource guards; these check if system resources are sufficient before a deploy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Small and Frequent Deploys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of large, monolithic deploys, I prefer small, atomic changes. This narrows the scope of a potential problem and makes rolling back much easier. When an issue arises, it becomes much simpler to pinpoint what changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rollback mechanisms are vitally important to me. When a deploy is found to be problematic, I need to be able to revert to the previous stable version with a single command. This sense of security somewhat alleviates that initial moment of stress. Furthermore, I'm not ashamed to make mistakes. Last month, when I wrote &lt;code&gt;sleep 360&lt;/code&gt; and got OOM-killed, I told myself, "this too was a lesson," and switched to a polling-wait mechanism. Learning from my self-created problems helps me be more careful in the next deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "It Happens" Philosophy and Acceptance
&lt;/h2&gt;

&lt;p&gt;Ultimately, a certain amount of risk and uncertainty is inherent in this line of work. There's no such thing as a perfect system; there can always be a vulnerability, a bug, or an unexpected interaction. Accepting this truth, embracing the "it happens" philosophy, reduces the pressure on me.&lt;/p&gt;

&lt;p&gt;Of course, this is not a state of complacency. On the contrary, it constantly pushes me to build better, more resilient, and more secure systems. There are times when I implement kernel module blacklists (like &lt;code&gt;algif_aead&lt;/code&gt; for CVE-2026-31431) as part of CVE mitigation; this is also part of the job. I learn from every mistake, every problem, and enter the next deploy better prepared.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ℹ️ Self-Hosted Runner Economics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To avoid exceeding GitHub Actions quotas, I use a self-hosted runner on my own VPS. This both reduces costs and gives me more control. However, it also brings its own operational overhead. Every decision has a trade-off.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This constant state of vigilance has, I suppose, become a part of my profession. Perhaps this situation is a source of motivation that drives us to build better systems. It's not about striving for perfection, but a continuous cycle of improvement and learning.&lt;/p&gt;

&lt;p&gt;Do you also have similar experiences after a deploy? How do you cope with that "what if" feeling? What's the first thing you do after a deploy? I'd love if you could share in the comments; perhaps we can learn from each other.&lt;/p&gt;

</description>
      <category>deploy</category>
      <category>stres</category>
      <category>psikoloji</category>
      <category>production</category>
    </item>
    <item>
      <title>The Hidden Dependency Hell of Cloud-Based Microservices</title>
      <dc:creator>Mustafa ERBAY</dc:creator>
      <pubDate>Sat, 09 May 2026 09:13:21 +0000</pubDate>
      <link>https://forem.com/merbayerp/the-hidden-dependency-hell-of-cloud-based-microservices-5g8m</link>
      <guid>https://forem.com/merbayerp/the-hidden-dependency-hell-of-cloud-based-microservices-5g8m</guid>
      <description>&lt;h2&gt;
  
  
  The Hidden Dependency Hell of Cloud-Based Microservices: A Guide to the Way Out
&lt;/h2&gt;

&lt;p&gt;Cloud-based microservice architectures have become an indispensable part of modern software development. While they offer benefits such as flexibility, scalability, and rapid development, the complexities they bring along also can't be overlooked. One of the most insidious and exhausting of these complexities is the situation we can call "hidden dependency hell."&lt;/p&gt;

&lt;p&gt;This situation arises when different services in the system develop invisible, vague, and hard-to-manage dependencies on each other. Although these dependencies aren't noticed at first, over time they erode the stability of the system, make debugging impossible, and turn adding new features into something close to torture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources of Hidden Dependencies in Microservices
&lt;/h3&gt;

&lt;p&gt;Hidden dependencies can find their way into microservice architectures for various reasons. Understanding these reasons is the first step to getting to the root of the problem and producing solutions. Typically, the pressure for rapid development, lack of documentation, or sloppy choice of communication protocols between services lead to these kinds of issues.&lt;/p&gt;

&lt;p&gt;Another important source is services becoming indirectly dependent on the inner workings of one another. For instance, one service might expect another service to return a specific piece of data in a specific format. If that format changes, an unexpected chain of errors can follow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ℹ️ What Is a Dependency?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the context of software development, a dependency is when a component (a service, a library, etc.) needs another component in order to function. This need can be a direct call, or it can occur through an indirect data flow or a shared resource.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Symptoms and Effects of Hidden Dependencies
&lt;/h3&gt;

&lt;p&gt;The presence of hidden dependencies usually shows itself when sudden and unexplained errors appear in the system. A small change in one service can cause big problems in unexpected places. This situation leads to a serious loss of motivation and a drop in productivity for the development teams.&lt;/p&gt;

&lt;p&gt;Such dependencies also negatively affect the overall stability of the system. The debugging process turns into something like a labyrinth because of the difficulty of finding which service caused the issue. New deployments come to be done with the worry of when the next big problem will erupt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Debugging Difficulty:&lt;/strong&gt; You have to inspect more than one service to find the source of the problem.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Slow Development Cycles:&lt;/strong&gt; It's hard to predict the possible effects of making a change.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Low System Stability:&lt;/strong&gt; Unexpected errors and crashes happen more often.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Increasing Operational Costs:&lt;/strong&gt; More resources are needed for troubleshooting and maintenance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Do You Get Out of This Hell? Paths to a Solution
&lt;/h3&gt;

&lt;p&gt;To escape hidden dependency hell, you have to take a proactive approach. This requires being careful in many areas, from architectural decisions all the way to daily development practices. One of the most effective methods is to make communication between services explicit and standardized.&lt;/p&gt;

&lt;p&gt;Using an API Gateway creates a centralized point of control by preventing services from communicating directly with each other. Thanks to this, dependencies between services become more visible and easier to manage. Additionally, event-driven architectures can prevent these kinds of problems by encouraging loose coupling between services.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 The Role of the API Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The API Gateway is a mediator that receives all client requests and ensures they're handled by the relevant services. Thanks to this, clients aren't aware of the architecture of the services and dependencies between services are managed better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Communication Patterns and Standardization
&lt;/h4&gt;

&lt;p&gt;How communication between microservices is done plays a critical role in managing dependencies. Using standardized communication protocols such as RESTful APIs and gRPC allows services to understand each other more easily. These standards help dependencies become more explicit and predictable.&lt;/p&gt;

&lt;p&gt;It's also important to clearly define the data formats and messaging schemas the services use. These definitions should be documented and versioned. That way, when one service changes a data format, the other services can adapt to the change or be made aware of it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Traceability and Observability
&lt;/h4&gt;

&lt;p&gt;Being able to monitor the behavior of all services in the system and the interactions among them is one of the most effective ways to detect hidden dependencies. &lt;code&gt;Observability&lt;/code&gt; tools such as centralized logging, distributed tracing, and metrics collection provide valuable information about the overall health of the system.&lt;/p&gt;

&lt;p&gt;Thanks to these tools, you can track how a request travels across multiple services, easily understand which service is causing latency or where the error started. This lets you quickly diagnose problems caused by hidden dependencies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ The Importance of Observability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Observability is a property that lets you understand the internal state of the system by observing it from outside. In microservice architectures, this property is vital for proactively detecting and solving problems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Other Methods of Managing Dependencies
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Injection:&lt;/strong&gt; Providing the dependencies a service needs from the outside makes services more independent.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Circuit Breaker Pattern:&lt;/strong&gt; When a service repeatedly fails, it prevents the system from crashing by blocking calls from other services to that service.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Service Discovery:&lt;/strong&gt; Lets services find each other dynamically, which helps reduce static dependencies.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Regular Refactoring:&lt;/strong&gt; It's important to regularly review the architecture and make improvements aimed at reducing dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion: Continuous Effort for Healthier Microservices
&lt;/h3&gt;

&lt;p&gt;The flexibility and speed brought by cloud-based microservices, when not managed properly, can lead to growing complexity and to problems like "hidden dependency hell." Escaping this hell isn't possible with a one-time fix; it's possible only through continuous effort.&lt;/p&gt;

&lt;p&gt;Making your architectural decisions carefully, standardizing inter-service communication, using &lt;code&gt;observability&lt;/code&gt; tools effectively, and regularly reviewing your system will help you reach healthier, more stable, and more manageable microservice architectures. Remember, a well-designed microservice architecture forms the foundation of your future growth and innovation.&lt;/p&gt;

</description>
      <category>life</category>
      <category>mikroservis</category>
      <category>bulut</category>
      <category>bamllk</category>
    </item>
  </channel>
</rss>
