7.6 KiB
Deployment Guide — ETC PRS Viewer & Editor
This guide covers deploying the app to a Digital Ocean Droplet running Ubuntu 24.04.
Prerequisites
- A Digital Ocean Droplet (1 GB RAM minimum, 2 GB recommended)
- A domain name pointed at your droplet's IP
- SSH access to the droplet
1. Initial Server Setup
# Update packages
sudo apt update && sudo apt upgrade -y
# 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 # should be v20.x
npm --version
# Install build tools (needed for better-sqlite3)
sudo apt install -y build-essential python3
# Install PM2 globally
sudo npm install -g pm2
# Install Nginx
sudo apt install -y nginx
# Install Certbot for SSL
sudo apt install -y certbot python3-certbot-nginx
2. Create the App User and Directories
# Create a dedicated user for the app (no login shell)
sudo useradd --system --shell /bin/false --home /opt/etc-prs prs
# Create app directory
sudo mkdir -p /opt/etc-prs/app
sudo chown -R prs:prs /opt/etc-prs
# Create data directory for SQLite (outside the app, survives redeployment)
sudo mkdir -p /var/lib/etc-prs
sudo chown -R prs:prs /var/lib/etc-prs
# Create log directory
sudo mkdir -p /var/log/etc-prs
sudo chown -R prs:prs /var/log/etc-prs
3. Deploy the Application
# Clone or copy your project to the server
# Option A: Git
cd /opt/etc-prs
sudo -u prs git clone https://github.com/yourusername/etc-prs-ui.git app
# Option B: Copy files via scp from your local machine
# scp -r ./etc-prs-ui user@your-droplet-ip:/tmp/
# sudo mv /tmp/etc-prs-ui /opt/etc-prs/app
# sudo chown -R prs:prs /opt/etc-prs/app
cd /opt/etc-prs/app
# Install dependencies (as the prs user)
sudo -u prs npm install
# Build the app
sudo -u prs npm run build
4. Environment Configuration
# Create the production .env file
sudo -u prs nano /opt/etc-prs/app/.env
Add the following contents:
DATABASE_URL=/var/lib/etc-prs/personalities.db
RATE_LIMIT_PUBLISH=5
RATE_LIMIT_READ=100
PUBLIC_BASE_URL=https://yourdomain.com
Replace yourdomain.com with your actual domain.
5. Configure PM2
Create the PM2 ecosystem file:
sudo -u prs nano /opt/etc-prs/app/ecosystem.config.cjs
module.exports = {
apps: [{
name: 'etc-prs',
script: '/opt/etc-prs/app/build/index.js',
cwd: '/opt/etc-prs/app',
user: 'prs',
env: {
NODE_ENV: 'production',
PORT: '3000',
HOST: '127.0.0.1',
},
error_file: '/var/log/etc-prs/error.log',
out_file: '/var/log/etc-prs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
restart_delay: 3000,
max_restarts: 10,
}]
};
Start the app with PM2:
# Start
sudo -u prs pm2 start /opt/etc-prs/app/ecosystem.config.cjs
# Save the process list so PM2 restores it on reboot
sudo -u prs pm2 save
# Set PM2 to start on system boot
sudo pm2 startup systemd -u prs --hp /opt/etc-prs
# Run the command PM2 outputs
Verify the app is running:
sudo -u prs pm2 status
# Should show etc-prs as "online"
# Check logs
sudo -u prs pm2 logs etc-prs --lines 50
6. Configure Nginx
sudo nano /etc/nginx/sites-available/etc-prs
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Proxy to SvelteKit
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;
proxy_cache_bypass $http_upgrade;
# Increase timeout for large uploads
proxy_read_timeout 30s;
client_max_body_size 2M;
}
}
Enable and test:
sudo ln -s /etc/nginx/sites-available/etc-prs /etc/nginx/sites-enabled/
sudo nginx -t # should say "syntax is ok"
sudo systemctl reload nginx
7. SSL with Let's Encrypt
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Follow the prompts. Certbot will automatically modify your Nginx config to add HTTPS and set up auto-renewal.
Verify auto-renewal works:
sudo certbot renew --dry-run
8. Database Backups
Set up a nightly cron job to back up the SQLite database:
sudo mkdir -p /var/backups/etc-prs
# Create backup script
sudo nano /opt/etc-prs/backup.sh
#!/bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_DIR=/var/backups/etc-prs
DB_PATH=/var/lib/etc-prs/personalities.db
# Create backup using SQLite's online backup
sqlite3 "$DB_PATH" ".backup $BACKUP_DIR/personalities-$DATE.db"
# Keep only the last 30 days of backups
find "$BACKUP_DIR" -name "personalities-*.db" -mtime +30 -delete
echo "Backup completed: personalities-$DATE.db"
sudo chmod +x /opt/etc-prs/backup.sh
# Add to cron (runs at 2am daily)
sudo crontab -e
Add this line:
0 2 * * * /opt/etc-prs/backup.sh >> /var/log/etc-prs/backup.log 2>&1
9. Redeployment
When you push updates:
cd /opt/etc-prs/app
# Pull latest code
sudo -u prs git pull
# Install any new dependencies
sudo -u prs npm install
# Rebuild
sudo -u prs npm run build
# Restart the app (zero-downtime reload)
sudo -u prs pm2 reload etc-prs
The SQLite database at /var/lib/etc-prs/personalities.db is never touched by redeployment since it lives outside the app directory.
10. Useful Commands
# View live logs
sudo -u prs pm2 logs etc-prs
# Restart app
sudo -u prs pm2 restart etc-prs
# Stop app
sudo -u prs pm2 stop etc-prs
# Check Nginx status
sudo systemctl status nginx
# View Nginx error log
sudo tail -f /var/log/nginx/error.log
# Open SQLite database directly
sqlite3 /var/lib/etc-prs/personalities.db
# Example queries
sqlite3 /var/lib/etc-prs/personalities.db "SELECT id, name, manufacturer, channel_count, created_at FROM personalities ORDER BY created_at DESC LIMIT 20;"
sqlite3 /var/lib/etc-prs/personalities.db "SELECT COUNT(*) FROM personalities;"
# Delete a personality by ID (if you need to moderate)
sqlite3 /var/lib/etc-prs/personalities.db "DELETE FROM personalities WHERE id = 'the_id_here';"
11. Firewall
# Allow SSH, HTTP, and HTTPS
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
12. Creating Admin Users
Admins are managed via a CLI script — there is no self-registration UI.
# Create your first admin (run from the app directory on the server)
cd /opt/etc-prs/app
node scripts/create-admin.js your-username your-password
# To update an existing admin's password (same command — it upserts)
node scripts/create-admin.js your-username new-password
# To add another admin
node scripts/create-admin.js another-user their-password
Requirements:
- Username: 2–32 characters
- Password: minimum 8 characters (use something strong)
The admin panel is available at https://yourdomain.com/admin.
Local Development
# Install dependencies
npm install
# Start dev server (uses ./dev.db automatically)
npm run dev
# The app runs at http://localhost:5173
# SQLite database is created at ./dev.db on first run