Back to Journal
April 29, 2026
Rachmat Hidayat

Overcoming Build Deadlocks: Deploying 5000+ Markdown Files to Cloudflare Pages

Astro Cloudflare Infrastructure Performance

The Challenge of Massive Content Volumes

Building a world-class educational platform means dealing with a massive amount of content. Our platform, rhidayat.work, integrates over 5000+ interactive markdown files from sources like Exercism and freeCodeCamp into a seamless, edge-native Astro application.

While Astro’s content collections are incredibly powerful, pushing this volume of data through the build pipeline introduced several unique infrastructure challenges that standard tutorials don’t prepare you for.

Problem 1: The Vite Watcher Overflow

The first issue appeared during local development. Running bun run dev resulted in a severe MaxListenersExceededWarning and eventual socket crash. Vite was attempting to attach file watchers to every single one of the 5000+ content files, plus the hidden .agents and node_modules directories. The OS file watcher limits were immediately overwhelmed.

The Solution: We stabilized the local environment by dropping the strict Bun runtime (bun x --bun astro dev) in favor of standard Node execution (astro dev). More importantly, we aggressively tuned astro.config.mjs to ignore heavy directories:

server: {
  watch: {
    ignored: [
      '**/node_modules/**',
      '**/.agents/**',
      '**/.wrangler/**'
    ]
  }
}

Problem 2: The “ASSETS” Binding Conflict

During production builds, the @astrojs/cloudflare adapter automatically generates a wrangler.json file. However, it injects a generic ASSETS binding that Cloudflare Pages strictly reserves. This caused the deployment CLI to instantly reject the build with a configuration error.

The Solution: We authored an inline Vite closeBundle plugin (patchWranglerPlugin) that hooks into the end of the Astro build lifecycle. It actively scans the generated dist/server folder, parses the wrangler.json files, and strips the conflicting ASSETS key before the Wrangler CLI ever sees it.

Problem 3: The Esbuild Deadlock

The most difficult challenge was a silent build failure. The production pipeline would successfully build the server entrypoints, sync all 5000 files, and then completely hang during the Rearranging server assets... step, throwing a Go routine deadlock in esbuild.

This happens because the Cloudflare adapter attempts to bundle the massive, content-heavy server entrypoint into a single _worker.js/index.js file, exhausting esbuild’s concurrency limits.

The Solution: Instead of trying to force a monolithic SSR worker for a site that is 99% static, we utilized Astro’s prerendering capabilities. The build generates all 5000 pages statically into the dist/client directory.

We updated our custom deployment script (cloudflare-deploy.sh) to detect this crash state. If Astro fails to create the final worker but successfully generates the static client files, the script intercepts the deployment and directly uploads the dist/client directory to Cloudflare Pages.

To ensure the root domain doesn’t 404 without the SSR router, we injected a static _redirects file directly into the output directory before upload.

The Result

We now have an incredibly robust, automated deployment pipeline. The site is live at the edge, incredibly fast, and capable of handling thousands of localized educational modules without breaking a sweat.

Engineering excellence isn’t just about writing code; it’s about building resilient systems that gracefully handle extreme scale.