Updating your Discord bot shouldn't mean SSH-ing into a server, pulling code, and manually restarting. A CI/CD pipeline deploys your bot automatically when you push to Git.
The Goal
Push code to GitHub -> Tests run automatically -> Bot deploys to server -> Zero downtime
No manual steps. No forgotten restarts. No "works on my machine" surprises.
Git Workflow
Branch Strategy
| Branch | Purpose | Deploys To | |--------|---------|------------| | main | Production code | Live bot | | develop | Staging/testing | Test bot (optional) | | feature/* | New features | Nothing (PR only) |
All changes go through pull requests into develop, then merged to main for production deployment.
Pre-Commit Hooks
Catch issues before they reach the repository:
{
"scripts": {
"precommit": "npm run lint && npm test"
}
}
Automated Testing
Unit Tests
Test your bot's command handlers without connecting to Discord:
// tests/commands.test.js
const { handlePing } = require('../commands/ping');
test('ping command returns pong', () => {
const mockMessage = {
reply: jest.fn()
};
handlePing(mockMessage);
expect(mockMessage.reply).toHaveBeenCalledWith('Pong!');
});
Integration Tests
Test database operations and API calls:
test('user data saves correctly', async () => {
await saveUser({ id: '123', points: 100 });
const user = await getUser('123');
expect(user.points).toBe(100);
});
GitHub Actions Pipeline
Create .github/workflows/deploy.yml:
name: Deploy Bot
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /home/botuser/bot
git pull origin main
npm ci --production
pm2 restart my-bot
Required Secrets
In your GitHub repository settings, add:
SERVER_HOST: Your VPS IP addressSERVER_USER: SSH usernameSSH_KEY: Private SSH key for authentication
Zero-Downtime Deployment
Graceful Shutdown
Your bot should handle shutdown signals:
process.on('SIGINT', async () => {
console.log('Shutting down gracefully...');
client.destroy();
await database.close();
process.exit(0);
});
PM2 Reload
Use pm2 reload instead of pm2 restart for zero-downtime:
pm2 reload my-bot
PM2 starts a new instance before stopping the old one. The transition is seamless.
Rollback Strategy
If a deployment breaks something:
# On the server
git revert HEAD
pm2 restart my-bot
Or revert the merge on GitHub and let the pipeline redeploy automatically.
Monitoring Deployments
Add a deployment notification to your pipeline:
- name: Notify Discord
run: |
curl -H "Content-Type: application/json" -d '{"content":"Bot deployed! Version: ${{ github.sha }}"}' ${{ secrets.DISCORD_WEBHOOK }}
Your team sees every deployment in Discord with the exact commit that was deployed.
Hosting for CI/CD
CI/CD works best with hosting that gives you SSH access and process management. Space-Node's Discord bot hosting plans include the access needed to set up automated deployments.
A good CI/CD pipeline means you can push fixes in 30 seconds from your phone (merge a PR) and have the bot updated automatically.
