Deployment (VPS)
This is a complete, production-focused deployment guide for your own VPS. Includes both non-Docker and Docker Compose paths, Nginx reverse proxy, WebSocket config, and free SSL.
Plan your domains
- api subdomain (backend REST + WebSockets): 
api.example.com - web app (Flutter web build): 
web.example.com - admin panel (Flutter web build): 
admin.example.com 
Point DNS A records of these subdomains to your VPS public IP.
Non‑Docker deployment (Ubuntu 22.04/24.04)
1) System packages
sudo apt update && sudo apt -y upgrade
sudo apt -y install nginx ufw git curl build-essential
2) Install MongoDB 6.0
curl -fsSL https://pgp.mongodb.com/server-6.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg --dearmor
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg ] https://repo.mongodb.org/apt/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME)/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
sudo apt update
sudo apt -y install mongodb-org
sudo systemctl enable --now mongod
Create users:
mongosh <<'EOF'
use admin
db.createUser({user: "root", pwd: "CHANGE-ME-STRONG", roles: ["root"]})
use super_up
db.createUser({user: "appuser", pwd: "CHANGE-ME-STRONG", roles: [{role: "readWrite", db: "super_up"}]})
EOF
3) Install Node.js (recommended latest LTS/Current) + PM2
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.nvm/nvm.sh
nvm install 23
node -v
npm i -g pm2@latest
4) Backend setup (NestJS API)
Assume your backend lives at /var/www/super-up/backend.
sudo mkdir -p /var/www/super-up/backend
sudo chown -R $USER:$USER /var/www/super-up
cd /var/www/super-up/backend
Create .env.production (adjust to your values):
DB_URL=mongodb://appuser:CHANGE-ME-STRONG@127.0.0.1:27017/super_up?authSource=super_up
JWT_SECRET=CHANGE-ME-STRONG
issuer=you@example.com
audience=you@example.com
NODE_ENV=production
EDIT_MODE=false
ignoreEnvFile=false
PORT=3000
ControlPanelAdminPassword=CHANGE-ME-ADMIN
ControlPanelAdminPasswordViewer=CHANGE-ME-VIEWER
isOneSignalEnabled=false
isFirebaseFcmEnabled=false
EMAIL_HOST=
EMAIL_USER=
EMAIL_PASSWORD=
AGORA_APP_ID=
AGORA_APP_CERTIFICATE=
Install, build, and run with PM2 cluster mode:
npm ci
npm run build
# Minimal PM2 ecosystem file (create ecosystem.config.js)
cat > ecosystem.config.js <<'JS'
module.exports = {
  apps: [
    {
      name: 'super-up-api',
      script: 'dist/main.js',
      cwd: '/var/www/super-up/backend',
      instances: 'max',
      exec_mode: 'cluster',
      env_production: { NODE_ENV: 'production', PORT: 3000 }
    }
  ]
}
JS
pm2 start ecosystem.config.js --env production
pm2 save
pm2 startup systemd -u $USER --hp $HOME
5) Nginx reverse proxy (with WebSocket support)
API api.example.com → Node.js at 127.0.0.1:3000:
server {
  listen 80;
  server_name api.example.com;
  location / {
    proxy_pass http://127.0.0.1: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;
  }
}
Flutter web (SPA) web.example.com serving built files at /var/www/super-up/web:
server {
  listen 80;
  server_name web.example.com;
  root /var/www/super-up/web;
  index index.html;
  location / {
    try_files $uri /index.html;
  }
}
Admin panel admin.example.com serving /var/www/super-up/admin similarly.
Enable sites and reload Nginx:
sudo mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
sudo tee /etc/nginx/sites-available/api.example.com >/dev/null < api.conf
sudo tee /etc/nginx/sites-available/web.example.com >/dev/null < web.conf
sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/web.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
6) Free SSL (Let’s Encrypt)
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx -d api.example.com -d web.example.com -d admin.example.com --redirect -m you@example.com --agree-tos -n
7) Firewall
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable
8) Deploy Flutter web + Admin builds
- Build web inside your Flutter apps:
 
cd super_up_app
flutter build web --release --web-renderer html
sudo rsync -a build/web/ /var/www/super-up/web/
cd ../super_up_admin
flutter build web --release --web-renderer html
sudo rsync -a build/web/ /var/www/super-up/admin/
Docker Compose deployment
Directory layout (example):
/opt/super-up/
  backend/            # your NestJS backend project (has Dockerfile)
  docker-compose.yml
  .env
  mongo-init.js
  media/              # persistent uploads/media
.env (used by compose):
DB_URL=mongodb://appuser:CHANGE-ME-STRONG@mongo:27017/super_up?authSource=admin
JWT_SECRET=CHANGE-ME-STRONG
NODE_ENV=production
PORT=3000
ControlPanelAdminPassword=CHANGE-ME-ADMIN
ControlPanelAdminPasswordViewer=CHANGE-ME-VIEWER
mongo-init.js (creates app DB user):
db = db.getSiblingDB("super_up");
db.createUser({
  user: "appuser",
  pwd: "CHANGE-ME-STRONG",
  roles: [{ role: "readWrite", db: "super_up" }],
});
docker-compose.yml:
version: "3.9"
services:
  mongo:
    image: mongo:6
    restart: unless-stopped
    volumes:
      - mongo_data:/data/db
      - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
    ports:
      - "127.0.0.1:27017:27017"
  api:
    build:
      context: ./backend
    environment:
      DB_URL: ${DB_URL}
      JWT_SECRET: ${JWT_SECRET}
      NODE_ENV: ${NODE_ENV}
      PORT: ${PORT}
      ControlPanelAdminPassword: ${ControlPanelAdminPassword}
      ControlPanelAdminPasswordViewer: ${ControlPanelAdminPasswordViewer}
    volumes:
      - ./media:/app/public/uploads
    depends_on:
      - mongo
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:3000" # expose API on 8080 locally
volumes:
  mongo_data:
Up the stack:
cd /opt/super-up
docker compose up -d --build
docker compose logs -f api | cat
Configure Nginx api.example.com to reverse proxy to http://127.0.0.1:8080 with the same WebSocket headers shown earlier. SSL with Certbot is identical to non-Docker.
Configure Flutter apps for your domain
Inside SConstants in Flutter set your production domain; example:
static const _productionBaseUrl = "example.com"; // not including protocol
// Media and API builders already prefix with https://api.
Ensure the backend is reachable at https://api.example.com and CORS is allowed if enforced.
Troubleshooting
- WebSocket 400/upgrade errors: ensure Nginx has 
Upgrade/Connection upgradeheaders andproxy_http_version 1.1. - 502 Bad Gateway: API not listening, wrong upstream port, or PM2/app crashed. Check 
pm2 logsordocker compose logs. - Firebase/OneSignal push not working: verify keys on backend 
.envand FlutterSConstants, and platform-specific setup in Flutter doc. - Mongo auth failed: double-check 
DB_URL, authSource, and that user exists in the correct DB.