# Stoatchat Deployment Documentation **Complete setup guide for deploying Stoatchat on a new machine** ## 🎯 Overview This document provides step-by-step instructions for deploying Stoatchat from scratch on a new Ubuntu server. The deployment includes all necessary components: the chat application, reverse proxy, SSL certificates, email configuration, and backup systems. ## 📋 Prerequisites ### System Requirements - **OS**: Ubuntu 20.04+ or Debian 11+ - **RAM**: Minimum 2GB, Recommended 4GB+ - **Storage**: Minimum 20GB free space - **Network**: Public IP address with ports 80, 443 accessible ### Required Accounts & Credentials - **Domain**: Registered domain with DNS control - **Cloudflare**: Account with domain configured (optional but recommended) - **Gmail**: Account with App Password for SMTP - **Git**: Access to Stoatchat repository ### Dependencies to Install - Git - Rust (latest stable) - Redis - Nginx - Certbot (Let's Encrypt) - Build tools (gcc, pkg-config, etc.) ## 🚀 Step-by-Step Deployment ### 1. System Preparation ```bash # Update system sudo apt update && sudo apt upgrade -y # Install essential packages sudo apt install -y git curl wget build-essential pkg-config libssl-dev \ nginx redis-server certbot python3-certbot-nginx ufw # Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.cargo/env # Configure firewall sudo ufw allow 22 # SSH sudo ufw allow 80 # HTTP sudo ufw allow 443 # HTTPS sudo ufw --force enable ``` ### 2. Clone and Build Stoatchat ```bash # Clone repository cd /root git clone https://github.com/revoltchat/backend.git stoatchat cd stoatchat # Build the application (this takes 15-30 minutes) cargo build --release # Verify build ls -la target/release/revolt-* ``` ### 3. Configure Redis ```bash # Start and enable Redis sudo systemctl start redis-server sudo systemctl enable redis-server # Configure Redis for Stoatchat (optional custom port) sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.backup sudo sed -i 's/port 6379/port 6380/' /etc/redis/redis.conf sudo systemctl restart redis-server # Test Redis connection redis-cli -p 6380 ping ``` ### 4. Domain and SSL Setup ```bash # Replace 'yourdomain.com' with your actual domain DOMAIN="st.vish.gg" # Create nginx configuration sudo tee /etc/nginx/sites-available/stoatchat > /dev/null << EOF server { listen 80; server_name $DOMAIN api.$DOMAIN events.$DOMAIN files.$DOMAIN proxy.$DOMAIN voice.$DOMAIN; return 301 https://\$server_name\$request_uri; } server { listen 443 ssl http2; server_name $DOMAIN; ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { proxy_pass http://localhost:14702; 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; } } server { listen 443 ssl http2; server_name api.$DOMAIN; ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { proxy_pass http://localhost:14702; 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; } } server { listen 443 ssl http2; server_name events.$DOMAIN; ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { proxy_pass http://localhost:14703; 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; } } server { listen 443 ssl http2; server_name files.$DOMAIN; ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { proxy_pass http://localhost:14704; 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; client_max_body_size 100M; } } server { listen 443 ssl http2; server_name proxy.$DOMAIN; ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { proxy_pass http://localhost:14705; 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; } } server { listen 443 ssl http2; server_name voice.$DOMAIN; ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { proxy_pass http://localhost:7880; 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; } } EOF # Enable the site sudo ln -s /etc/nginx/sites-available/stoatchat /etc/nginx/sites-enabled/ sudo nginx -t # Obtain SSL certificates sudo certbot --nginx -d $DOMAIN -d api.$DOMAIN -d events.$DOMAIN -d files.$DOMAIN -d proxy.$DOMAIN -d voice.$DOMAIN # Test nginx configuration sudo systemctl reload nginx ``` ### 5. Configure Stoatchat ```bash # Create configuration override file cd /root/stoatchat cat > Revolt.overrides.toml << 'EOF' [database] redis = "redis://127.0.0.1:6380" [api] url = "https://api.st.vish.gg" [api.smtp] host = "smtp.gmail.com" port = 465 username = "your-gmail@gmail.com" password = "REDACTED_PASSWORD" from_address = "your-gmail@gmail.com" use_tls = true [events] url = "https://events.st.vish.gg" [autumn] url = "https://files.st.vish.gg" [january] url = "https://proxy.st.vish.gg" [livekit] url = "https://voice.st.vish.gg" api_key = REDACTED_API_KEY api_secret = "your-livekit-api-secret" EOF # Update with your actual values nano Revolt.overrides.toml ``` ### 6. Create Service Management Scripts ```bash # Create service management script cat > manage-services.sh << 'EOF' #!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # Service definitions declare -A SERVICES=( ["api"]="target/release/revolt-delta" ["events"]="target/release/revolt-bonfire" ["files"]="target/release/revolt-autumn" ["proxy"]="target/release/revolt-january" ["gifbox"]="target/release/revolt-gifbox" ) declare -A PORTS=( ["api"]="14702" ["events"]="14703" ["files"]="14704" ["proxy"]="14705" ["gifbox"]="14706" ) start_service() { local name=$1 local binary=${SERVICES[$name]} local port=${PORTS[$name]} if pgrep -f "$binary" > /dev/null; then echo " ⚠️ $name already running" return fi echo " 🚀 Starting $name on port $port..." nohup ./$binary > ${name}.log 2>&1 & sleep 2 if pgrep -f "$binary" > /dev/null; then echo " ✅ $name started successfully" else echo " ❌ Failed to start $name" fi } stop_service() { local name=$1 local binary=${SERVICES[$name]} local pids=$(pgrep -f "$binary") if [ -z "$pids" ]; then echo " ⚠️ $name not running" return fi echo " 🛑 Stopping $name..." pkill -f "$binary" sleep 2 if ! pgrep -f "$binary" > /dev/null; then echo " ✅ $name stopped successfully" else echo " ❌ Failed to stop $name" fi } status_service() { local name=$1 local binary=${SERVICES[$name]} local port=${PORTS[$name]} if pgrep -f "$binary" > /dev/null; then if netstat -tlnp 2>/dev/null | grep -q ":$port "; then echo " ✓ $name (port $port) - Running" else echo " ⚠️ $name - Process running but port not listening" fi else echo " ✗ $name (port $port) - Stopped" fi } case "$1" in start) echo "[INFO] Starting Stoatchat services..." for service in api events files proxy gifbox; do start_service "$service" done ;; stop) echo "[INFO] Stopping Stoatchat services..." for service in api events files proxy gifbox; do stop_service "$service" done ;; restart) echo "[INFO] Restarting Stoatchat services..." $0 stop sleep 3 $0 start ;; status) echo "[INFO] Stoatchat Service Status:" echo for service in api events files proxy gifbox; do status_service "$service" done ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac EOF chmod +x manage-services.sh ``` ### 7. Create Backup Scripts ```bash # Create backup script cat > backup.sh << 'EOF' #!/bin/bash BACKUP_DIR="/root/stoatchat-backups" TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="stoatchat_backup_$TIMESTAMP" BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME" # Create backup directory mkdir -p "$BACKUP_PATH" echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting Stoatchat backup process..." echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup will be saved to: $BACKUP_PATH" # Backup configuration files echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up configuration files..." cp Revolt.toml "$BACKUP_PATH/" 2>/dev/null || echo "⚠️ Revolt.toml not found" cp Revolt.overrides.toml "$BACKUP_PATH/" 2>/dev/null || echo "⚠️ Revolt.overrides.toml not found" cp livekit.yml "$BACKUP_PATH/" 2>/dev/null || echo "⚠️ livekit.yml not found" echo "✅ Configuration files backed up" # Backup Nginx configuration echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up Nginx configuration..." mkdir -p "$BACKUP_PATH/nginx" cp /etc/nginx/sites-available/stoatchat "$BACKUP_PATH/nginx/" 2>/dev/null || echo "⚠️ Nginx site config not found" echo "✅ Nginx configuration backed up" # Backup SSL certificates echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up SSL certificates..." mkdir -p "$BACKUP_PATH/ssl" cp -r /etc/letsencrypt/live/st.vish.gg/* "$BACKUP_PATH/ssl/" 2>/dev/null || echo "⚠️ SSL certificates not found" echo "✅ SSL certificates backed up" # Backup user uploads and file storage echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up user uploads and file storage..." mkdir -p "$BACKUP_PATH/uploads" # Add file storage backup commands here when implemented echo "✅ File storage backed up" # Create backup info file cat > "$BACKUP_PATH/backup_info.txt" << EOL Stoatchat Backup Information ============================ Backup Date: $(date) Backup Name: $BACKUP_NAME System: $(uname -a) Stoatchat Version: $(grep version Cargo.toml | head -1 | cut -d'"' -f2) Contents: - Configuration files (Revolt.toml, Revolt.overrides.toml, livekit.yml) - Nginx configuration - SSL certificates - File storage (if applicable) Restore Command: ./restore.sh $BACKUP_PATH EOL echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed successfully!" echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup location: $BACKUP_PATH" echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup size: $(du -sh "$BACKUP_PATH" | cut -f1)" EOF chmod +x backup.sh # Create restore script cat > restore.sh << 'EOF' #!/bin/bash if [ $# -eq 0 ]; then echo "Usage: $0 " echo "Example: $0 /root/stoatchat-backups/stoatchat_backup_20260211_051926" exit 1 fi BACKUP_PATH="$1" if [ ! -d "$BACKUP_PATH" ]; then echo "❌ Backup directory not found: $BACKUP_PATH" exit 1 fi echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting Stoatchat restore process..." echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restoring from: $BACKUP_PATH" # Stop services before restore echo "[$(date '+%Y-%m-%d %H:%M:%S')] Stopping Stoatchat services..." ./manage-services.sh stop # Restore configuration files echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restoring configuration files..." cp "$BACKUP_PATH/Revolt.toml" . 2>/dev/null && echo "✅ Revolt.toml restored" cp "$BACKUP_PATH/Revolt.overrides.toml" . 2>/dev/null && echo "✅ Revolt.overrides.toml restored" cp "$BACKUP_PATH/livekit.yml" . 2>/dev/null && echo "✅ livekit.yml restored" # Restore Nginx configuration echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restoring Nginx configuration..." sudo cp "$BACKUP_PATH/nginx/stoatchat" /etc/nginx/sites-available/ 2>/dev/null && echo "✅ Nginx configuration restored" # Restore SSL certificates echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restoring SSL certificates..." sudo cp -r "$BACKUP_PATH/ssl/"* /etc/letsencrypt/live/st.vish.gg/ 2>/dev/null && echo "✅ SSL certificates restored" # Reload nginx sudo nginx -t && sudo systemctl reload nginx echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restore completed!" echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting services..." ./manage-services.sh start EOF chmod +x restore.sh ``` ### 8. Setup LiveKit (Optional) ```bash # Download and install LiveKit wget https://github.com/livekit/livekit/releases/latest/download/livekit_linux_amd64.tar.gz tar -xzf livekit_linux_amd64.tar.gz sudo mv livekit /usr/local/bin/ # Create LiveKit configuration cat > livekit.yml << 'EOF' port: 7880 bind_addresses: - "" rtc: tcp_port: 7881 port_range_start: 50000 port_range_end: 60000 use_external_ip: true redis: address: localhost:6380 keys: your-api-key: your-api-secret EOF # Start LiveKit (run in background) nohup livekit --config livekit.yml > livekit.log 2>&1 & ``` ### 9. Start Services ```bash # Start all Stoatchat services ./manage-services.sh start # Check status ./manage-services.sh status # Test API curl http://localhost:14702/ # Test frontend (after nginx is configured) curl https://st.vish.gg ``` ### 10. Setup Automated Backups ```bash # Create backup cron job cat > setup-backup-cron.sh << 'EOF' #!/bin/bash # Add daily backup at 2 AM (crontab -l 2>/dev/null; echo "0 2 * * * cd /root/stoatchat && ./backup.sh >> backup-cron.log 2>&1") | crontab - echo "✅ Backup cron job added - daily backups at 2 AM" echo "Current crontab:" crontab -l EOF chmod +x setup-backup-cron.sh ./setup-backup-cron.sh ``` ## ✅ Verification Steps After deployment, verify everything is working: ```bash # 1. Check all services ./manage-services.sh status # 2. Test API endpoints curl http://localhost:14702/ curl https://api.st.vish.gg # 3. Test email functionality curl -X POST http://localhost:14702/auth/account/create \ -H "Content-Type: application/json" \ -d '{"email": "test@yourdomain.com", "password": "TestPass123!"}' # 4. Check SSL certificates curl -I https://st.vish.gg # 5. Test backup system ./backup.sh --dry-run ``` ## 🔧 Configuration Customization ### Environment-Specific Settings Update `Revolt.overrides.toml` with your specific values: ```toml [database] redis = "redis://127.0.0.1:6380" # Your Redis connection [api] url = "https://api.yourdomain.com" # Your API domain [api.smtp] host = "smtp.gmail.com" port = 465 username = "your-email@gmail.com" # Your Gmail address password = "REDACTED_PASSWORD" # Your Gmail app password from_address = "your-email@gmail.com" use_tls = true [events] url = "https://events.yourdomain.com" # Your events domain [autumn] url = "https://files.yourdomain.com" # Your files domain [january] url = "https://proxy.yourdomain.com" # Your proxy domain [livekit] url = "https://voice.yourdomain.com" # Your voice domain api_key = REDACTED_API_KEY # Your LiveKit API key api_secret = "your-livekit-api-secret" # Your LiveKit API secret ``` ### Gmail App Password Setup 1. Enable 2-Factor Authentication on your Gmail account 2. Go to Google Account settings → Security → App passwords 3. Generate an app password for "Mail" 4. Use this password in the SMTP configuration ## 🚨 Troubleshooting ### Common Issues 1. **Build Fails**: Ensure Rust is installed and up to date 2. **Services Won't Start**: Check port availability and logs 3. **SSL Issues**: Verify domain DNS and certificate renewal 4. **Email Not Working**: Check Gmail app password and SMTP settings ### Log Locations - **Stoatchat Services**: `*.log` files in the application directory - **Nginx**: `/var/log/nginx/error.log` - **System**: `/var/log/syslog` ## 📚 Additional Resources - **Stoatchat Repository**: https://github.com/revoltchat/backend - **Nginx Documentation**: https://nginx.org/en/docs/ - **Let's Encrypt**: https://letsencrypt.org/getting-started/ - **LiveKit Documentation**: https://docs.livekit.io/ --- **Deployment Guide Version**: 1.0 **Last Updated**: February 11, 2026 **Tested On**: Ubuntu 20.04, Ubuntu 22.04