Complete guide to deploying Mobile-WP to production.
- Overview
- VPS Requirements
- Initial Setup
- Production Deployment
- SSL / TLS
- Process Management
- Monitoring
- Troubleshooting
Mobile-WP is deployed as a stack of services on a single Linux VPS:
Internet
│
▼ HTTPS (443)
┌─────────────────┐
│ Cloudflare │ ← CDN, DDoS protection
└────────┬────────┘
│
▼
┌─────────────────┐
│ Traefik │ ← TLS termination, reverse proxy
│ (Docker) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ nginx │ ← Static files (Vite bundle)
│ (port 3090) │
└────────┬────────┘
│ /api/*
▼
┌─────────────────┐
│ Express API │ ← Auth, CRUD, webhooks
│ PM2 (port 3001)│
└────────┬────────┘
│
▼
┌─────────────────┐
│ PostgreSQL │ ← Persistent data
│ (Docker) │
└─────────────────┘
The live deployment runs at flutter.streamtvlive.cloud.
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 vCPU | 4+ vCPU |
| RAM | 4 GB | 16+ GB |
| Disk | 40 GB SSD | 200+ GB SSD |
| Bandwidth | 1 TB/month | Unlimited |
| OS | Ubuntu 22.04+ / Debian 12+ | Ubuntu 24.04 |
| Tool | Version |
|---|---|
| Docker | 24+ |
| Docker Compose | 2.20+ |
| Node.js | 20+ |
| pnpm | 10+ |
| PM2 | 5+ |
| nginx | 1.20+ |
SSH into your server:
ssh root@YOUR_VPS_IP# Update
apt update && apt upgrade -y
# Install Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
# Install pnpm
npm install -g pnpm
# Install PM2
npm install -g pm2
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install nginx
apt install -y nginxCreate the Docker stack:
mkdir -p /projects/orchestrator
cd /projects/orchestrator
cat > docker-compose.yml <<'EOF'
services:
postgres:
image: postgres:16
container_name: orchestrator-postgres-1
restart: unless-stopped
environment:
POSTGRES_USER: orchestrator
POSTGRES_PASSWORD: CHANGE_ME
POSTGRES_DB: orchestrator
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
container_name: orchestrator-redis-1
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis-data:/data
traefik:
image: traefik:v3.2
container_name: orchestrator-traefik-1
restart: unless-stopped
command:
- --api.dashboard=true
- --providers.docker=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.email=you@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
volumes:
postgres-data:
redis-data:
letsencrypt:
EOF
docker compose up -ddocker exec orchestrator-postgres-1 psql -U orchestrator <<EOF
CREATE DATABASE fluxbuilder;
EOFmkdir -p /projects
cd /projects
git clone https://github.com/alahdal262/Web-Mobile-Flux.git fluxbuilder
cd fluxbuilder/fluxbuilder-projectpnpm installcat > .env <<EOF
API_PORT=3001
DATABASE_URL=postgresql://orchestrator:CHANGE_ME@localhost:5432/fluxbuilder
NODE_ENV=production
LOG_LEVEL=info
SESSION_SECRET=$(openssl rand -hex 32)
EOF
chmod 600 .envpnpm --filter @workspace/db run db:pushPORT=3090 API_PORT=3001 BASE_PATH=/ \
pnpm --filter @workspace/fluxbuilder run buildOutput: artifacts/fluxbuilder/dist/
pnpm --filter @workspace/api-server run buildOutput: artifacts/api-server/dist/index.mjs
cat > /etc/nginx/sites-available/mobilewp <<'EOF'
server {
listen 3090;
server_name _;
root /projects/fluxbuilder/fluxbuilder-project/artifacts/fluxbuilder/dist;
index index.html;
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# API proxy
location /api/ {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
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;
}
# Static asset caching
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
EOF
ln -s /etc/nginx/sites-available/mobilewp /etc/nginx/sites-enabled/mobilewp
nginx -t && systemctl reload nginxcd /projects/fluxbuilder/fluxbuilder-project
pm2 start artifacts/api-server/dist/index.mjs \
--name fluxbuilder-api \
--env production
pm2 save
pm2 startupcurl -sI http://localhost:3090 | head -3
curl -s http://localhost:3001/healthzTraefik automatically provisions Let's Encrypt certificates. Add a label to your nginx service (if running in Docker) or configure Traefik with a file provider:
cat > /projects/orchestrator/traefik-dynamic.yml <<'EOF'
http:
routers:
mobilewp:
rule: "Host(`yourdomain.com`)"
entryPoints:
- websecure
tls:
certResolver: le
service: mobilewp
services:
mobilewp:
loadBalancer:
servers:
- url: "http://host.docker.internal:3090"
EOFAdd to docker-compose.yml:
traefik:
command:
- --providers.file.filename=/etc/traefik/dynamic.yml
volumes:
- ./traefik-dynamic.yml:/etc/traefik/dynamic.ymlpm2 list # List all processes
pm2 logs fluxbuilder-api # View logs
pm2 logs fluxbuilder-api --lines 100
pm2 restart fluxbuilder-api # Restart
pm2 stop fluxbuilder-api # Stop
pm2 delete fluxbuilder-api # Remove
pm2 monit # Interactive monitorpm2 start artifacts/api-server/dist/index.mjs \
--name fluxbuilder-api \
--instances max \
--exec-mode clusterpm2 start ... --max-memory-restart 1GFor production updates without downtime:
#!/bin/bash
# deploy.sh
set -e
cd /projects/fluxbuilder
# Pull latest
git fetch origin main
git reset --hard origin/main
# Install deps
cd fluxbuilder-project
pnpm install --frozen-lockfile
# Run migrations (forward-compatible only)
pnpm --filter @workspace/db run db:push
# Build
PORT=3090 API_PORT=3001 BASE_PATH=/ \
pnpm --filter @workspace/fluxbuilder run build
pnpm --filter @workspace/api-server run build
# Graceful restart (no downtime)
pm2 reload fluxbuilder-api
# Verify
sleep 2
curl -sf http://localhost:3001/healthz || { echo "Health check failed"; exit 1; }
echo "Deployed successfully"Run with:
chmod +x deploy.sh
./deploy.sh# Application logs
pm2 logs fluxbuilder-api
# nginx access logs
tail -f /var/log/nginx/access.log
# nginx error logs
tail -f /var/log/nginx/error.log
# Docker logs
docker logs -f orchestrator-postgres-1
docker logs -f orchestrator-traefik-1# API
curl https://yourdomain.com/api/healthz
# Database
docker exec orchestrator-postgres-1 pg_isready -U orchestrator
# Redis
docker exec orchestrator-redis-1 redis-cli pingInstall PM2 Plus for real-time metrics:
pm2 link YOUR_KEY YOUR_SECRETOr set up Prometheus + Grafana for custom dashboards.
Backend is down. Check:
pm2 list
pm2 logs fluxbuilder-api --lines 50Restart:
pm2 restart fluxbuilder-api# Test connection
docker exec orchestrator-postgres-1 pg_isready
# Check credentials
grep DATABASE_URL /projects/fluxbuilder/fluxbuilder-project/.env
# Connect manually
docker exec -it orchestrator-postgres-1 psql -U orchestrator -d fluxbuilder# Check process memory
pm2 monit
# Check system memory
free -h
# Restart leaky process
pm2 restart fluxbuilder-apiTraefik auto-renews certificates 30 days before expiry. If renewal fails:
# Check Traefik logs
docker logs orchestrator-traefik-1 --tail 100
# Manually trigger renewal
docker restart orchestrator-traefik-1Check PostgreSQL slow query log:
docker exec orchestrator-postgres-1 psql -U orchestrator -d fluxbuilder -c "
SELECT query, calls, total_exec_time, mean_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
"If a deployment breaks production:
cd /projects/fluxbuilder
git log --oneline -5 # Find the last good commit
git reset --hard <commit> # Roll back code
cd fluxbuilder-project
pnpm install --frozen-lockfile
PORT=3090 API_PORT=3001 BASE_PATH=/ pnpm --filter @workspace/fluxbuilder run build
pnpm --filter @workspace/api-server run build
pm2 reload fluxbuilder-apiWhen you outgrow one VPS:
- Separate database — Move PostgreSQL to managed service (AWS RDS, Supabase)
- Load balancer — Cloudflare or AWS ALB in front of multiple app servers
- Redis cluster — Use Upstash or managed Redis
- Object storage — Move uploads to S3/R2
- CDN for static assets — Cloudflare R2 or AWS CloudFront
- ARCHITECTURE.md — System design
- DATABASE.md — Database details
- SETUP.md — Local development