Skip to content

Production Deployment

Complete guide to deploying ImageBot to production on Cloudflare’s edge infrastructure.

  • Cloudflare account (free tier works)
  • Domain name (optional but recommended)
  • Wrangler CLI installed (npm install -g wrangler)
  • Git repository access
┌─────────────────────────────────────────────┐
│ Cloudflare Pages (Frontend) │
│ https://your-site.pages.dev │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Cloudflare Worker (API Backend) │
│ https://imagebot-worker.workers.dev │
└─────────────────────────────────────────────┘
┌───────────┬──────────┬─────────┐
│ D1 (DB) │ R2 │ AI │
│ SQLite │ Storage │ Models │
└───────────┴──────────┴─────────┘
Terminal window
# Create production database
wrangler d1 create imagebot-db-prod
# Copy the database_id from output
# Example output:
# database_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

Update wrangler.toml:

[[d1_databases]]
binding = "DB"
database_name = "imagebot-db-prod"
database_id = "your-database-id-here" # ← Paste your ID
Terminal window
# Create production storage bucket
wrangler r2 bucket create imagebot-images-prod
# Verify creation
wrangler r2 bucket list

Update wrangler.toml:

[[r2_buckets]]
binding = "IMAGES"
bucket_name = "imagebot-images-prod"

1.3 Configure Custom Domain for R2 (Optional)

Section titled “1.3 Configure Custom Domain for R2 (Optional)”
Terminal window
# Add custom domain to R2 bucket
wrangler r2 bucket domain add imagebot-images-prod --domain images.yourdomain.com
# Verify domain
wrangler r2 bucket domain list imagebot-images-prod

Benefits:

  • SEO-friendly image URLs
  • Brand consistency
  • Better caching control

Set production secrets using Wrangler:

Terminal window
# JWT Secret (generate strong random string)
wrangler secret put JWT_SECRET
# Paste: (output of: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))")
# Stripe Keys (if using payment features)
wrangler secret put STRIPE_SECRET_KEY
wrangler secret put STRIPE_WEBHOOK_SECRET
# Email Service (MailerSend for notifications)
wrangler secret put MAILERSEND_API_TOKEN
# GitHub OAuth (if using GitHub login)
wrangler secret put GITHUB_CLIENT_ID
wrangler secret put GITHUB_CLIENT_SECRET

Generate JWT Secret:

Terminal window
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

Apply all database schemas to production:

Terminal window
# Core schema
wrangler d1 execute imagebot-db-prod --remote --file database/schema-init.sql
# Feature schemas (in order)
wrangler d1 execute imagebot-db-prod --remote --file database/license-certificates-schema.sql
wrangler d1 execute imagebot-db-prod --remote --file database/multi-model-comparison-schema.sql
wrangler d1 execute imagebot-db-prod --remote --file database/api-keys-schema.sql
wrangler d1 execute imagebot-db-prod --remote --file database/teams-minimal.sql
wrangler d1 execute imagebot-db-prod --remote --file database/batch-schema.sql
wrangler d1 execute imagebot-db-prod --remote --file database/branding-schema.sql
wrangler d1 execute imagebot-db-prod --remote --file database/prompt-packs-schema.sql

Verify Database:

Terminal window
# Check table count (should be 41+)
wrangler d1 execute imagebot-db-prod --remote --command "SELECT COUNT(*) as table_count FROM sqlite_master WHERE type='table'"
# List all tables
wrangler d1 execute imagebot-db-prod --remote --command "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"

Critical: Deploy Worker BEFORE frontend to ensure API is available.

Terminal window
# Build and deploy Worker
npm run worker:deploy
# Or with explicit environment
wrangler deploy
# Verify deployment
curl https://imagebot-worker.your-subdomain.workers.dev/api/health

Expected Response:

{
"status": "ok",
"timestamp": "2024-01-15T10:30:00Z",
"database": "connected",
"r2": "connected"
}
Terminal window
# Add custom domain to Worker
wrangler publish --routes "api.yourdomain.com/*"

Update DNS:

  • Type: CNAME
  • Name: api
  • Value: imagebot-worker.your-subdomain.workers.dev
  • Proxy: Enabled (orange cloud)

Update .env.production:

Terminal window
PUBLIC_API_URL=https://imagebot-worker.your-subdomain.workers.dev
# Or if using custom domain:
# PUBLIC_API_URL=https://api.yourdomain.com

Build:

Terminal window
npm run build
Terminal window
# Deploy via Wrangler
wrangler pages deploy dist --project-name=imagebot
# Or via Git integration (recommended)
git push origin main

