Skip to main content

Deployment Guide

Complete guide for deploying FlowCampaign to production environments, including Cloudflare Workers, traditional hosting, and containerized deployments.

Deployment Options

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.