How to Host a Discord Bot 24/7 on a Linux VPS
Complete guide to deploy a Discord bot (discord.js or discord.py) on a Linux VPS with PM2 or systemd for 24/7 uptime and auto-restart on reboot.
A Discord bot running on your laptop works fine until the first Windows reboot or Wi-Fi outage. To keep a bot available 24 hours a day, you need a server that stays on all the time — and a cheap Linux VPS solves that for under USD 6/month with room to grow.
This tutorial targets developers who already have a working bot in discord.js (Node.js) or discord.py (Python) and want to deploy it to production for the first time. You will learn how to prepare the VPS, transfer your code, manage the process with PM2 or systemd, and make sure the bot restarts automatically if the VPS reboots.
Estimated time: 30-45 minutes from the first SSH connection to a bot online with auto-restart configured.
Prerequisites
A VPS running Ubuntu 24.04 LTS (any 1 vCPU / 2 GB plan covers small bots), SSH access as root or a user with sudo, and a working bot codebase locally — a token generated in the Discord Developer Portal and the bot added to at least one test server.
Ubuntu 24.04 LTS 1 vCPU / 1 GB RAM SSH with sudo 22 (default) Keep the bot token handy (from
https://discord.com/developers/applications → your application → Bot →
Reset Token). You will paste this token into a .env file on the VPS.
SSH connection and dedicated user
Running a bot as root is a bad practice — any compromised dependency becomes full access to the VPS. Create a dedicated user to isolate the process.
Connect to the VPS via SSH from your local terminal:
ssh root@your-vps-ipReplace your-vps-ip with the IP your provider delivered. On the first
connection, accept the server fingerprint by typing yes.
Create a bot user with a home directory and bash shell:
adduser bot
usermod -aG sudo botThe first line asks for a password (pick a strong one) and a few optional fields you can skip with Enter. The second adds the user to the sudo group so they can install packages when needed.
Switch to the bot user and confirm permissions are correct:
su - bot
whoamiOutput should be bot. From here on, every command runs as this user
(not as root).
Installing the runtime
Choose the stack according to your bot’s language. This section covers Node.js (for discord.js) and Python (for discord.py) — skip to the appropriate subsection.
Node.js for discord.js
The recommended way to install Node.js on Ubuntu is via NodeSource, which keeps packages updated for each LTS major version.
Install Node.js 22 LTS (current in 2026):
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejsThe first command adds the official NodeSource repository to apt. The second installs Node.js along with npm.
Verify the installed versions:
node --version
npm --versionYou should see v22.x.x and something like 10.x.x respectively.
Python for discord.py
Ubuntu 24.04 ships with Python 3.12. You only need venv and pip to isolate dependencies.
Install Python tools:
sudo apt update
sudo apt install -y python3-pip python3-venv gitpython3-venv lets you create isolated virtual environments, avoiding
conflicts between dependencies of different bots or with system packages.
Transferring the code
You can upload your code via Git (recommended — keeps history and makes updates easier) or SCP directly from your computer.
If your code is on GitHub/GitLab, clone it into the bot user home:
cd ~
git clone https://github.com/your-username/your-bot.git
cd your-botFor private repositories, configure an SSH deploy key or use a Personal Access Token in the URL.
Alternative via SCP — run this on your local machine, not on the VPS:
scp -r ./your-bot bot@your-vps-ip:/home/bot/Reconnect to the VPS afterwards (ssh bot@your-vps-ip) and enter the
directory with cd ~/your-bot.
Install project dependencies. For Node.js:
npm install --productionFor Python (with venv):
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtThe --production flag on npm skips devDependencies (tests, linter),
saving space and install time.
Environment variables
Never leave the bot token hardcoded in the source. Use a .env file with
restricted permissions.
Create the .env file at the project root:
nano .envAdd the required variables — example for discord.js:
DISCORD_TOKEN=your-token-here
NODE_ENV=productionSave with Ctrl+O, Enter, Ctrl+X.
Restrict the file permission so only the bot user can read it:
chmod 600 .envThis prevents other users on the system (if any) from reading the token.
Add .env to your .gitignore before the first commit. A token leaked
in a public repository is one of the most common bot compromise vectors —
malicious bots scan GitHub for these 24/7.
Process management with PM2
PM2 is the de facto process manager for the Node.js ecosystem. It does automatic restart on crash, keeps structured logs, and integrates with systemd to survive a reboot.
Install PM2 globally:
sudo npm install -g pm2The -g flag installs it under /usr/lib/node_modules, available to all
users.
Start the bot with PM2:
pm2 start index.js --name my-botReplace index.js with your bot’s entrypoint and my-bot with a name
you can identify. PM2 will display a table with PID, status, and memory
usage.
Configure PM2 to start on VPS boot:
pm2 startup
pm2 saveThe first command prints a sudo env PATH=... pm2 startup ... line —
copy and run that exact line. The second saves the current process state
so it can be restored on the next boot.
Process management with systemd
For Python bots or when you prefer the base Linux stack, systemd is the native alternative. Nothing to install — it ships with Ubuntu.
Create a systemd service file:
sudo nano /etc/systemd/system/my-bot.servicePaste the content below, adjusting paths and command:
[Unit]
Description=My Discord Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=bot
WorkingDirectory=/home/bot/your-bot
ExecStart=/home/bot/your-bot/venv/bin/python3 bot.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetFor Node.js without PM2, swap ExecStart for
/usr/bin/node /home/bot/your-bot/index.js.
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable my-bot
sudo systemctl start my-botdaemon-reload makes systemd reread the service files. enable
configures it to start on boot. start starts it now.
Verification
Confirm the bot is running and responding on Discord.
With PM2:
pm2 status
pm2 logs my-bot --lines 50
You should see the bot with online status in the table and logs
showing messages like Logged in as MyBot#1234.
With systemd:
sudo systemctl status my-bot
sudo journalctl -u my-bot -n 50 -f
Output should show active (running) in green. The -f flag on
journalctl tails logs in real time (Ctrl+C to exit).
On Discord itself, go to your test server and send a command the bot responds to. Confirm it appears online (green dot) in the member list.
Troubleshooting
Bot shows offline status even though the process is running
Usually an invalid token (you generated a new one and forgot to update
.env) or the bot does not have the right intent enabled in the
Developer Portal. Check PM2/journalctl logs for Invalid Token or
DisallowedIntents.
”EADDRINUSE” error at startup
You have another instance of the bot running. List with pm2 list or
sudo systemctl status my-bot and stop the duplicate before restarting.
Bot consumes growing RAM until it crashes
Memory leak in the code — common in message handlers that accumulate
state without clearing it. Use pm2 monit to watch usage in real time.
To mitigate while investigating, configure --max-memory-restart 500M
in PM2, which restarts the process when it exceeds 500 MB.
Next steps
With the bot running 24/7, consider the next production-focused steps:
- Configure automatic backups of the code and database (if any) to
another location —
rsyncvia cron to another server or an S3-compatible service - Add external monitoring (UptimeRobot, Healthchecks.io) that pings your bot or a health-check endpoint
- Implement structured logging (Winston for Node.js, structlog for Python) and centralize it in Loki or Elasticsearch if you run multiple bots
- Use native sharding from discord.js/discord.py if the bot grows past 2,500 servers
- If you are putting this into serious production, a Hostini VPS in Brazil delivers low latency to Discord’s South American gateways and scheduled snapshots to restore the bot in minutes if something goes wrong.
Frequently asked questions
PM2 or systemd: which is better for running a Discord bot?
PM2 is more convenient for Node.js developers — cluster mode, structured logs, and `pm2 monit` work out of the box. Systemd is the Linux default, has no extra dependency, and integrates natively with journald for logs. If you only have one Node.js bot, PM2 saves time. If you run bots in different languages or prefer the base Ubuntu stack, systemd is more consistent.
How much RAM and CPU does a Discord bot consume?
A small discord.js bot sits between 80-150 MB of RAM at idle and uses negligible CPU (<1%). Bots with large guild caches (10k+ members), music, or AI features can rise to 300-800 MB. A 1 vCPU / 2 GB RAM VPS comfortably handles several small bots running together.
Why does my bot crash on its own after a few hours?
Common causes: token invalidated by a race condition from multiple instances running at the same time, Discord rate limit (429), an unhandled error in an event handler that kills the process, or kernel OOM kill when the VPS runs out of RAM. Check `pm2 logs` or `journalctl -u your-bot.service -e` to find the stack trace right before the crash.
Do I need a static IP to host a Discord bot?
No. The bot opens an outbound WebSocket connection to api.discord.com — the bot initiates the handshake, not Discord. Any VPS with a public IP works. A static IP only matters if you also run a webhook server receiving HTTP events from Discord, and even then a subdomain plus DNS solves it.
How do I deploy a bot update without losing uptime?
With PM2: `git pull && pm2 reload your-bot` does a zero-downtime reload keeping the process alive. With systemd: `git pull && sudo systemctl restart your-bot` causes ~2-5s of offline time. To avoid that, run two instances with sharding and restart one at a time — only worth it if your bot is in 2,500+ servers.
Is it safe to keep the bot token directly in .env?
Yes, as long as the .env file has permission 600 (`chmod 600 .env`) and is never committed to Git (add it to .gitignore). The real risk is leaking it via screenshot, public log, or misconfigured backup. On larger teams, use a secret manager (HashiCorp Vault, AWS Secrets Manager), but for a solo developer on a dedicated VPS, a .env file with restricted permission is acceptable.