Skip to content

Deployment Guide

dev-mondoshawan edited this page Apr 15, 2026 · 1 revision

Deployment Guide

Complete guide to deploying the Dissensus AI Debate Engine on a VPS.


Prerequisites

  • OS: Ubuntu 22.04 or 24.04 LTS
  • Access: Root or sudo privileges
  • Domain: app.dissensus.fun (or your domain) pointed to your VPS IP
  • API Key: DeepSeek API key (get from platform.deepseek.com)
  • Tools: SSH client (Terminal on Mac/Linux, PuTTY or PowerShell on Windows)

Quick Deploy (60 Seconds)

After initial VPS setup, deploy in 4 commands:

# 1. Upload from your local machine
scp dissensus-engine.zip dissensus@YOUR_VPS_IP:~/

# 2. SSH into VPS and unpack
ssh dissensus@YOUR_VPS_IP
unzip -o dissensus-engine.zip -d ~/dissensus-engine

# 3. Install dependencies and configure
cd ~/dissensus-engine && npm install --production
cp .env.example .env && nano .env  # Add DEEPSEEK_API_KEY

# 4. Start with PM2
pm2 start server/index.js --name dissensus
pm2 save && pm2 startup

Full Step-by-Step Deployment

1. System Setup

Connect to your VPS as root:

ssh root@YOUR_VPS_IP

Update system packages:

apt update && apt upgrade -y

Set timezone:

timedatectl set-timezone UTC

