-
Notifications
You must be signed in to change notification settings - Fork 0
Deployment Guide
Complete guide to deploying the Dissensus AI Debate Engine on a VPS.
- 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)
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 startupConnect to your VPS as root:
ssh root@YOUR_VPS_IPUpdate system packages:
apt update && apt upgrade -ySet timezone:
timedatectl set-timezone UTCCreate a dedicated user (don't run Node as root):
adduser dissensus
usermod -aG sudo dissensus
su - dissensusInstall 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.xUpload 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 --productionCreate the environment file:
cp .env.example .env
nano .envAdd 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=UTCSave and exit: Ctrl+X → Y → Enter
Install Nginx:
sudo apt install -y nginxCreate the site configuration:
sudo nano /etc/nginx/sites-available/dissensusPaste 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 hasproxy_buffering off— this is essential. Without it, Nginx will buffer the streaming chunks and the debate won't stream in real-time.
Install Certbot:
sudo apt install -y certbot python3-certbot-nginxObtain SSL certificate:
sudo certbot --nginx -d app.dissensus.funFollow the prompts:
- Enter email for renewal notices
- Agree to terms
- Certbot will auto-modify Nginx config for SSL
Verify auto-renewal:
sudo certbot renew --dry-runInstall PM2 globally:
sudo npm install -g pm2Start the application:
cd ~/dissensus-engine
pm2 start server/index.js --name dissensusSave PM2 configuration and enable startup:
pm2 save
pm2 startup systemdRun 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 PM2Install 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 verboseExpected 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.
| 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 |
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/healthIf 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 dissensusThe 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:
- Go to Domains → select
dissensus.fun - Click DNS / Nameservers → DNS records
- Keep existing A records for
@andwww(landing page) - Add one new A record:
| Type | Name | Points to | TTL |
|---|---|---|---|
| A | app | YOUR_VPS_IP | 14400 |
Wait 5-30 minutes for DNS propagation.
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 │
└─────────────────────────┘
502 Bad Gateway:
pm2 restart dissensusSSE not streaming:
# Check Nginx has proxy_buffering off in the stream location
sudo nginx -t && sudo systemctl reload nginxSSL issues:
sudo certbot renew --dry-runOut 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