Discord Bot CI/CD: Deploying Updates Without Bot Downtime in 2026
Deploying your Discord bot should be one command (or zero commands — fully automatic). Manual deployments involving SSH, git pulls, and pm2 restarts while hoping nothing breaks are a recipe for eventual disaster. CI/CD eliminates the anxiety.
GitHub Actions: Automatic Deployment on Push
# .github/workflows/deploy.yml
name: Deploy Bot
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to VPS
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /home/bot/discord-bot
git pull origin main
npm ci --production
pm2 reload discord-bot
Set secrets in GitHub repository Settings → Secrets → Actions:
VPS_HOST— Your server IPVPS_USER— SSH username (e.g.bot)VPS_SSH_KEY— Private SSH key content
Every push to main triggers automatic deployment. The pm2 reload ensures graceful restart (not abrupt kill).
Running Tests Before Deployment
Add a test step to the pipeline:
- name: Run Tests
run: npm test
# If tests fail, the deploy step never runs
Even minimal tests (lint, type check) catch obvious errors before they reach production.
Rollback Strategy
When a deployment breaks the bot:
# On your VPS - rollback to previous version
cd /home/bot/discord-bot/
git revert HEAD --no-edit
git push origin main # CI/CD deploys the revert
# Or manually:
git checkout HEAD~1 -- src/
npm ci && pm2 reload discord-bot
Maintain a staging branch that deploys to a test bot (different token, same code) before merging to main. Deploy staging, verify in test server, then merge to main for production deployment.
Zero-Downtime Slash Command Updates
Discord slash commands are registered via API, not loaded from files. Register commands in a separate step before the bot restarts:
// deploy-commands.js (run before starting bot)
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
await rest.put(Routes.applicationCommands(CLIENT_ID), { body: commands });
console.log('Commands registered');
In the CI script, run node deploy-commands.js before pm2 reload to ensure commands are updated before the bot restarts.