Cloudflare Deployment Pitfalls & Best Practices

Critical lessons learned from production deployment failures

⚠️ Cloudflare Deployment Pitfalls & Best Practices

Hard-won lessons from production deployment failures. Read this before deploying.


🚨 Critical Issue: NX Cache Serving Stale Builds

The Problem

When deploying to Cloudflare, the NX build system may serve cached build artifacts that don't include your recent code changes. This happens because:

  1. NX caches builds based on file hashes and configuration
  2. Even with code changes committed, NX may return cached output if it thinks inputs haven't changed
  3. The cached build is deployed to Cloudflare, but your fixes aren't included

Symptoms

The Fix

ALWAYS clear caches before production deployment:

# Step 1: Clear NX cache
nx reset

# Step 2: Remove dist folder
rm -rf dist/apps/docs-angular

# Step 3: Build with --skip-nx-cache flag
yarn nx build docs-angular --configuration=production --skip-nx-cache

# Step 4: Deploy
npx wrangler pages deploy ./dist/apps/docs-angular/browser \
  --project-name=gograb-docs \
  --branch=main \
  --commit-dirty=true

Recommended: Update deploy script

Add this to tools/scripts/deploy-docs.sh:

# CRITICAL: Clear caches to ensure fresh build
echo "🧹 Clearing NX cache..."
nx reset
rm -rf dist/apps/docs-angular

# Build with cache bypass
yarn nx build docs-angular --configuration=production --skip-nx-cache

🚨 Critical Issue: Wrong Branch = Preview Only

The Problem

Cloudflare Pages uses branch-based environments:

If you deploy to main branch, your deployment goes to Preview only. The custom domain (docs.gograb.space) won't update because it's bound to Production.

Symptoms

The Fix

Deploy to the production branch (deploy/docs for docs-angular):

npx wrangler pages deploy ./dist/apps/docs-angular/browser \
  --project-name=gograb-docs \
  --branch=deploy/docs \
  --commit-dirty=true

How to Check Production Branch

npx wrangler pages deployment list --project-name=gograb-docs | head -10
# Look for "Production" environment - note which branch it uses

🚨 Critical Issue: Cloudflare Edge Caching

The Problem

Even after successful deployment, Cloudflare's edge CDN may serve cached content for some time. The custom domain (docs.gograb.space) may lag behind the pages.dev URL.

Symptoms

The Fix

  1. Test the deployment-specific URL first (e.g., https://abc123.gograb-docs.pages.dev/)
  2. Wait for propagation (usually 1-5 minutes)
  3. Hard refresh on production URL (Cmd+Shift+R)
  4. If still issues, purge cache via Cloudflare dashboard

🔧 Best Practices for Docs-Angular Deployment

Pre-Deployment Checklist

# 1. Verify your changes are committed
git status
git log --oneline -3 -- apps/docs-angular/

# 2. Check for the specific fix in source files
grep -l "your_fix_identifier" apps/docs-angular/src/app/**/*.ts

# 3. Clear all caches
nx reset && rm -rf dist/apps/docs-angular

# 4. Fresh build
yarn nx build docs-angular --configuration=production --skip-nx-cache

# 5. Verify fix is in build output
grep -r "your_fix_identifier" dist/apps/docs-angular/browser/*.js

# 6. Deploy
yarn deploy:docs

Post-Deployment Verification

# 1. Test deployment-specific URL
curl -s https://NEW_HASH.gograb-docs.pages.dev/ | grep -o 'main-[A-Z0-9]*.js'

# 2. Compare with local build
ls dist/apps/docs-angular/browser/main-*.js

# 3. If hashes don't match, something is wrong

🐛 Common Bugs to Watch For

1. Angular Effect Loops

Pattern: Using effect() with side effects that trigger the effect again.

// ❌ BAD - Can cause infinite loops
effect(() => {
  const message = this.loadingService.currentMessage(); // Changes frequently
  this.loadingService.showOverlay({ subtitle: message }); // Triggers more changes
});

// ✅ GOOD - Track state to prevent re-entry
private overlayShown = signal(false);
effect(() => {
  const isLoading = this.loadingService.isLoading();
  if (isLoading && !this.overlayShown()) {
    this.overlayShown.set(true);
    this.loadingService.showOverlay({...});
  } else if (!isLoading && this.overlayShown()) {
    this.overlayShown.set(false);
    this.loadingService.hideOverlay();
  }
});

2. Async Effect Timing

Pattern: Setting state AFTER async operation instead of BEFORE.

// ❌ BAD - Multiple loads can happen
effect(() => {
  if (currentPath !== this.lastLoadedPath) {
    this.loadContent(currentPath).then(() => {
      this.lastLoadedPath = currentPath; // Too late!
    });
  }
});

// ✅ GOOD - Set before async to prevent duplicate calls
effect(() => {
  if (currentPath !== this.lastLoadedPath) {
    this.lastLoadedPath = currentPath; // Lock immediately
    this.loadContent(currentPath);
  }
});

3. Instance vs Static Caches

Pattern: Caching in component instance that gets destroyed.

// ❌ BAD - Cache lost when component is destroyed/recreated
private cache = new Map();

// ✅ GOOD - Static cache persists across instances
private static cache = new Map();

📋 MCP Decisions to Log

When MCP is available, log these decisions:

Type Question Chosen Reasoning
deployment How to deploy docs-angular safely Clear NX cache + skip-nx-cache flag NX cache can serve stale builds even with committed changes
architecture Where to put content caches Static class properties Instance caches are lost on component destroy/recreate
bugfix How to fix Angular effect loops Track state with separate signal Prevents re-entry when side effects trigger the effect

🔗 Related Documents