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:
- NX caches builds based on file hashes and configuration
- Even with code changes committed, NX may return cached output if it thinks inputs haven't changed
- The cached build is deployed to Cloudflare, but your fixes aren't included
Symptoms
- Production crashes while localhost works perfectly
- Console shows different chunk names than expected (e.g.,
main-POEXQGIY.jsvsmain-JPJDTTZ2.js) - Wrangler says "0 files uploaded (X already uploaded)"
- Bug fixes you just committed aren't reflected in production
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:
- Production: Bound to a specific branch (e.g.,
deploy/docs) - Preview: All other branches (e.g.,
main)
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
- New deployment URL works:
https://abc123.gograb-docs.pages.dev/ - Custom domain still broken:
https://docs.gograb.space/ - Running
wrangler pages deployment listshows your deployment as "Preview" not "Production"
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
- New deployment URL works:
https://abc123.gograb-docs.pages.dev/ - Production URL still broken:
https://docs.gograb.space/ - Different console errors between URLs
The Fix
- Test the deployment-specific URL first (e.g.,
https://abc123.gograb-docs.pages.dev/) - Wait for propagation (usually 1-5 minutes)
- Hard refresh on production URL (Cmd+Shift+R)
- 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 |