Deployment
Running with PM2
PM2 keeps all three services alive, restarts them on crash, and manages log files. The repository includes a pre-configured ecosystem.config.cjs that covers all three processes.
Start all services
pm2 start ecosystem.config.cjs
Check status
pm2 status
View logs
pm2 logs # all services
pm2 logs api # API only
pm2 logs bot # bot only
pm2 logs web # web only
Persist across reboots
pm2 save
pm2 startup
# Follow the printed instruction — it will give you a command to run as root
Restart after a rebuild
After rebuilding any package, restart the affected service:
pnpm --filter @arkenbot/api build && pm2 restart api
pnpm --filter @arkenbot/bot build && pm2 restart bot
pnpm --filter @arkenbot/web build && pm2 restart web
If PM2 was started by a specific system user (e.g. bot), always prefix commands with sudo -u bot pm2 .... Running PM2 as the wrong user will not find the running processes.
Nginx Reverse Proxy
Nginx lets you expose the dashboard and API on standard ports (80/443) behind a domain name.
sudo apt-get install -y nginx certbot python3-certbot-nginx
Create /etc/nginx/sites-available/arkenbot:
server {
listen 80;
server_name yourdomain.com;
# Web dashboard (Next.js on port 3000)
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# REST API (Fastify on port 4000)
location /api/ {
rewrite ^/api/(.*) /$1 break;
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket endpoint
location /socket.io/ {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/arkenbot /etc/nginx/sites-enabled/
sudo nginx -t # verify configuration
sudo systemctl reload nginx
HTTPS with Let's Encrypt
sudo certbot --nginx -d yourdomain.com
Certbot configures HTTPS automatically and sets up auto-renewal. After running it, update your .env to use https:// and wss:// URLs, then rebuild the web package and restart the web PM2 process.
Lavalink — Music Support
Music commands require a running Lavalink server. If you do not need music, skip this section.
- Download the latest
Lavalink.jarfrom the Lavalink releases. - Create an
application.ymlconfig file (the Lavalink docs provide a template). - Run Lavalink:
java -jar Lavalink.jar - Set these variables in your
.env:
LAVALINK_HOST=localhost
LAVALINK_PORT=2333
LAVALINK_PASSWORD=youshallnotpass # must match your application.yml
LAVALINK_SECURE=false # set to true if using TLS
YouTube cookies (optional): Server IPs are sometimes blocked by YouTube. If music fails, export cookies from your browser using the "Get cookies.txt LOCALLY" Chrome extension while signed into YouTube, save the file on the server, and set:
YOUTUBE_COOKIES_FILE=/path/to/youtube-cookies.txt
Updating
Pull the latest code, rebuild, and restart:
git pull
pnpm install # pick up any new or changed dependencies
pnpm db:push # apply any schema changes (safe to run with no changes)
pnpm build # rebuild all packages
pm2 restart all
If the Prisma schema changed, also regenerate the client:
pnpm db:generate
Quick Reference
# Start all services
pm2 start ecosystem.config.cjs
# Rebuild and restart one service
pnpm --filter @arkenbot/api build && pm2 restart api
pnpm --filter @arkenbot/bot build && pm2 restart bot
pnpm --filter @arkenbot/web build && pm2 restart web
# Apply a schema change
pnpm db:push && pnpm db:generate && pm2 restart all
# Deploy slash commands
pnpm deploy:commands
# Grant staff access to a Discord user
pnpm grant-staff <discord-user-id>