You've built a Discord bot. It works perfectly on your laptop. But the moment you close the terminal, it goes offline. If you want your bot to serve a community around the clock, it needs a permanent home — a Virtual Private Server (VPS).
This guide walks you through the entire deployment pipeline: provisioning a VPS, securing it, installing the runtime, configuring process management, and setting up a systemd service so your bot restarts automatically after crashes or server reboots. Whether you're running a simple utility bot or a complex sharded application, these fundamentals remain the same.
Choosing and Provisioning Your VPS
Before you can deploy anything, you need a server. For most Discord bots, a modest VPS with 1–2 GB of RAM and 1 vCPU is sufficient. Popular providers include DigitalOcean, Linode (Akamai), Vultr, Hetzner, and Contabo. Hetzner and Contabo offer particularly strong price-to-performance ratios in Europe; DigitalOcean and Linode excel in ease of use and global coverage.
When creating your instance:
- OS: Ubuntu 22.04 LTS or 24.04 LTS (long-term support, wide community knowledge)
- Region: Choose a data center geographically close to your Discord voice region or your user base for lower latency
- Authentication: Use SSH keys, not passwords. Disable password authentication entirely
Once the VPS is provisioned, connect via SSH:
ssh root@your_vps_ip
Initial Server Hardening
Never run your bot as root. Create a dedicated user with sudo privileges, then lock down SSH.
# Create a new user (replace 'botuser' with your preferred name)
adduser botuser
usermod -aG sudo botuser
# Switch to the new user
su - botuser
# Set up SSH key for the new user (on your LOCAL machine)
ssh-copy-id botuser@your_vps_ip
Now harden the SSH daemon:
sudo nano /etc/ssh/sshd_config
Change or ensure these settings:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Restart SSH to apply changes:
sudo systemctl restart sshd
Critical: Keep your current root session open. Open a second terminal and verify you can log in as botuser with your key before closing the root session.
Always test your new SSH user in a separate terminal before closing your root session. Locking yourself out is the most common first-step mistake.
Installing Node.js and Essential Tools
Most Discord bots are written in Node.js (discord.js) or Python (discord.py). This guide assumes Node.js, but the process management steps apply equally to Python.
Install Node.js via NodeSource for the latest LTS version:
# Install Node.js 20 LTS (current as of writing)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify installation
node --version # Should output v20.x.x
npm --version # Should output 10.x.x
# Install git and build tools for native dependencies
sudo apt-get install -y git build-essential
Deploying Your Bot Code
There are two common approaches: cloning a private Git repository or uploading files via SFTP/SCP. Git is preferred for version control and easy updates.
# Create a project directory
mkdir -p ~/discord-bot
cd ~/discord-bot
# Clone your repository (replace with your repo URL)
git clone https://github.com/yourusername/your-bot-repo.git .
# Install production dependencies only
npm ci --only=production
If your bot requires environment variables (token, database URLs, API keys), create a .env file:
nano .env
DISCORD_TOKEN=your_bot_token_here
DATABASE_URL=postgresql://user:pass@localhost:5432/botdb
NODE_ENV=production
Secure the file:
chmod 600 .env
Your bot token is a secret. Treat it like a password — never commit it to version control, never share it, and rotate it immediately if exposed.
Hesham Kholef
Process Management with PM2
PM2 is a production-grade process manager for Node.js. It keeps your bot alive, restarts it on crash, manages logs, and enables zero-downtime reloads.
# Install PM2 globally
sudo npm install -g pm2
# Start your bot with PM2
pm2 start index.js --name "discord-bot" --time
# Save the process list so PM2 resurrects it on reboot
pm2 save
# Generate the startup script for systemd
pm2 startup systemd -u botuser --hp /home/botuser
The pm2 startup command will output a command you must run with sudo. Copy and execute it exactly as shown. This creates a systemd unit that launches PM2 (and your bot) on boot.
Verify the setup:
pm2 status
pm2 logs discord-bot
Creating a Native systemd Service (Alternative to PM2 Startup)
Some developers prefer a direct systemd unit instead of PM2's generated one. This gives you full control over the service definition and avoids an extra abstraction layer.
sudo nano /etc/systemd/system/discord-bot.service
Paste the following, adjusting paths and user as needed:
[Unit]
Description=Discord Bot
After=network.target
[Service]
Type=simple
User=botuser
WorkingDirectory=/home/botuser/discord-bot
ExecStart=/usr/bin/node index.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
EnvironmentFile=/home/botuser/discord-bot/.env
StandardOutput=journal
StandardError=journal
SyslogIdentifier=discord-bot
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/home/botuser/discord-bot
[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable discord-bot
sudo systemctl start discord-bot
sudo systemctl status discord-bot
View logs with:
journalctl -u discord-bot -f
Reverse Proxy and SSL (Optional but Recommended)
If your bot exposes a web dashboard, API, or OAuth callback, put it behind Nginx with a valid TLS certificate.
sudo apt-get install -y nginx certbot python3-certbot-nginx
# Configure Nginx (example for a dashboard on port 3000)
sudo nano /etc/nginx/sites-available/bot.dashboard.conf
server {
listen 80;
server_name bot.yourdomain.com;
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;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Enable site and get certificate
sudo ln -s /etc/nginx/sites-available/bot.dashboard.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d bot.yourdomain.com
Updating Your Bot
Deploying updates should be fast and safe. With Git and PM2:
cd ~/discord-bot
git pull origin main
npm ci --only=production
pm2 reload discord-bot
If using a native systemd service:
cd ~/discord-bot
git pull origin main
npm ci --only=production
sudo systemctl restart discord-bot
Consider a simple deploy script or GitHub Actions workflow to automate this.
Monitoring and Maintenance
A production bot deserves basic observability:
- Logs:
journalctl -u discord-bot -forpm2 logs - Metrics: Install
pm2 install pm2-server-monitfor CPU/memory dashboards - Uptime: Use UptimeRobot, Better Uptime, or a self-hosted Prometheus + Grafana stack
- Backups: Snapshot your VPS weekly; back up databases daily
Set up log rotation to prevent disk exhaustion:
sudo nano /etc/logrotate.d/discord-bot
/home/botuser/discord-bot/logs/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 640 botuser botuser
}
Common Pitfalls to Avoid
- Running as root: Compromised bot = compromised server. Always use a dedicated unprivileged user.
- Hardcoding secrets: Use
.envfiles with restricted permissions, or a secrets manager. - No restart policy: Crashes happen. Memory leaks happen.
Restart=always(systemd) or PM2's built-in restart is non-negotiable. - Ignoring updates:
apt update && apt upgraderegularly. Subscribe to Ubuntu security announcements. - Single point of failure: For critical bots, run multiple instances behind a load balancer or use Discord's sharding.
Your Bot Has a Home
You now have a Discord bot running on a hardened VPS, managed by a production process supervisor, secured with SSH keys, and configured to survive reboots. The infrastructure is invisible to your users — they only see a bot that's always there, always responsive.
The patterns here — dedicated users, process management, systemd, environment-based configuration, log rotation — apply far beyond Discord bots. They're the foundation of any reliable self-hosted service. Master them once, and every future deployment becomes faster, safer, and more predictable.
Your community thanks you for the uptime. Your future self thanks you for the maintainability.
Comments (0)