Host a Discord Bot 24/7 - Complete Tutorial

Step-by-step guide to host your Discord bot in production 24/7 on a VPS. With PM2 (Node.js) and systemd (Python).

Introduction

You've built a Discord bot with discord.js or discord.py? To keep it running 24/7, you need a VPS. Here's how.

Step 1: Choose and order a VPS

An entry-level VPS (1 vCPU, 1-2 GB RAM, NVMe) is enough for most Discord bots. At By-Hoster, count around 4.99 €/month.

  1. Go to the By-Hoster client area
  2. Choose "VPS Linux" → Ubuntu 22.04 LTS distribution
  3. Validate and pay, the VPS is delivered in 60 seconds by email with SSH access

Step 2: SSH connection and preparation

  1. Connect via SSH: ssh root@your-ip
  2. Update the system: apt update && apt upgrade -y
  3. Create a non-root user: adduser botuser && usermod -aG sudo botuser
  4. Install Node.js 20 LTS: curl -fsSL https://deb.nodesource.com/setup_20.x | bash && apt install -y nodejs (for Node) or Python 3: apt install -y python3 python3-pip python3-venv (for Python)

Step 3: Deploy your bot

  1. Upload your code via Git: git clone https://github.com/you/your-bot.git
  2. Install dependencies: npm install (Node) or pip install -r requirements.txt (Python)
  3. Create a .env file with your Discord token, never commit it to Git
  4. Test by launching manually: node bot.js or python bot.py

Step 4: Keep the bot running 24/7

For Node.js: use PM2.

  1. Install PM2: npm install -g pm2
  2. Start your bot: pm2 start bot.js --name discord-bot
  3. Configure auto-startup on reboot: pm2 startup && pm2 save
  4. View logs: pm2 logs discord-bot

Step 5: Harden your Discord bot VPS

A Discord bot exposed on the Internet is a prime target for SSH bruteforce, port scans and exploit attempts. Three hardening measures eliminate 99% of threats: SSH key-only authentication, restrictive UFW firewall and fail2ban against dictionary attacks.

  1. Disable SSH password login: generate a key on your machine (ssh-keygen -t ed25519), copy it to the VPS (ssh-copy-id botuser@your-ip), then edit /etc/ssh/sshd_config and set PasswordAuthentication no + PermitRootLogin no. Reload: systemctl restart sshd
  2. Enable UFW (Ubuntu firewall): ufw default deny incoming && ufw default allow outgoing && ufw allow ssh && ufw enable. Your Discord bot establishes outbound connections (to gateway.discord.gg), no inbound ports needed
  3. Install fail2ban: apt install -y fail2ban && systemctl enable --now fail2ban. Automatically blocks any IP that attempts more than 5 failed SSH logins in 10 minutes (1-hour ban by default)
  4. Automatic security updates: apt install -y unattended-upgrades && dpkg-reconfigure --priority=low unattended-upgrades. Critical patches (CVEs) install without intervention
  5. Store the Discord token in a .env outside the repo: add .env to your .gitignore, load it with dotenv (Node) or python-dotenv (Python). Never commit your token to GitHub, Discord revokes leaked tokens within seconds

Troubleshooting: my Discord bot crashes, what to do?

A bot crashing in production is rarely due to a failing VPS, in 90% of cases it's an application error (expired token, unhandled exception, memory overflow, Discord rate limit). Here's the diagnostic checklist to run through in order.

  • Check process status: pm2 status (Node) or systemctl status my-bot (Python). Look for "online" / "active (running)". If "errored" / "failed", proceed to next point
  • Read recent logs: pm2 logs discord-bot --lines 100 or journalctl -u my-bot -n 100 --no-pager. Typical Discord logs to identify: DiscordAPIError[401] = invalid or revoked token, DiscordAPIError[429] = rate limit, UnhandledPromiseRejection = unhandled async exception
  • Invalid token (401 Unauthorized): regenerate a new token on discord.com/developers/applications → Bot → Reset Token. Update your .env then pm2 restart discord-bot
  • Bot OOM-killed (out of memory): check with dmesg | grep -i "killed process". If confirmed, either your code has a memory leak (cache growing indefinitely), or the VPS is undersized, upgrade to 4 GB RAM
  • Bot offline but process active: Discord likely closed the WebSocket. Enable autoReconnect: true in discord.js (default) or implement an on('disconnect') handler that relaunches client.login()
  • Force a clean restart: pm2 restart discord-bot --update-env reloads both code AND environment variables. To start completely fresh: pm2 delete discord-bot && pm2 start bot.js --name discord-bot
  • Continuous monitoring: configure pm2 install pm2-logrotate (avoids filling the disk) and a Discord webhook via pm2-discord-webhook that alerts you if the bot crashes more than 3 times in 5 minutes

Going further: adding a database to your bot

A serious Discord bot needs persistence: per-server configuration, user statistics, virtual economy, slash command cache, moderation (warns, mutes). Three choices depending on your need: PostgreSQL for reliable relational data, MongoDB for flexible schemas, Redis for fast cache and leaderboards.

  • PostgreSQL (recommended for 80% of bots): apt install -y postgresql postgresql-contrib. Create a database: sudo -u postgres createdb mybot. Ideal with Prisma ORM (Node): npm i @prisma/client prisma then define your models in schema.prisma and run npx prisma migrate dev. Type-safe, versioned migrations, perfect for data like guilds/users/warnings
  • MongoDB (for evolving schemas): apt install -y mongodb-org then systemctl enable --now mongod. Use Mongoose (Node): npm i mongoose. Convenient to store nested JSON configurations (server settings with role lists, custom channels, etc.) without defining a rigid schema upfront
  • Redis (cache + real-time leaderboards): apt install -y redis-server. Use ioredis (Node) or redis-py (Python). Ideal for: slash command cache (TTL 1h), XP counting in sorted set (ZADD leaderboard 1500 user_id then ZREVRANGE leaderboard 0 9 WITHSCORES for top 10), per-user anti-spam rate limit
  • Minimal Prisma example (Node + PostgreSQL): model Guild { id String @id; prefix String @default("!"); xpEnabled Boolean @default(true); warnings Warning[] } then in your bot: const guild = await prisma.guild.upsert({ where: { id: msg.guildId }, update: {}, create: { id: msg.guildId } }). The bot persists per-server settings reliably
  • Best practices: back up your database daily (pg_dump mybot > backup.sql via cron), never expose PostgreSQL/MongoDB on the public IP (UFW deny everything except SSH), use a connection pool to avoid saturating the DB if your bot scales

Frequently asked questions

For a simple bot: 1 GB RAM minimum. For a bot with a database and several hundred servers: 2-4 GB RAM. The entry-level By-Hoster VPS at 4.99 €/month is enough for 90% of bots.

Yes, provided you configured pm2 startup (Node) or a systemd service (Python). The bot restarts automatically after reboot.

PM2 is a Node.js process manager with integrated dashboard, colored logs, real-time CPU/RAM monitoring and clustering, ideal for discord.js. systemd is the native Linux service manager: lower-level but universal (Node, Python, Go, Rust). For Python with discord.py/aiogram, systemd is generally preferred since PM2 is designed for Node.