Git Integration Setup:

  1. Go to Cloudflare Dashboard → Pages
  2. Click “Create a project”
  3. Connect to GitHub repository
  4. Configure build settings:
    • Build command: npm run build
    • Build output directory: dist
    • Environment variables: Add PUBLIC_API_URL
  1. Pages → Settings → Custom domains
  2. Click “Set up a custom domain”
  3. Enter your domain (e.g., app.yourdomain.com)
  4. Update DNS:
    • Type: CNAME
    • Name: app
    • Value: imagebot.pages.dev
    • Proxy: Enabled

In worker/index.ts, configure allowed origins:

const corsHeaders = {
'Access-Control-Allow-Origin': 'https://app.yourdomain.com', // Your Pages domain
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};

Update wrangler.toml:

[vars]
RATE_LIMIT_PER_MINUTE = "100"
RATE_LIMIT_BURST = "200"

Cloudflare automatically tracks:

  • Request volume
  • Response times
  • Error rates
  • Geographic distribution

View at: Dashboard → Workers → Analytics

Cloudflare provides automatic SSL certificates:

  1. Dashboard → SSL/TLS → Overview
  2. Set encryption mode: Full (strict)
  3. Enable:
    • Always Use HTTPS
    • Automatic HTTPS Rewrites
    • Minimum TLS Version: 1.2

Configure cache rules for R2 images:

# In wrangler.toml
[env.production.routes]
pattern = "*/generated/*"
cache_ttl = 31536000 # 1 year

Reduces latency by up to 30%:

  • Dashboard → Traffic → Argo
  • Enable Argo Smart Routing
  • Cost: $0.10 per GB

Create page rules for static assets:

  • Pattern: app.yourdomain.com/*.js
  • Settings:
    • Cache Level: Cache Everything
    • Edge Cache TTL: 1 month
    • Browser Cache TTL: 1 week
Terminal window
# Tail Worker logs
wrangler tail imagebot-worker
# Filter errors only
wrangler tail imagebot-worker --status error

Dashboard → Account → Notifications:

  • Worker error rate threshold
  • Worker CPU time exceeded
  • R2 storage quota
  • D1 request quota

Add analytics to frontend:

src/lib/analytics.ts
export function trackEvent(event: string, properties?: Record<string, any>) {
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', event, properties);
}
}
Terminal window
# Export database
wrangler d1 export imagebot-db-prod --output backup-$(date +%Y%m%d).sql
# Automated backup script
#!/bin/bash
DATE=$(date +%Y%m%d)
wrangler d1 export imagebot-db-prod --output "backups/backup-$DATE.sql"
echo "Backup completed: backup-$DATE.sql"

Set up daily backups via cron job.

Terminal window
# List all objects
wrangler r2 object list imagebot-images-prod
# Download specific object
wrangler r2 object get imagebot-images-prod/generated/img_abc123.png --file=backup.png

Consider using R2 to S3 replication for redundancy.

Before going live:

  • All environment secrets configured
  • Database migrations applied
  • Worker deployed and health check passes
  • Frontend deployed to Pages
  • Custom domains configured (if using)
  • SSL/TLS set to Full (strict)
  • CORS configured correctly
  • Rate limiting enabled
  • Analytics enabled
  • Monitoring alerts configured
  • Backup strategy implemented
  • Load testing completed
  • Security audit completed
  • Documentation updated

If deployment fails:

Terminal window
# Rollback Worker to previous version
wrangler rollback
# Rollback Pages deployment
# Go to Pages → Deployments → Previous deployment → "Rollback to this deployment"

Cloudflare Free Tier:

  • Workers: 100,000 requests/day
  • Pages: Unlimited bandwidth
  • D1: 5GB storage, 5M reads/day
  • R2: 10GB storage, 1M reads/month
  • AI: 10,000 neurons/day

Expected Monthly Costs (beyond free tier):

  • Workers: $5/10M requests
  • R2: $0.015/GB storage
  • D1: $0.50/GB storage
  • AI: $0.01/1,000 neurons

Estimated cost for 10K users: $20-50/month

  • Cloudflare automatically scales Workers globally
  • No configuration needed for geographic distribution
  • Add indexes for frequently queried columns
  • Use prepared statements for performance
  • Monitor D1 query times via analytics
  • Enable R2 lifecycle policies
  • Implement image compression
  • Use CDN caching aggressively

Common production issues:

Worker errors:

Terminal window
wrangler tail imagebot-worker --status error

Database connection issues:

  • Verify database_id in wrangler.toml
  • Check D1 quota limits
  • Review D1 analytics for slow queries

CORS errors:

  • Verify allowed origins match Pages domain
  • Check preflight OPTIONS handling

Rate limiting:

  • Review Worker analytics for throttled requests
  • Adjust rate limits in wrangler.toml