Deployment Guide
Complete guide for deploying FlowCampaign to production environments, including Cloudflare Workers, traditional hosting, and containerized deployments.
Deployment Options
Option 1: Cloudflare Workers (Recommended)
Option 2: Docker Containers
Option 3: Traditional Server (Node.js)
Option 4: Serverless Platforms
Pre-Deployment Checklist
Environment Verification
# Verify system requirements
node --version # Should be 18.x or higher
npm --version # Should be 8.x or higher
git --version # Latest recommended
sqlite3 --version # 3.x required for development
# Verify Cloudflare account
wrangler whoami # Should show your account
Configuration Checklist
- Domain name registered and configured
- SSL certificates available
- Database provisioned (Turso, D1, or SQLite)
- Email provider credentials obtained
- API keys for external services
- Monitoring and alerting configured
- Backup strategy defined
- Disaster recovery plan in place
Option 1: Cloudflare Workers Deployment
Architecture Overview
┌─────────────────────────────────────────────┐
│ Cloudflare Edge │
├─────────────────────────────────────────────┤
│ FlowCampaign Worker (Hono) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ API Routes │ │ Static Assets │ │
│ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────┤
│ D1 Database │
│ or Turso (Edge Database) │
└─────────────────────────────────────────────┘
Step-by-Step Deployment
Step 1: Install Wrangler CLI
npm install -g wrangler
# or using pnpm
pnpm add -g wrangler
# Authenticate with Cloudflare
wrangler login
Step 2: Clone and Configure
git clone https://github.com/ns-software-solutions/flowcampaign.git
cd flowcampaign
# Install dependencies
npm install
# Create production configuration
cp .env.production.example .env.production
Step 3: Configure Environment Variables
# Edit production environment
nano .env.production
# Required variables
NODE_ENV=production
APP_URL=https://flowcampaign.yourdomain.com
DATABASE_URL=your-turso-database-url
JWT_SECRET=$(openssl rand -base64 32)
SESSION_SECRET=$(openssl rand -base64 32)
# Email providers
ZEPTOMAIL_API_KEY=your-zeptomail-api-key
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
Step 4: Set Up Database
Option A: Cloudflare D1 Database
# Create D1 database
wrangler d1 create flowcampaign-db
# Apply migrations
wrangler d1 execute flowcampaign-db --file=migrations/001_initial.sql
wrangler d1 execute flowcampaign-db --file=migrations/002_seed_data.sql
Option B: Turso Database
# Install Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash
# Create database
turso db create flowcampaign-prod
# Get connection string
turso db show flowcampaign-prod --url
# Create tables
turso db shell flowcampaign-prod < schema.sql
Step 5: Configure Wrangler
# Create wrangler.toml
cat > wrangler.toml << EOF
name = "flowcampaign"
main = "src/backend/index.ts"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
[env.production]
vars = {
NODE_ENV = "production",
APP_URL = "https://flowcampaign.yourdomain.com"
}
[[d1_databases]]
binding = "DB"
database_name = "flowcampaign-db"
database_id = "YOUR_D1_DATABASE_ID"
[build]
command = "npm run build"
upload = { format = "modules" }
[[build.upload.rules]]
type = "ESModule"
globs = ["**/*.js"]
EOF
Step 6: Set Secrets
# Set sensitive values as secrets
echo "your-jwt-secret" | wrangler secret put JWT_SECRET
echo "your-database-url" | wrangler secret put DATABASE_URL
echo "your-zeptomail-key" | wrangler secret put ZEPTOMAIL_API_KEY
Step 7: Build and Deploy
# Build the application
npm run build
# Deploy to Cloudflare Workers
wrangler deploy
Step 8: Configure Domain
# Add custom domain
wrangler routes list
wrangler routes create https://flowcampaign.yourdomain.com/*
# Or configure via Cloudflare Dashboard:
# 1. Go to Workers & Pages
# 2. Select your worker
# 3. Configure Custom Domain
Advanced Cloudflare Configuration
KV Namespace for File Storage
# Create KV namespace
wrangler kv:namespace create "FILES"
# Update wrangler.toml
[[kv_namespaces]]
binding = "FILES"
id = "KV_NAMESPACE_ID"
Rate Limiting
// Rate limiting configuration
const rateLimiting = {
analytics: {
enabled: true,
key: "ip_address",
limit: 1000,
window: 60 // seconds
}
};
Caching Configuration
# Cache configuration in wrangler.toml
[[cache]]
type = "durable_objects"
name = "CACHE"
class_name = "Cache"
[[migrations]]
tag = "v1"
new_classes = ["Cache"]
Option 2: Docker Deployment
Docker Compose Configuration
docker-compose.yml
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- REACT_APP_API_URL=http://backend:8787
depends_on:
- backend
volumes:
- ./uploads:/app/uploads
networks:
- flowcampaign-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8787:8787"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/flowcampaign
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
- ./uploads:/app/uploads
- ./logs:/app/logs
networks:
- flowcampaign-network
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=flowcampaign
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- flowcampaign-network
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis-data:/data
networks:
- flowcampaign-network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
- ./uploads:/var/www/uploads
depends_on:
- frontend
- backend
networks:
- flowcampaign-network
certbot:
image: certbot/certbot
volumes:
- ./ssl:/etc/letsencrypt
- ./webroot:/var/www/html
command: certonly --webroot -w /var/www/html -d flowcampaign.yourdomain.com --email admin@yourdomain.com --agree-tos --no-eff-email
volumes:
postgres-data:
redis-data:
networks:
flowcampaign-network:
driver: bridge
Dockerfile (Backend)
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY package-lock.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Build application
RUN npm run build
# Production image
FROM node:18-alpine
WORKDIR /app
# Copy built application
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
# Create uploads directory
RUN mkdir -p uploads logs
# Set permissions
RUN chown -R node:node /app
USER node
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:8787/health', (r) => {if(r.statusCode === 200) process.exit(0); process.exit(1)})"
EXPOSE 8787
CMD ["node", "dist/index.js"]
Dockerfile (Frontend)
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY package-lock.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build application
RUN npm run build
# Production image
FROM nginx:alpine
# Copy built files
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
# Create uploads directory
RUN mkdir -p /usr/share/nginx/html/uploads
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
# Backend upstream
upstream backend {
server backend:8787;
}
# Frontend upstream
upstream frontend {
server frontend:3000;
}
server {
listen 80;
server_name flowcampaign.yourdomain.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name flowcampaign.yourdomain.com;
# SSL certificates
ssl_certificate /etc/nginx/ssl/live/flowcampaign.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/flowcampaign.yourdomain.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
# Frontend
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Backend API
location /api {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# Static files
location /uploads {
alias /var/www/uploads;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Health checks
location /health {
proxy_pass http://backend/health;
access_log off;
}
}
}
Deployment Commands
# Build and start containers
docker-compose build
docker-compose up -d
# View logs
docker-compose logs -f
# Stop containers
docker-compose down
# Stop and remove volumes
docker-compose down -v
# Update containers
docker-compose pull
docker-compose up -d --build
Option 3: Traditional Server Deployment
Requirements
- Linux server (Ubuntu 22.04 LTS recommended)
- Node.js 18.x or higher
- PostgreSQL 14+ or MySQL 8+
- Redis 7+
- Nginx or Apache
Installation Steps
Step 1: Server Setup
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# Install Redis
sudo apt install -y redis-server
# Install Nginx
sudo apt install -y nginx
# Install PM2 for process management
sudo npm install -g pm2
Step 2: Database Setup
# Create PostgreSQL database
sudo -u postgres psql
CREATE DATABASE flowcampaign;
CREATE USER flowcampaign_user WITH ENCRYPTED PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE flowcampaign TO flowcampaign_user;
\q
# Test connection
psql -h localhost -U flowcampaign_user -d flowcampaign
Step 3: Application Setup
# Create application directory
sudo mkdir -p /var/www/flowcampaign
sudo chown -R $USER:$USER /var/www/flowcampaign
# Clone repository
cd /var/www/flowcampaign
git clone https://github.com/ns-software-solutions/flowcampaign.git .
git checkout production
# Install dependencies
npm ci --only=production
# Configure environment
cp .env.production.example .env.production
nano .env.production
Step 4: Configure Environment
# Production environment variables
NODE_ENV=production
APP_URL=https://flowcampaign.yourdomain.com
PORT=3000
# Database
DATABASE_URL=postgresql://flowcampaign_user:password@localhost:5432/flowcampaign
# Redis
REDIS_URL=redis://localhost:6379
# Email providers
ZEPTOMAIL_API_KEY=your-key-here
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
# Security
JWT_SECRET=$(openssl rand -base64 32)
SESSION_SECRET=$(openssl rand -base64 32)
# File uploads
UPLOAD_DIR=/var/www/flowcampaign/uploads
MAX_FILE_SIZE=10485760
Step 5: Database Migrations
# Run migrations
npm run db:migrate
# Seed initial data
npm run db:seed
Step 6: Build Application
# Build frontend and backend
npm run build
# Create uploads directory
mkdir -p uploads logs
chmod 755 uploads logs
Step 7: Configure PM2
# Create ecosystem file
cat > ecosystem.config.js << EOF
module.exports = {
apps: [{
name: 'flowcampaign',
script: 'dist/index.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: 'logs/err.log',
out_file: 'logs/out.log',
log_file: 'logs/combined.log',
time: true,
max_memory_restart: '1G'
}]
};
EOF
# Start application
pm2 start ecosystem.config.js
# Enable startup on boot
pm2 startup
pm2 save
Step 8: Configure Nginx
# Create Nginx site configuration
sudo nano /etc/nginx/sites-available/flowcampaign
server {
listen 80;
server_name flowcampaign.yourdomain.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name flowcampaign.yourdomain.com;
# SSL configuration (update with your certificate paths)
ssl_certificate /etc/letsencrypt/live/flowcampaign.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/flowcampaign.yourdomain.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Root directory
root /var/www/flowcampaign/dist/public;
# Proxy to Node.js application
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static files
location /uploads {
alias /var/www/flowcampaign/uploads;
expires 30d;
add_header Cache-Control "public, immutable";
}
# API endpoints
location /api {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Health checks
location /health {
proxy_pass http://localhost:3000/health;
access_log off;
}
}
# Enable site
sudo ln -s /etc/nginx/sites-available/flowcampaign /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Step 9: SSL Certificate (Let's Encrypt)
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d flowcampaign.yourdomain.com
# Auto-renewal
sudo certbot renew --dry-run
Option 4: Serverless Platforms
Vercel Deployment
vercel.json
{
"version": 2,
"builds": [
{
"src": "src/backend/index.ts",
"use": "@vercel/node",
"config": { "includeFiles": ["dist/**"] }
}
],
"routes": [
{
"src": "/api/(.*)",
"dest": "src/backend/index.ts"
},
{
"src": "/(.*)",
"dest": "dist/frontend/index.html"
}
],
"env": {
"NODE_ENV": "production",
"DATABASE_URL": "@database_url",
"JWT_SECRET": "@jwt_secret"
}
}
Deployment Commands
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel --prod
# Set environment variables
vercel env add DATABASE_URL production
vercel env add JWT_SECRET production
AWS Lambda Deployment
serverless.yml
service: flowcampaign
provider:
name: aws
runtime: nodejs18.x
region: us-east-1
environment:
NODE_ENV: production
DATABASE_URL: ${env:DATABASE_URL}
JWT_SECRET: ${env:JWT_SECRET}
functions:
api:
handler: dist/index.handler
events:
- http:
path: /{proxy+}
method: any
timeout: 30
memorySize: 1024
plugins:
- serverless-offline
Deployment Commands
# Install Serverless Framework
npm install -g serverless
# Deploy
serverless deploy
# Set environment variables
serverless config credentials --provider aws --key YOUR_KEY --secret YOUR_SECRET
Monitoring & Maintenance
Health Checks
# Create health check script
cat > check-health.sh << 'EOF'
#!/bin/bash
# Check application
curl -f http://localhost:3000/health || exit 1
# Check database
psql -c "SELECT 1" || exit 1
# Check disk space
df -h / | awk 'NR==2 {if ($5 > 90) exit 1}'
# Check memory
free -m | awk 'NR==2 {if ($4 < 100) exit 1}'
echo "All checks passed"
EOF
chmod +x check-health.sh
Log Management
# Configure log rotation
sudo nano /etc/logrotate.d/flowcampaign
/var/www/flowcampaign/logs/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 644 www-data www-data
postrotate
pm2 reload flowcampaign --update-env
endscript
}
Backup Strategy
Database Backup
# Backup script
cat > backup-db.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/var/backups/flowcampaign"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="flowcampaign_db_$DATE.sql"
mkdir -p $BACKUP_DIR
# PostgreSQL backup
pg_dump -U flowcampaign_user flowcampaign > $BACKUP_DIR/$FILENAME
# Compress
gzip $BACKUP_DIR/$FILENAME
# Keep only last 30 days
find $BACKUP_DIR -name "flowcampaign_db_*.sql.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/$FILENAME.gz"
EOF
chmod +x backup-db.sh
# Schedule daily backup
(crontab -l 2>/dev/null; echo "0 2 * * * /var/www/flowcampaign/backup-db.sh") | crontab -
File Backup
# Backup uploads directory
rsync -avz --delete /var/www/flowcampaign/uploads/ backup-server:/backups/flowcampaign/uploads/
Performance Monitoring
PM2 Monitoring
# Monitor application
pm2 monit
pm2 logs
# Status check
pm2 status
pm2 show flowcampaign
# Metrics
pm2 metrics
External Monitoring
- Uptime Robot: Website availability
- Datadog: Application performance monitoring
- Sentry: Error tracking
- Google Analytics: User behavior tracking
Scaling Strategies
Horizontal Scaling
// Load balancer configuration
const scalingConfig = {
instances: {
min: 2,
max: 10,
scaling_rules: {
cpu: { threshold: 70, period: 300 },
memory: { threshold: 80, period: 300 },
requests: { threshold: 1000, period: 60 }
}
}
};
Database Scaling
- Read Replicas: For read-heavy workloads
- Connection Pooling: PgBouncer for PostgreSQL
- Caching Layer: Redis for frequent queries
- Sharding: For very large datasets
File Storage Scaling
- CDN Integration: Cloudflare CDN for static assets
- Object Storage: S3/R2 for file uploads
- Image Optimization: On-the-fly resizing
Security Hardening
Firewall Configuration
# Configure UFW firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
SSL/TLS Configuration
# Strong SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
Regular Updates
# Update schedule
(crontab -l 2>/dev/null; echo "0 4 * * 0 sudo apt update && sudo apt upgrade -y") | crontab -
(crontab -l 2>/dev/null; echo "0 5 * * 0 npm update -g") | crontab -
Troubleshooting Deployment
Common Issues
1. Port Already in Use
# Find process using port
sudo lsof -i :3000
# Kill process
sudo kill -9 PID
2. Database Connection Failed
# Check PostgreSQL status
sudo systemctl status postgresql
# Test connection
psql -h localhost -U flowcampaign_user -d flowcampaign
# Check logs
sudo journalctl -u postgresql -f
3. Memory Issues
# Check memory usage
free -h
top -o %MEM
# Increase PM2 memory
pm2 restart flowcampaign --max-memory-restart 1G
4. SSL Certificate Issues
# Check certificate
sudo certbot certificates
# Renew certificate
sudo certbot renew
# Debug SSL
openssl s_client -connect flowcampaign.yourdomain.com:443
Debug Commands
# Check application logs
tail -f /var/www/flowcampaign/logs/combined.log
# Check Nginx logs
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
# Check system resources
htop
iotop -o
iftop
Rollback Procedures
Quick Rollback
# Stop current version
pm2 stop flowcampaign
# Restore previous version
cd /var/www/flowcampaign
git checkout HEAD~1
# Restart
npm ci
npm run build
pm2 start flowcampaign
Database Rollback
# Restore from backup
psql -U flowcampaign_user -d flowcampaign < backup.sql
# Or restore specific tables
pg_restore -U flowcampaign_user -d flowcampaign -t campaigns backup.dump
Deployment Support: For deployment assistance, contact deploy@nssoftwaresolutions.in
Production Checklist: Ensure all items in the pre-deployment checklist are completed before going live.
FlowCampaign is designed for reliable, scalable deployment across various environments, from edge computing platforms to traditional servers.