Files
homelab-optimized/docs/services/stoatchat/DEPLOYMENT_GUIDE.md
Gitea Mirror Bot 1ab33b1e66
Some checks failed
Documentation / Deploy to GitHub Pages (push) Has been cancelled
Documentation / Build Docusaurus (push) Has been cancelled
Sanitized mirror from private repository - 2026-04-19 09:48:50 UTC
2026-04-19 09:48:50 +00:00

12 KiB

Stoatchat Complete Deployment Guide - Seattle VM

This guide documents the complete process used to deploy Stoatchat on the Seattle VM. Follow these steps to recreate the deployment on a new server.

Prerequisites

  • Ubuntu/Debian server with root access
  • Domain name with Cloudflare DNS management
  • Gmail account with App Password for SMTP
  • At least 4GB RAM and 20GB storage

Step 1: Server Preparation

1.1 Update System

apt update && apt upgrade -y
apt install -y curl wget git build-essential pkg-config libssl-dev nginx certbot python3-certbot-nginx

1.2 Install Docker

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
systemctl enable docker
systemctl start docker

1.3 Install Rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
rustup default stable

Step 2: Clone and Build Stoatchat

2.1 Clone Repository

cd /root
git clone https://github.com/stoatchat/stoatchat.git
cd stoatchat

2.2 Build Services

# This takes 15-30 minutes depending on server specs
cargo build --release

# Or for debug builds (faster compilation, used in current deployment):
cargo build

Step 3: Infrastructure Services Setup

3.1 Create Docker Compose File

cat > compose.yml << 'EOF'
services:
  redis:
    image: eqalpha/keydb
    container_name: stoatchat-redis
    ports:
      - "6380:6379"
    volumes:
      - ./data/redis:/data
    restart: unless-stopped

  database:
    image: mongo:7
    container_name: stoatchat-mongodb
    ports:
      - "27017:27017"
    volumes:
      - ./data/mongodb:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: stoatchat
      MONGO_INITDB_ROOT_PASSWORD: "REDACTED_PASSWORD"
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
    restart: unless-stopped

  minio:
    image: minio/minio:latest
    container_name: stoatchat-minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: REDACTED_MINIO_CRED
      MINIO_ROOT_PASSWORD: "REDACTED_PASSWORD"
    volumes:
      - ./data/minio:/data
    ports:
      - "14009:9000"
      - "9001:9001"
    restart: unless-stopped

  livekit:
    image: livekit/livekit-server:v1.9.9
    container_name: stoatchat-livekit
    ports:
      - "7880:7880"
      - "7881:7881"
      - "7882:7882/udp"
    volumes:
      - ./livekit.yml:/livekit.yml:ro
    command: --config /livekit.yml
    restart: unless-stopped
EOF

3.2 Create LiveKit Configuration

cat > livekit.yml << 'EOF'
port: 7880
redis:
    address: localhost:6380
    username: ""
    password: ""
webhook:
  api_key: worldwide
  urls:
    - 'http://localhost:8500/worldwide'
logging:
  level: debug
keys:
  worldwide: YOUR_LIVEKIT_API_KEY_GENERATE_RANDOM_32_CHARS
EOF

3.3 Start Infrastructure Services

docker-compose up -d

Step 4: Stoatchat Configuration

4.1 Create Configuration Override

cat > Revolt.overrides.toml << 'EOF'
[database]
redis = "redis://127.0.0.1:6380/"
mongodb = "mongodb://stoatchat:YOUR_SECURE_MONGODB_PASSWORD@127.0.0.1:27017/revolt"

[hosts]
app = "https://YOUR_DOMAIN"
api = "https://api.YOUR_DOMAIN"
events = "wss://events.YOUR_DOMAIN"
autumn = "https://files.YOUR_DOMAIN"
january = "https://proxy.YOUR_DOMAIN"

[hosts.livekit]
worldwide = "wss://voice.YOUR_DOMAIN"

[email]
smtp_host = "smtp.gmail.com"
smtp_port = 587
smtp_username = "YOUR_GMAIL@gmail.com"
smtp_password = "REDACTED_PASSWORD"
from_address = "YOUR_GMAIL@gmail.com"
smtp_tls = true

[files]
s3_region = "us-east-1"
s3_bucket = "revolt-uploads"
s3_endpoint = "http://127.0.0.1:14009"
s3_access_key_id = "REDACTED_MINIO_CRED"
s3_secret_access_key = "YOUR_SECURE_MINIO_PASSWORD"

[security]
vapid_private_key = REDACTED_VAPID_PRIVATE_KEY

[features]
captcha_enabled = false
email_verification = true
invite_only = false

[limits]
max_file_size = 104857600  # 100MB
max_message_length = 2000
max_embed_count = 10
EOF

Step 5: SSL Certificates Setup

5.1 Configure Cloudflare DNS

Set up A records for all subdomains pointing to your server IP:

  • YOUR_DOMAIN
  • api.YOUR_DOMAIN
  • events.YOUR_DOMAIN
  • files.YOUR_DOMAIN
  • proxy.YOUR_DOMAIN
  • voice.YOUR_DOMAIN

5.2 Obtain SSL Certificates

# Get certificates for all domains
certbot certonly --nginx -d YOUR_DOMAIN -d api.YOUR_DOMAIN -d events.YOUR_DOMAIN -d files.YOUR_DOMAIN -d proxy.YOUR_DOMAIN -d voice.YOUR_DOMAIN

