Complete guide for deploying SonicJS AI to production on Cloudflare's edge platform.
- Overview
- Prerequisites
- Environment Configuration
- Wrangler Configuration
- D1 Database Setup
- R2 Bucket Setup
- KV Namespace Setup
- Environment Variables and Secrets
- Deployment Workflow
- Custom Domains and SSL
- Production Checklist
- Monitoring and Logging
- Rollback Procedures
- Performance Optimization
- CI/CD with GitHub Actions
- Troubleshooting
SonicJS AI runs on Cloudflare's global edge network, providing:
- Cloudflare Workers: Serverless application runtime at 300+ edge locations
- D1 Database: SQLite-based distributed database at the edge
- R2 Object Storage: S3-compatible object storage for media files
- KV Storage: Low-latency key-value storage for caching
- Zero cold starts: Instant response times globally
┌─────────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Workers │ │ D1 Database │ │ R2 Storage │ │
│ │ (Runtime) │──│ (SQLite) │ │ (Media) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ └───────────── KV Cache ────────────┘ │
└─────────────────────────────────────────────────────────┘
-
Cloudflare Account
- Sign up at https://dash.cloudflare.com/sign-up
- Workers Paid plan ($5/month minimum)
- Payment method on file
-
Domain Name (optional but recommended)
- Can use workers.dev subdomain for testing
- Custom domain for production recommended
# Node.js 18+ and npm
node --version # Should be v18.0.0 or higher
npm --version
# Wrangler CLI (Cloudflare Workers CLI)
npm install -g wrangler@latest
wrangler --version # Should be v3.0.0 or higher
# Git for version control
git --version# Install Wrangler globally
npm install -g wrangler@latest
# Login to Cloudflare
wrangler login
# This will:
# 1. Open browser for authentication
# 2. Save credentials locally
# 3. Verify account access
# Verify login
wrangler whoamiSonicJS AI supports three environments:
- Development (
dev) - Local development with local D1 - Preview (
preview) - Staging environment for testing - Production (
production) - Live production environment
┌─────────────┬──────────────┬─────────────┬──────────────┐
│ Environment │ Database │ R2 Bucket │ Domain │
├─────────────┼──────────────┼─────────────┼──────────────┤
│ dev │ local D1 │ dev bucket │ localhost │
│ preview │ shared D1 │ preview │ preview.dev │
│ production │ prod D1 │ prod bucket │ yourdomain │
└─────────────┴──────────────┴─────────────┴──────────────┘
The wrangler.toml file defines all environments and bindings.
name = "sonicjs-ai"
main = "src/index.ts"
compatibility_date = "2024-06-01"
compatibility_flags = ["nodejs_compat"]
# Build configuration
[build]
command = ""
[build.upload]
format = "modules"
[[build.upload.rules]]
type = "CompiledWasm"
globs = ["**/*.wasm"]
fallthrough = true
# Static assets binding
[assets]
directory = "./public"
binding = "ASSETS"
# Development environment variables
[vars]
ENVIRONMENT = "development"
# D1 Database binding (development)
[[d1_databases]]
binding = "DB"
database_name = "sonicjs-dev"
database_id = "874cad37-313c-4d71-97fa-ad7184526f5a"
# R2 bucket binding (development)
[[r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "sonicjs-media-dev"
# KV namespace binding (development)
[[kv_namespaces]]
binding = "CACHE_KV"
id = "a16f8246fc294d809c90b0fb2df6d363"
preview_id = "25360861fb2745fab3b1ef2f0f13ffc8"
# Preview environment
[env.preview]
name = "sonicjs-ai-preview"
[env.preview.vars]
ENVIRONMENT = "preview"
[[env.preview.d1_databases]]
binding = "DB"
database_name = "sonicjs-dev"
database_id = "874cad37-313c-4d71-97fa-ad7184526f5a"
[[env.preview.r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "sonicjs-media-preview"
[[env.preview.kv_namespaces]]
binding = "CACHE_KV"
id = "6fc451bee9bb412ebd1d325723779c9c"
preview_id = "38c54bd2b55643c097412fa3464c2ddd"
# Production environment
[env.production]
name = "sonicjs-ai-prod"
[env.production.vars]
ENVIRONMENT = "production"
[[env.production.d1_databases]]
binding = "DB"
database_name = "sonicjs-ai"
database_id = "583c089c-1a4a-477d-9d58-06c07bf7c1d7"
[[env.production.r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "sonicjs-media-prod"
[[env.production.kv_namespaces]]
binding = "CACHE_KV"
id = "7171ca98640e43b6b33dbff516a5a6cf"
preview_id = "aed99543752d438b8051a0b6809ced10"| Binding | Purpose | Type |
|---|---|---|
DB |
Main database | D1 Database |
MEDIA_BUCKET |
Media/file storage | R2 Bucket |
CACHE_KV |
Cache storage | KV Namespace |
ASSETS |
Static files | Assets binding |
# Create a new D1 database for production
wrangler d1 create sonicjs-ai
# Output example:
# ✅ Successfully created DB 'sonicjs-ai'
#
# [[d1_databases]]
# binding = "DB"
# database_name = "sonicjs-ai"
# database_id = "583c089c-1a4a-477d-9d58-06c07bf7c1d7"Copy the output and update your wrangler.toml production section:
[[env.production.d1_databases]]
binding = "DB"
database_name = "sonicjs-ai"
database_id = "YOUR-DATABASE-ID-HERE"SonicJS uses Drizzle ORM for migrations. Apply all migrations to production:
# Apply migrations to production database
npm run db:migrate:prod
# Or manually with wrangler
wrangler d1 migrations apply DB --env production
# This will:
# 1. Read migration files from drizzle/migrations/
# 2. Apply them in order to production D1
# 3. Track applied migrations# List tables in production database
wrangler d1 execute sonicjs-ai --env production --command="SELECT name FROM sqlite_master WHERE type='table';"
# Expected tables:
# - users
# - collections
# - content
# - content_versions
# - media
# - api_tokens
# - plugins
# - plugin_routes
# - plugin_hooks
# - plugin_assets
# - plugin_activity_logCreate admin user and initial collections:
# Use the production seed script
wrangler d1 execute sonicjs-ai --env production --file=./scripts/seed-production-content.sql
# Or create a custom admin user
wrangler d1 execute sonicjs-ai --env production --command="
INSERT INTO users (id, email, username, first_name, last_name, role, is_active, created_at, updated_at)
VALUES (
'admin-' || lower(hex(randomblob(16))),
'admin@yourdomain.com',
'admin',
'Admin',
'User',
'admin',
1,
strftime('%s', 'now') * 1000,
strftime('%s', 'now') * 1000
);
"# For local development, use local D1
wrangler d1 execute sonicjs-dev --local --file=./drizzle/migrations/0000_happy_donald_blake.sql
# Apply all migrations locally
npm run db:migrate# Create R2 bucket for production media storage
wrangler r2 bucket create sonicjs-media-prod
# Verify creation
wrangler r2 bucket list
# Output:
# sonicjs-media-dev
# sonicjs-media-preview
# sonicjs-media-prodIf you need direct browser uploads:
# Create cors-config.json
cat > cors-config.json << 'EOF'
{
"CORSRules": [
{
"AllowedOrigins": ["https://yourdomain.com"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3600
}
]
}
EOF
# Apply CORS configuration
# Note: CORS configuration currently requires Cloudflare Dashboard
# Go to R2 > your-bucket > Settings > CORSFor programmatic access outside Workers:
# Go to Cloudflare Dashboard > R2 > Manage R2 API Tokens
# Create new API token with:
# - Permissions: Read & Write
# - Buckets: sonicjs-media-prod
# Save these securely:
# - Access Key ID
# - Secret Access Key
# - Endpoint URL: https://[account-id].r2.cloudflarestorage.com# In Cloudflare Dashboard:
# 1. Go to R2 > sonicjs-media-prod > Settings
# 2. Click "Connect Domain"
# 3. Enter: media.yourdomain.com
# 4. DNS records will be created automatically
# This allows public access to media files via:
# https://media.yourdomain.com/uploads/image.jpgRecommended folder structure:
sonicjs-media-prod/
├── uploads/ # User uploaded media
├── avatars/ # User avatar images
├── thumbnails/ # Generated thumbnails
├── documents/ # PDF and documents
└── temp/ # Temporary files (auto-cleanup)
# Create KV namespace for cache
wrangler kv:namespace create "CACHE_KV" --env production
# Output:
# ✅ Created namespace with id "7171ca98640e43b6b33dbff516a5a6cf"
# Add the following to your wrangler.toml:
# [[env.production.kv_namespaces]]
# binding = "CACHE_KV"
# id = "7171ca98640e43b6b33dbff516a5a6cf"
# Create preview namespace
wrangler kv:namespace create "CACHE_KV" --env production --preview
# Output:
# ✅ Created preview namespace with id "aed99543752d438b8051a0b6809ced10"Add the KV namespace IDs to production config:
[[env.production.kv_namespaces]]
binding = "CACHE_KV"
id = "7171ca98640e43b6b33dbff516a5a6cf"
preview_id = "aed99543752d438b8051a0b6809ced10"# List all KV namespaces
wrangler kv:namespace list
# Test write
wrangler kv:key put --binding CACHE_KV --env production "test" "Hello from production"
# Test read
wrangler kv:key get --binding CACHE_KV --env production "test"
# Delete test key
wrangler kv:key delete --binding CACHE_KV --env production "test"The cache plugin uses KV for:
- API response caching
- Collection metadata caching
- Content caching
- Admin dashboard stats
Cache keys follow the pattern:
cache:api:collections:all
cache:api:content-list:limit:50
cache:collection-content:blog-posts:limit:50
-
Public Variables (
[vars]in wrangler.toml)- Non-sensitive configuration
- Available at build time
- Examples: ENVIRONMENT, feature flags
-
Secrets (via
wrangler secret)- Sensitive data (passwords, API keys)
- Encrypted at rest
- Only available at runtime
- Examples: JWT_SECRET, API keys
# JWT Secret for authentication
# Generate a secure random string
openssl rand -base64 32 | wrangler secret put JWT_SECRET --env production
# Admin password for initial setup (optional)
echo "your-secure-admin-password" | wrangler secret put ADMIN_PASSWORD --env production
# R2 Access credentials (if using external access)
echo "your-r2-access-key-id" | wrangler secret put R2_ACCESS_KEY_ID --env production
echo "your-r2-secret-key" | wrangler secret put R2_SECRET_ACCESS_KEY --env production
# Email service credentials (if using email features)
echo "your-sendgrid-api-key" | wrangler secret put SENDGRID_API_KEY --env production
# Analytics/monitoring tokens (optional)
echo "your-sentry-dsn" | wrangler secret put SENTRY_DSN --env production# List all secrets (names only, not values)
wrangler secret list --env production
# Update a secret
echo "new-value" | wrangler secret put SECRET_NAME --env production
# Delete a secret
wrangler secret delete SECRET_NAME --env production
# Secrets from file
cat secret.txt | wrangler secret put SECRET_NAME --env production[env.production.vars]
ENVIRONMENT = "production"
# CDN_DOMAIN = "media.yourdomain.com"
# IMAGES_ACCOUNT_ID = "your-cloudflare-account-id"
# FEATURE_NEW_EDITOR = "true"
# RATE_LIMIT_REQUESTS = "100"
# RATE_LIMIT_WINDOW = "60"// In your Worker code
export default {
async fetch(request: Request, env: Env) {
// Public variables
const environment = env.ENVIRONMENT // "production"
// Secrets (encrypted)
const jwtSecret = env.JWT_SECRET
// Bindings
const db = env.DB
const bucket = env.MEDIA_BUCKET
const cache = env.CACHE_KV
// ...
}
}# 1. Start local development server
npm run dev
# This runs: wrangler dev
# - Uses local D1 database
# - Live reload on file changes
# - Access at: http://localhost:8787
# 2. Run tests
npm test
# 3. Build project
npm run build
# This runs: tsc && wrangler deploy --dry-run
# - Compiles TypeScript
# - Validates configuration
# - No actual deployment# Deploy to preview environment
wrangler deploy --env preview
# Output:
# ✅ Successfully deployed to preview
# URL: https://sonicjs-ai-preview.workers.dev
# Test the preview deployment
curl https://sonicjs-ai-preview.workers.dev/api/health# Run all tests
npm test
# Check TypeScript compilation
npm run build
# Verify migrations are up to date
wrangler d1 migrations list DB --env production# Option 1: Using predeploy script (runs tests + build)
npm run predeploy && npm run deploy
# Option 2: Direct deployment
wrangler deploy --env production
# Output:
# ✅ Successfully deployed to production
# URL: https://sonicjs-ai-prod.workers.dev
#
# Deployment details:
# - Version: 2024-01-15-abc123
# - Size: 2.4 MB
# - Upload time: 3.2s# Health check
curl https://sonicjs-ai-prod.workers.dev/api/health
# Expected response:
# {
# "status": "healthy",
# "timestamp": "2024-01-15T12:00:00.000Z",
# "schemas": ["users", "collections", "content", ...]
# }
# Test API endpoints
curl https://sonicjs-ai-prod.workers.dev/api/collections
# Check admin panel
curl -I https://sonicjs-ai-prod.workers.dev/admin# Deploy to development (default)
wrangler deploy
# Deploy to preview
wrangler deploy --env preview
# Deploy to production
wrangler deploy --env production
# Deploy with specific version
wrangler deploy --env production --name sonicjs-ai-prod-v1.0.0# List recent deployments
wrangler deployments list --env production
# Output:
# Created ID Version
# 2024-01-15 abc123def456 v1.2.3
# 2024-01-14 def456abc789 v1.2.2
# 2024-01-13 ghi789jkl012 v1.2.1
# View specific deployment
wrangler deployments view abc123def456 --env production# Option 1: Domain already in Cloudflare
# Go to Cloudflare Dashboard > Add site > Enter domain
# Option 2: Domain elsewhere
# - Add nameservers to your registrar
# - Wait for DNS propagation (up to 24 hours)- Go to Workers & Pages > sonicjs-ai-prod
- Click Triggers tab
- Click Add Custom Domain
- Enter:
yourdomain.com - Click Add Custom Domain
[env.production]
name = "sonicjs-ai-prod"
routes = [
{ pattern = "yourdomain.com/*", zone_name = "yourdomain.com" },
{ pattern = "www.yourdomain.com/*", zone_name = "yourdomain.com" }
]Then deploy:
wrangler deploy --env productionCloudflare automatically creates DNS records when you add a custom domain.
Verify DNS records:
# Check DNS propagation
dig yourdomain.com
# Or using nslookup
nslookup yourdomain.comManual DNS configuration (if needed):
Type: AAAA
Name: @
Content: 100::
Proxy: Enabled (orange cloud)
Type: AAAA
Name: www
Content: 100::
Proxy: Enabled (orange cloud)
SSL is automatic with Cloudflare:
- Universal SSL: Issued automatically
- Edge Certificates: Free, auto-renewed
- Full (Strict): Recommended encryption mode
Configure SSL mode:
# Via Dashboard:
# SSL/TLS > Overview > Full (strict)Verify SSL:
# Check certificate
curl -vI https://yourdomain.com 2>&1 | grep -i "ssl\|tls"
# Or use SSL checker
# https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.comAdd redirect rule in Cloudflare:
# Via Dashboard:
# Rules > Page Rules > Create Page Rule
# URL: http://*yourdomain.com/*
# Setting: Always Use HTTPSOr add to Worker:
// In src/index.ts
app.use('*', async (c, next) => {
const url = new URL(c.req.url)
if (url.protocol === 'http:') {
url.protocol = 'https:'
return c.redirect(url.toString(), 301)
}
await next()
})[env.production]
routes = [
{ pattern = "yourdomain.com/*", zone_name = "yourdomain.com" },
{ pattern = "www.yourdomain.com/*", zone_name = "yourdomain.com" },
{ pattern = "app.yourdomain.com/*", zone_name = "yourdomain.com" },
{ pattern = "api.yourdomain.com/*", zone_name = "yourdomain.com" }
]Use this checklist before going live:
- Production D1 database created and migrated
- Production R2 bucket created
- Production KV namespace created
- All bindings configured in wrangler.toml
- Secrets uploaded (JWT_SECRET, etc.)
- Custom domain added and DNS configured
- SSL certificate active and valid
- All migrations applied successfully
- Initial data seeded (users, collections)
- Admin user created with secure password
- Database indexes verified
- Backup strategy in place
- Health check endpoint responding
- API endpoints working correctly
- Admin panel accessible
- Authentication system working
- Media upload functionality tested
- Cache system operational
- HTTPS enforced (no HTTP access)
- Strong JWT secret configured
- CORS properly configured
- Rate limiting enabled (if applicable)
- Security headers configured
- Admin password is strong and unique
- Cache headers configured
- Static assets compressed
- Database queries optimized
- KV cache working correctly
- R2 CDN domain configured (if needed)
- Error logging configured
- Performance monitoring enabled
- Uptime monitoring setup
- Alert notifications configured
- Log retention policy set
- Deployment process documented
- Environment variables documented
- Rollback procedure documented
- Team access configured
- Support contacts documented
- End-to-end tests passing
- Load testing completed
- Security scan performed
- Mobile responsiveness verified
- Cross-browser testing done
Access real-time analytics:
# Via Dashboard:
# Workers & Pages > sonicjs-ai-prod > AnalyticsKey metrics:
- Requests: Total requests and errors
- Success Rate: % of successful requests
- Duration: P50, P95, P99 response times
- CPU Time: Average CPU usage
- Errors: Error count and types
View real-time logs:
# Tail production logs
wrangler tail --env production
# Filter by status
wrangler tail --env production --status error
# Filter by request method
wrangler tail --env production --method POST
# Sample rate (10%)
wrangler tail --env production --sampling-rate 0.1SonicJS uses structured JSON logging:
// Example log format
console.log(JSON.stringify({
level: 'info',
message: 'Content created',
userId: 'user-123',
contentId: 'content-456',
timestamp: new Date().toISOString(),
requestId: crypto.randomUUID()
}))Set up external monitoring for the health endpoint:
# Health check endpoint
GET https://yourdomain.com/api/health
# Expected response:
{
"status": "healthy",
"timestamp": "2024-01-15T12:00:00.000Z",
"schemas": ["users", "collections", "content", "media", "plugins"]
}Recommended monitoring services:
- UptimeRobot: https://uptimerobot.com
- Pingdom: https://www.pingdom.com
- Better Uptime: https://betteruptime.com
Integration with error tracking services:
// Sentry integration example
import * as Sentry from '@sentry/cloudflare'
Sentry.init({
dsn: env.SENTRY_DSN,
environment: env.ENVIRONMENT,
tracesSampleRate: 0.1
})
// Capture errors
try {
// Application code
} catch (error) {
Sentry.captureException(error)
throw error
}Track custom metrics using Workers Analytics Engine:
// Track API usage
c.env.ANALYTICS.writeDataPoint({
blobs: ['api_request', endpoint],
doubles: [responseTime, statusCode],
indexes: [userId]
})Cloudflare Workers logs retention:
- Free Plan: Last 1 hour
- Paid Plan: Last 24 hours
- Enterprise: Customizable
For long-term storage, forward logs to:
- Cloudflare Logpush: S3, GCS, Azure
- Datadog: Real-time monitoring
- New Relic: Application performance
- Splunk: Log aggregation
# List recent deployments
wrangler deployments list --env production
# Output:
# Created ID Version
# 2024-01-15 abc123 v1.2.3 (current)
# 2024-01-14 def456 v1.2.2
# 2024-01-13 ghi789 v1.2.1# Option 1: Rollback to previous deployment
wrangler rollback --env production
# This will rollback to: v1.2.2
# Option 2: Rollback to specific version
wrangler rollback --message "Rolling back to v1.2.1" --env production --deployment-id ghi789
# Verify rollback
curl https://yourdomain.com/api/healthIf the new version included database migrations:
# 1. Export current database
wrangler d1 export sonicjs-ai --env production --output backup-before-rollback.sql
# 2. Rollback Worker deployment
wrangler rollback --env production
# 3. If needed, manually revert migrations
# Check which migrations to revert:
wrangler d1 migrations list DB --env production
# No automatic down migrations in D1
# Manual SQL required for schema changes#!/bin/bash
# emergency-rollback.sh
echo "Starting emergency rollback..."
# 1. Backup current state
echo "Creating backup..."
wrangler d1 export sonicjs-ai --env production --output "emergency-backup-$(date +%Y%m%d-%H%M%S).sql"
# 2. Rollback deployment
echo "Rolling back deployment..."
wrangler rollback --env production
# 3. Clear cache
echo "Clearing KV cache..."
wrangler kv:key list --binding CACHE_KV --env production | \
jq -r '.[].name' | \
xargs -I {} wrangler kv:key delete --binding CACHE_KV --env production {}
# 4. Verify health
echo "Verifying health..."
curl -f https://yourdomain.com/api/health || echo "❌ Health check failed"
echo "Rollback complete"For safer deployments, use gradual rollout:
# Deploy to preview first
wrangler deploy --env preview
# Test preview thoroughly
curl https://sonicjs-ai-preview.workers.dev/api/health
# If successful, deploy to production
wrangler deploy --env production
# Monitor for 5-10 minutes
wrangler tail --env production --status error
# If issues detected, rollback immediately
wrangler rollback --env productionSonicJS implements multi-layer caching:
// 1. Edge Cache (Cloudflare CDN)
// Static assets cached automatically
// 2. KV Cache (SonicJS Cache Plugin)
// API responses, collections, content
const cache = getCacheService(CACHE_CONFIGS.api!)
const cacheKey = cache.generateKey('collections', 'all')
const result = await cache.getWithSource(cacheKey)
// 3. D1 Prepared Statements
// Queries are compiled and cached
const stmt = db.prepare('SELECT * FROM collections WHERE is_active = 1')// Cache TTL settings in src/plugins/cache/index.ts
export const CACHE_CONFIGS = {
api: {
defaultTTL: 300, // 5 minutes
maxTTL: 3600, // 1 hour
staleWhileRevalidate: true
},
content: {
defaultTTL: 600, // 10 minutes
maxTTL: 86400 // 24 hours
}
}-- Create indexes for common queries
CREATE INDEX IF NOT EXISTS idx_content_status ON content(status);
CREATE INDEX IF NOT EXISTS idx_content_collection ON content(collection_id);
CREATE INDEX IF NOT EXISTS idx_content_slug ON content(slug);
CREATE INDEX IF NOT EXISTS idx_media_folder ON media(folder);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_collections_name ON collections(name);
-- Use prepared statements (automatic in Drizzle ORM)
-- Queries are compiled once and reused# Compress responses
[build]
command = "npm run build"
[build.upload]
format = "modules"
# Enable compression
[[build.upload.rules]]
type = "CompiledWasm"
globs = ["**/*.wasm"]
fallthrough = true# Configure custom domain for R2
# Dashboard > R2 > sonicjs-media-prod > Settings
# Add custom domain: media.yourdomain.com
# Benefits:
# - Global CDN caching
# - Reduced R2 egress costs
# - Faster media delivery
# - Custom cache rules# Check bundle size
wrangler deploy --dry-run --env production --outdir dist
# Optimize:
# 1. Remove unused dependencies
# 2. Use tree-shaking
# 3. Minimize imports
# 4. Lazy load routes
# Current size limit: 10 MB (free), 25 MB (paid)// Add timing headers
apiRoutes.use('*', async (c, next) => {
const startTime = Date.now()
await next()
const totalTime = Date.now() - startTime
c.header('X-Response-Time', `${totalTime}ms`)
})
// Parallel queries
const [collections, content] = await Promise.all([
db.select().from(collections),
db.select().from(content)
])Cloudflare Workers run at 300+ locations:
┌─────────────────────────────────────────┐
│ Request → Nearest Edge Location │
│ ↓ │
│ Worker executes locally │
│ ↓ │
│ D1 query → Nearest D1 location │
│ ↓ │
│ Response cached at edge │
└─────────────────────────────────────────┘
Result: <50ms response times globally
While SonicJS doesn't currently include GitHub Actions workflows, here's a recommended setup:
Create .github/workflows/deploy.yml:
name: Deploy to Cloudflare Workers
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
deploy-preview:
needs: test
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Deploy to Preview
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env preview
deploy-production:
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
environment:
name: production
url: https://yourdomain.com
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run database migrations
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: d1 migrations apply DB --env production
- name: Deploy to Production
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env production
- name: Verify deployment
run: |
curl -f https://yourdomain.com/api/health || exit 1Create .github/workflows/backup.yml:
name: Database Backup
on:
schedule:
# Daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
jobs:
backup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Wrangler
run: npm install -g wrangler@latest
- name: Backup Database
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
DATE=$(date +%Y%m%d-%H%M%S)
wrangler d1 export sonicjs-ai --env production --output "backup-$DATE.sql"
- name: Upload to S3
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Copy to S3
run: |
DATE=$(date +%Y%m%d-%H%M%S)
aws s3 cp "backup-$DATE.sql" "s3://your-backup-bucket/sonicjs-backups/"Add these secrets in GitHub Settings > Secrets:
CLOUDFLARE_API_TOKEN=your-cloudflare-api-token
CLOUDFLARE_ACCOUNT_ID=your-cloudflare-account-id
AWS_ACCESS_KEY_ID=your-aws-key (for backups)
AWS_SECRET_ACCESS_KEY=your-aws-secret (for backups)Generate Cloudflare API token:
- Go to Cloudflare Dashboard > My Profile > API Tokens
- Create Token > Custom Token
- Permissions:
- Account > Workers Scripts > Edit
- Account > D1 > Edit
- Zone > Workers Routes > Edit
Configure in GitHub Settings > Environments > production:
- Required reviewers: Add team members
- Wait timer: 5 minutes
- Deployment branches: Only
main
# Symptom
Error: D1_ERROR: no such table: users
# Solution
# Apply migrations
wrangler d1 migrations apply DB --env production
# Verify tables exist
wrangler d1 execute sonicjs-ai --env production --command="SELECT name FROM sqlite_master WHERE type='table';"# Symptom
Error: Uncaught ReferenceError: JWT_SECRET is not defined
# Solution
# List secrets
wrangler secret list --env production
# Add missing secret
echo "your-secret-value" | wrangler secret put JWT_SECRET --env production# Symptom
Error: R2 bucket 'sonicjs-media-prod' not found
# Solution
# Verify bucket exists
wrangler r2 bucket list
# Create if missing
wrangler r2 bucket create sonicjs-media-prod
# Verify binding in wrangler.toml
[[env.production.r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "sonicjs-media-prod"# Symptom
Error: KV namespace binding 'CACHE_KV' not found
# Solution
# List namespaces
wrangler kv:namespace list
# Create if missing
wrangler kv:namespace create "CACHE_KV" --env production
# Update wrangler.toml with the ID-- Add missing indexes
CREATE INDEX idx_content_status ON content(status);
CREATE INDEX idx_content_collection ON content(collection_id);
-- Analyze query performance
EXPLAIN QUERY PLAN SELECT * FROM content WHERE status = 'published';# Check Worker size
wrangler deploy --dry-run --env production
# Optimize bundle
# - Remove unused dependencies
# - Use dynamic imports
# - Minimize vendor code# Clear KV cache
wrangler kv:key list --binding CACHE_KV --env production
# Delete all cache keys
wrangler kv:key delete --binding CACHE_KV --env production "cache:api:collections:all"
# Verify cache headers
curl -I https://yourdomain.com/api/collections | grep -i cache# Check SSL status
curl -vI https://yourdomain.com 2>&1 | grep -i ssl
# Solutions:
# 1. Wait for certificate issuance (up to 24 hours)
# 2. Verify domain is proxied (orange cloud)
# 3. Check SSL/TLS mode is "Full (strict)"# Verify DNS
dig yourdomain.com
# Check Worker routes
wrangler routes list
# Verify route in wrangler.toml
[env.production]
routes = [
{ pattern = "yourdomain.com/*", zone_name = "yourdomain.com" }
]# Real-time logs
wrangler tail --env production
# Filter errors only
wrangler tail --env production --status error
# Filter by method
wrangler tail --env production --method POST
# Sample logs (10%)
wrangler tail --env production --sampling-rate 0.1- Cloudflare Community: https://community.cloudflare.com
- Discord: https://discord.gg/cloudflaredev
- Cloudflare Docs: https://developers.cloudflare.com/workers
- SonicJS Issues: https://github.com/lane711/sonicjs/issues
- Stack Overflow: Tag
cloudflare-workers
# Check Cloudflare status
https://www.cloudflarestatus.com
# Open support ticket
# Dashboard > Support > Contact Support
# Emergency: For production outages with paid plan- Getting Started Guide - Initial setup and local development
- Database Documentation - Schema design and migrations
- API Documentation - REST API endpoints and usage
- Configuration Guide - Environment variables and settings
- Cloudflare Workers Docs - Official platform docs
- Cloudflare D1 Docs - Database documentation
- Cloudflare R2 Docs - Object storage documentation
Quick reference checklist:
# Infrastructure Setup
□ D1 database created and migrated
□ R2 bucket created
□ KV namespace created
□ Secrets configured
□ Custom domain added
# Deployment
□ Tests passing
□ Build successful
□ Deployed to preview
□ Deployed to production
□ Health check passing
# Verification
□ API endpoints working
□ Admin panel accessible
□ Media uploads working
□ Cache functioning
□ SSL active
# Monitoring
□ Logs monitoring setup
□ Error tracking configured
□ Uptime monitoring active
□ Backup automation runningNeed help? Check the Troubleshooting section or reach out to the community.