Create a dedicated user (don't run Node as root):

adduser dissensus
usermod -aG sudo dissensus
su - dissensus

Install Node.js 20 LTS:

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Verify
node --version    # v20.x.x
npm --version     # 10.x.x

2. Deploy the Engine

Upload the code (from your local machine):

scp dissensus-engine.zip dissensus@YOUR_VPS_IP:~/

On the VPS, unpack and install:

cd ~
unzip -o dissensus-engine.zip -d ~/dissensus-engine
cd ~/dissensus-engine
npm install --production

3. Configure Environment Variables

Create the environment file:

cp .env.example .env
nano .env

Add your configuration:

# Server Configuration
PORT=3000
NODE_ENV=production

# API Keys (at least one required for visitors to use without their own key)
DEEPSEEK_API_KEY=sk-your-deepseek-key
OPENAI_API_KEY=sk-your-openai-key
GEMINI_API_KEY=AIza-your-gemini-key

# Optional: Timezone for "Debate of the Day"
DEBATE_OF_THE_DAY_TZ=UTC

Save and exit: Ctrl+XYEnter


4. Nginx Reverse Proxy Setup

Install Nginx:

sudo apt install -y nginx

Create the site configuration:

sudo nano /etc/nginx/sites-available/dissensus

Paste this configuration:

server {
    listen 80;
    listen [::]:80;
    server_name app.dissensus.fun;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;

    # Static assets — serve directly from Nginx
    location /css/ {
        alias /home/dissensus/dissensus-engine/public/css/;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    location /js/ {
        alias /home/dissensus/dissensus-engine/public/js/;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    location /images/ {
        alias /home/dissensus/dissensus-engine/public/images/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # SSE streaming — CRITICAL: disable buffering
    location /api/debate/stream {
        proxy_pass http://127.0.0.1:3000;
        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;
        proxy_set_header Connection '';

        # CRITICAL for SSE streaming — disable all buffering
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding off;

        # Long timeout for debates (they can take 5+ minutes)
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }

    # API endpoints
    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        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;
    }

    # Everything else — proxy to Node
    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

Enable the site:

sudo ln -sf /etc/nginx/sites-available/dissensus /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl enable nginx

⚠️ Critical: The SSE streaming location block has proxy_buffering off — this is essential. Without it, Nginx will buffer the streaming chunks and the debate won't stream in real-time.


5. SSL with Let's Encrypt

Install Certbot:

sudo apt install -y certbot python3-certbot-nginx

Obtain SSL certificate:

sudo certbot --nginx -d app.dissensus.fun

Follow the prompts:

  1. Enter email for renewal notices
  2. Agree to terms
  3. Certbot will auto-modify Nginx config for SSL

Verify auto-renewal:

sudo certbot renew --dry-run

6. PM2 Process Management

Install PM2 globally:

sudo npm install -g pm2

Start the application:

cd ~/dissensus-engine
pm2 start server/index.js --name dissensus

Save PM2 configuration and enable startup:

pm2 save
pm2 startup systemd

Run the command that PM2 outputs to enable startup on boot.

PM2 Commands:

pm2 status                 # View process status
pm2 logs dissensus         # View logs
pm2 logs dissensus --lines 100
pm2 restart dissensus      # Restart app
pm2 stop dissensus         # Stop app
pm2 delete dissensus       # Remove from PM2

7. Firewall (UFW)

Install and configure UFW:

sudo apt install -y ufw

# Set defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (IMPORTANT — do this first!)
sudo ufw allow 22/tcp

# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable

# Verify
sudo ufw status verbose

Expected output:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

Note: Port 3000 is NOT exposed publicly. Nginx handles all public traffic on 80/443 and proxies to Node.js internally.


Quick Reference Commands

Action Command
Start engine pm2 start dissensus
Stop engine pm2 stop dissensus
Restart engine pm2 restart dissensus
View status pm2 status
View logs pm2 logs dissensus
Monitor pm2 monit
Restart Nginx sudo systemctl reload nginx
Test Nginx config sudo nginx -t
Renew SSL sudo certbot renew
Check firewall sudo ufw status

Update Procedure

When deploying new code:

# 1. SSH into VPS
ssh dissensus@YOUR_VPS_IP

# 2. Backup current version
cp -r ~/dissensus-engine ~/dissensus-engine-backup-$(date +%Y%m%d)

# 3. Upload new zip from local machine (in another terminal)
scp dissensus-engine.zip dissensus@YOUR_VPS_IP:~/

# 4. Unpack over existing
cd ~ && unzip -o dissensus-engine.zip -d ~/dissensus-engine

# 5. Install dependencies (in case package.json changed)
cd ~/dissensus-engine && npm install --production

# 6. Restart
pm2 restart dissensus

# 7. Verify
pm2 status
curl http://localhost:3000/api/health

Rollback Procedure

If an update breaks something:

# Stop current version
pm2 stop dissensus

# Restore backup
rm -rf ~/dissensus-engine
mv ~/dissensus-engine-backup-YYYYMMDD ~/dissensus-engine

# Restart
pm2 start dissensus
cd ~/dissensus-engine && pm2 start server/index.js --name dissensus

DNS Configuration

The debate engine runs on app.dissensus.fun while the landing page stays on dissensus.fun:

dissensus.fun          → Landing page (Hostinger shared hosting)
app.dissensus.fun      → Debate engine (Hostinger VPS)

In Hostinger hPanel:

  1. Go to Domains → select dissensus.fun
  2. Click DNS / NameserversDNS records
  3. Keep existing A records for @ and www (landing page)
  4. Add one new A record:
Type Name Points to TTL
A app YOUR_VPS_IP 14400

Wait 5-30 minutes for DNS propagation.


Architecture Overview

Internet
    │
    ▼
┌─────────────────────────┐
│   Nginx (port 80/443)   │  ← SSL termination, compression, static files
│   - SSL via Let's Encrypt
│   - Gzip compression
│   - Static asset caching
│   - SSE proxy (no buffer)
└──────────┬──────────────┘
           │ proxy_pass
           ▼
┌─────────────────────────┐
│  Node.js (port 3000)    │  ← Express server, debate orchestration
│  - SSE streaming        │
│  - Multi-provider AI    │
│  - 4-phase debate engine│
└──────────┬──────────────┘
           │ HTTPS
           ▼
┌─────────────────────────┐
│  AI Providers           │
│  - DeepSeek V3.2        │
│  - Google Gemini        │
│  - OpenAI GPT-4o        │
└─────────────────────────┘

Troubleshooting Quick Fixes

502 Bad Gateway:

pm2 restart dissensus

SSE not streaming:

# Check Nginx has proxy_buffering off in the stream location
sudo nginx -t && sudo systemctl reload nginx

SSL issues:

sudo certbot renew --dry-run

Out of memory:

# Add 2GB swap
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Clone this wiki locally