DeployCI/CDZero downtime5 min read

Update your bot with zero downtime

Deploy strategies with git pull + PM2 reload, blue-green with systemd and CI/CD with GitHub Actions.


Why zero downtime?

Every time you restart your bot, users see it disconnect briefly. With the right techniques you can minimize or eliminate that dead time.

Method 1: PM2 Reload (graceful)

PM2 reload starts a new instance before killing the old one:

bash
#!/bin/bash
# deploy.sh
cd /opt/bots/my-bot
git pull origin main
npm install --production
pm2 reload my-bot
echo "Deploy complete: $(date)"

For this to work correctly, handle graceful shutdown:

javascript
process.on('SIGINT', async () => {
  console.log('Closing connections...');
  await client.destroy();
  process.exit(0);
});

Method 2: Blue-Green with systemd

Keep two instances and alternate between them:

bash
# Structure
/opt/bots/my-bot-blue/   # Active version
/opt/bots/my-bot-green/  # New version

Deploy script:

bash
#!/bin/bash
ACTIVE=$(systemctl is-active discord-bot-blue && echo 'blue' || echo 'green')
NEW=$([ "$ACTIVE" = 'blue' ] && echo 'green' || echo 'blue')

cd /opt/bots/my-bot-$NEW
git pull origin main
npm install --production

# Start the new version
sudo systemctl start discord-bot-$NEW
sleep 5

# Verify it started correctly
if systemctl is-active --quiet discord-bot-$NEW; then
  sudo systemctl stop discord-bot-$ACTIVE
  echo "Switched to $NEW"
else
  sudo systemctl stop discord-bot-$NEW
  echo "Deploy failed, $ACTIVE still active"
  exit 1
fi

Method 3: CI/CD with GitHub Actions

Automate deployment on every push to main:

yaml
# .github/workflows/deploy.yml
name: Deploy Bot
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: deploy
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/bots/my-bot
            git pull origin main
            npm install --production
            pm2 reload my-bot

Configure secrets in your GitHub repository:

  • VPS_HOST: Your Baires Host VPS IP
  • SSH_PRIVATE_KEY: SSH private key for the deploy user

Graceful shutdown in discord.js

javascript
const shutdown = async () => {
  console.log('Shutdown initiated...');
  // Save pending state to DB
  await saveState();
  // Disconnect the bot
  client.destroy();
  process.exit(0);
};

process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);

Recommendations

  • Use pm2 reload instead of pm2 restart for zero downtime
  • Implement graceful shutdown to avoid data loss
  • Test deployments in a staging environment before production
  • Set up deploy success/failure notifications in Discord

Was this guide helpful?