-
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 startup
Connect to your VPS as root:
ssh root@YOUR_VPS_IP
Update system packages:
apt update && apt upgrade -ySet 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
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
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+X → Y → Enter
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 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-nginx
Obtain SSL certificate:
sudo certbot --nginx -d app.dissensus.fun
Follow 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-run
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
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.
| 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/health
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
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:
- 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 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