Cloudflare Pages Deployment
Complete guide for deploying to Cloudflare Pages
Developer Documentation
This page contains technical information for developers. For staff operational procedures, see the Customer Service section.
Overview
Both muse-customer (ncmuse.co) and muse-admin (staff.ncmuse.co) are deployed to Cloudflare Pages. This guide covers deployment procedures, common issues, and best practices.
Key Infrastructure:
- Platform: Cloudflare Pages + Workers
- Customer Portal: ncmuse.co
- Admin Portal: staff.ncmuse.co
- Database: Cloudflare D1
- Build Tool: Vite
Deployment Commands
Customer Portal (ncmuse.co)
cd muse-customer
rm -rf dist && npm run build
npx wrangler pages deploy dist --project-name=muse-customerAdmin Portal (staff.ncmuse.co)
cd muse-admin
rm -rf dist && npm run build
npx wrangler pages deploy dist --project-name=muse-adminAlways Clean Build
The rm -rf dist step is critical. Vite caches build artifacts, and deploying without cleaning will deploy stale code.
Critical: Clean Rebuild Before Deploy
Problem: Cached Build Artifacts
Symptom: After updating code, deployment still shows old content or demo data.
Root Cause: Vite caches build artifacts in the dist/ folder. If you deploy without rebuilding, you're deploying stale code.
Solution: Always clean rebuild before deployment:
# ❌ WRONG - Will deploy old code
npm run build
npx wrangler pages deploy dist
# ✅ CORRECT - Always clean first
rm -rf dist && npm run build
npx wrangler pages deploy distAutomated Deployment Script
Add this to your package.json:
{
"scripts": {
"deploy": "rm -rf dist && npm run build && npx wrangler pages deploy dist --project-name=PROJECT_NAME"
}
}Then deploy with:
npm run deployCustom Domain Propagation
Preview URLs vs Custom Domains
Every deployment gets a preview URL like:
https://[hash].muse-customer.pages.devhttps://[hash].muse-admin.pages.dev
These work immediately after deployment.
Custom domains like ncmuse.co and staff.ncmuse.co take 1-2 minutes to propagate.
Testing Workflow
Step 1: Test Preview URL First
# After deployment, Cloudflare shows:
✨ Deployment complete! Take a peek over at https://[hash].muse-customer.pages.dev
# Test this URL immediatelyStep 2: Wait for Custom Domain
- Wait 2-3 minutes for propagation
- Hard refresh browser (Cmd+Shift+R / Ctrl+Shift+R)
- Test in incognito window to avoid cache
Step 3: Verify Custom Domain
- Check ncmuse.co or staff.ncmuse.co
- Verify new changes are live
Don't Debug Too Early
If custom domain doesn't work immediately after deployment, wait 2-3 minutes before debugging. It's likely just propagation delay, not a real issue.
Environment Variables
Build-Time vs Runtime Variables
CRITICAL: Vite compiles environment variables at build time, not runtime.
# ❌ WRONG - Won't update deployed app
# Just updating .env and redeploying won't work
# ✅ CORRECT - Must rebuild
1. Update .env.production
2. rm -rf dist && npm run build
3. npx wrangler pages deploy distRequired Environment Variables
Both Portals:
VITE_STRIPE_PUBLISHABLE_KEY- Stripe frontend keyVITE_API_BASE_URL- API endpoint base URLVITE_SITE_URL- Current app URL
Cloudflare Secrets (Backend):
STRIPE_SECRET_KEY- Stripe backend keySTRIPE_WEBHOOK_SECRET- Webhook signing secretDB- D1 database binding
Setting Cloudflare Secrets
Secrets are set via command line:
# Set a secret
npx wrangler pages secret put SECRET_NAME --project-name=PROJECT_NAME
# List secrets
npx wrangler pages secret list --project-name=PROJECT_NAMESecrets Require Redeployment
After setting secrets, you must redeploy for them to take effect. Cloudflare Pages binds secrets at deploy time.
Database Migrations
Migration Workflow
Step 1: Add Database Binding (if new database)
In wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "your-database-name"
database_id = "[YOUR_DATABASE_ID]"Step 2: Create Migration SQL File
-- migrations/001_add_table.sql
CREATE TABLE IF NOT EXISTS new_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_at INTEGER DEFAULT (unixepoch())
);Step 3: Test Locally
# Test on local database first
npx wrangler d1 execute your-database-name --file=migrations/001_add_table.sqlStep 4: Run on Production
# Deploy to production with --remote flag
npx wrangler d1 execute your-database-name --file=migrations/001_add_table.sql --remoteStep 5: Verify Immediately
# Check the table was created
npx wrangler d1 execute your-database-name --command="SELECT * FROM new_table LIMIT 1" --remoteAlways Use --remote Flag for Production
Without --remote, migrations only run locally. Always verify which database you're modifying.
Deployment Checklist
Pre-Deployment
- [ ] Code reviewed and tested locally
- [ ] All tests passing
- [ ] Environment variables updated (if needed)
- [ ] Database migrations prepared (if needed)
- [ ] No secrets or API keys in code
During Deployment
- [ ] Clean rebuild:
rm -rf dist - [ ] Build completes without errors
- [ ] Correct project name specified
- [ ] Deployment shows success message
Post-Deployment
- [ ] Test preview URL immediately
- [ ] Verify key functionality works
- [ ] Wait 2-3 minutes for custom domain
- [ ] Hard refresh and test custom domain
- [ ] Check database changes applied (if applicable)
- [ ] Monitor for errors in Cloudflare dashboard
Common Issues
Issue: Old Code Still Showing
Cause: Didn't clean dist/ folder before rebuilding.
Fix:
rm -rf dist && npm run build && npx wrangler pages deploy distIssue: Custom Domain Not Working
Cause: Propagation delay (1-2 minutes).
Fix: Wait 2-3 minutes, hard refresh, test in incognito.
Issue: Environment Variables Not Updating
Cause: Vite bakes variables at build time.
Fix:
- Update
.env.production - Clean rebuild
- Redeploy
Issue: Database Changes Not Applied
Cause: Forgot --remote flag.
Fix: Run migration again with --remote:
npx wrangler d1 execute DB_NAME --file=migration.sql --remoteIssue: Secrets Not Working
Cause: Secrets require redeployment to bind.
Fix:
- Set secret:
npx wrangler pages secret put SECRET_NAME - Redeploy application
Multi-App Architecture
Why Two Separate Apps?
Muse & Co uses a dual-app architecture:
- muse-customer - Customer-facing portal (ncmuse.co)
- muse-admin - Staff/admin portal (staff.ncmuse.co)
Benefits:
- Smaller bundle sizes (no admin code in customer app)
- Independent deployments (update one without affecting other)
- Better security (admin functions isolated)
- Faster builds and deploys
Shared Resources
Both apps share:
- Same D1 database (different bindings)
- Stripe integration
- Email service (centralized in muse-customer)
Cross-App APIs
Admin app calls customer app APIs for shared functionality (like email sending). This requires CORS configuration:
// In API endpoint
const allowedOrigins = [
'https://ncmuse.co',
'https://staff.ncmuse.co',
'https://localhost:5173', // Dev
];nodejs_compat Flag (Critical)
Next.js 14+ Requirement
MANDATORY: All Cloudflare Pages projects using Next.js 14+ must have the nodejs_compat flag.
In wrangler.toml:
compatibility_flags = ["nodejs_compat"]Without this flag:
- Deployments fail with cryptic errors
- Node.js built-ins don't work
- Serverless functions break
Don't Deploy Without nodejs_compat
This flag is absolutely required for Next.js 14+. Deployments will fail without it.
Best Practices
1. Always Clean Build
rm -rf dist && npm run build2. Test Preview URL First
Don't expect custom domain to work immediately. Test the preview URL.
3. Use Deployment Scripts
Add "deploy" script to package.json to ensure consistent deployment.
4. Verify Database Migrations
Always run a SELECT query after migration to verify it worked.
5. Monitor Cloudflare Dashboard
Check for errors and performance issues after deployment.
6. Use Incognito for Testing
Avoid browser cache issues when verifying deployments.
7. Document Environment Changes
If you update environment variables, document what changed and why.
Related Resources
- Database Migrations - D1 migration patterns
- Environment Variables - Managing env vars
- Architecture - Why dual-app architecture
- Troubleshooting - Common deployment problems
Questions? See Troubleshooting Guide or ask a senior developer.