# Or individually if needed:
certbot certonly --nginx -d YOUR_DOMAIN
certbot certonly --nginx -d api.YOUR_DOMAIN
certbot certonly --nginx -d events.YOUR_DOMAIN
certbot certonly --nginx -d files.YOUR_DOMAIN
certbot certonly --nginx -d proxy.YOUR_DOMAIN
certbot certonly --nginx -d voice.YOUR_DOMAIN

Step 6: Nginx Configuration

6.1 Create Nginx Configuration

cat > /etc/nginx/sites-available/stoatchat << 'EOF'
# Main app (placeholder/frontend)
server {
    listen 80;
    server_name YOUR_DOMAIN;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name YOUR_DOMAIN;
    
    ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem;
    
    location / {
        return 200 'Stoatchat - Coming Soon';
        add_header Content-Type text/plain;
    }
}

# API Server
server {
    listen 80;
    server_name api.YOUR_DOMAIN;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.YOUR_DOMAIN;
    
    ssl_certificate /etc/letsencrypt/live/api.YOUR_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.YOUR_DOMAIN/privkey.pem;
    
    location / {
        proxy_pass http://127.0.0.1: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;
    }
}

# Events WebSocket
server {
    listen 80;
    server_name events.YOUR_DOMAIN;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name events.YOUR_DOMAIN;
    
    ssl_certificate /etc/letsencrypt/live/events.YOUR_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/events.YOUR_DOMAIN/privkey.pem;
    
    location / {
        proxy_pass http://127.0.0.1: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;
        proxy_read_timeout 86400;
    }
}

# File Server
server {
    listen 80;
    server_name files.YOUR_DOMAIN;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name files.YOUR_DOMAIN;
    
    ssl_certificate /etc/letsencrypt/live/files.YOUR_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/files.YOUR_DOMAIN/privkey.pem;
    
    client_max_body_size 100M;
    
    location / {
        proxy_pass http://127.0.0.1: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;
    }
}

# Media Proxy
server {
    listen 80;
    server_name proxy.YOUR_DOMAIN;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name proxy.YOUR_DOMAIN;
    
    ssl_certificate /etc/letsencrypt/live/proxy.YOUR_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/proxy.YOUR_DOMAIN/privkey.pem;
    
    location / {
        proxy_pass http://127.0.0.1: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;
    }
}

# Voice/Video (LiveKit)
server {
    listen 80;
    server_name voice.YOUR_DOMAIN;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name voice.YOUR_DOMAIN;
    
    ssl_certificate /etc/letsencrypt/live/voice.YOUR_DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/voice.YOUR_DOMAIN/privkey.pem;
    
    location / {
        proxy_pass http://127.0.0.1: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;
        proxy_read_timeout 86400;
    }
}
EOF

6.2 Enable Configuration

ln -s /etc/nginx/sites-available/stoatchat /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

Step 7: Start Stoatchat Services

7.1 Create Service Startup Script

cat > /root/stoatchat/start-services.sh << 'EOF'
#!/bin/bash
cd /root/stoatchat

# Start services in background
nohup ./target/debug/revolt-delta > api.log 2>&1 &
nohup ./target/debug/revolt-bonfire > events.log 2>&1 &
nohup ./target/debug/revolt-autumn > files.log 2>&1 &
nohup ./target/debug/revolt-january > proxy.log 2>&1 &
nohup ./target/debug/revolt-gifbox > gifbox.log 2>&1 &

echo "All Stoatchat services started"
EOF

chmod +x /root/stoatchat/start-services.sh

7.2 Start Services

cd /root/stoatchat
./start-services.sh

Step 8: Verification

8.1 Check Services

# Check processes
ps aux | grep revolt

# Check ports
ss -tlnp | grep -E "(14702|14703|14704|14705|14706|7880)"

# Test endpoints
curl -k https://api.YOUR_DOMAIN/
curl -k https://files.YOUR_DOMAIN/
curl -k https://proxy.YOUR_DOMAIN/
curl -k https://voice.YOUR_DOMAIN/

8.2 Expected Responses

  • API: {"revolt":"0.10.3","features":...}
  • Files: {"autumn":"Hello, I am a file server!","version":"0.10.3"}
  • Proxy: {"january":"Hello, I am a media proxy server!","version":"0.10.3"}
  • Voice: OK

9.1 Create Systemd Service Files

# Create service for each component
cat > /etc/systemd/system/stoatchat-api.service << 'EOF'
[Unit]
Description=Stoatchat API Server
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
User=root
WorkingDirectory=/root/stoatchat
ExecStart=/root/stoatchat/target/debug/revolt-delta
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Repeat for other services...
systemctl daemon-reload
systemctl enable stoatchat-api
systemctl start stoatchat-api

Step 10: Frontend Setup (Future)

The main domain currently shows a placeholder. To complete the setup:

  1. Deploy a Revolt.js frontend or compatible client
  2. Update nginx configuration to serve the frontend
  3. Configure the frontend to use your API endpoints

Security Considerations

  1. Change all default passwords in the configuration files
  2. Generate new API keys for LiveKit and VAPID
  3. Set up firewall rules to restrict access to internal ports
  4. Enable fail2ban for SSH protection
  5. Regular security updates for the system and Docker images

Backup Strategy

  1. Database: Regular MongoDB dumps
  2. Files: Backup MinIO data directory
  3. Configuration: Backup all .toml and .yml files
  4. SSL Certificates: Backup Let's Encrypt directory

Monitoring

Consider setting up monitoring for:

  • Service health checks
  • Resource usage (CPU, RAM, disk)
  • Log aggregation
  • SSL certificate expiration
  • Database performance

This deployment guide captures the complete process used to set up Stoatchat on the Seattle VM. Adjust domain names, passwords, and paths as needed for your specific deployment.