Updated 4 November 2026
To run a Discord bot 24/7 on a VPS, you install Node.js or Python, put your code in its own user's folder, and let systemd start it: systemd starts the bot when the server boots, restarts it within seconds after a crash, and keeps its logs. You don't need screen, tmux or pm2 for that. The whole setup takes about 15 minutes on Ubuntu 24.04 or Debian 12/13.
Checked on 29 October 2026: Ubuntu 24.04 and Debian 12 ship Node.js 18 in their own repositories, and discord.js 14.27.0 needs Node.js 18 or newer (npm). discord.py 2.7.1 needs Python 3.8 or newer (PyPI); Ubuntu 24.04 has Python 3.12.
1. Prepare the server
Follow first steps on a new VPS first (SSH key, firewall, updates). A bot needs no open ports: it connects out to Discord. Then make a separate user, so the bot can't touch the rest of the system:
adduser --disabled-password --gecos "" bot
2. Install the runtime
Node.js (discord.js):
apt install -y nodejs npm git
node --version # 18 or newer
Python (discord.py):
apt install -y python3 python3-venv git
On Ubuntu 24.04 and Debian 12, pip install outside a virtual environment stops with externally-managed-environment. That's on purpose: give every bot its own venv, as below.
3. Put your code on the server
su - bot
git clone https://github.com/you/your-bot.git mybot # or upload with scp/SFTP
cd mybot
nano .env # DISCORD_TOKEN=your-token
chmod 600 .env
Then install the packages, still as the bot user:
npm install --omit=dev # Node.js
python3 -m venv .venv # Python
.venv/bin/pip install -r requirements.txt
Test it once by hand (node index.js or .venv/bin/python app.py), check that the bot comes online in Discord, stop it with Ctrl+C and type exit to become root again.
4. Let systemd run it
Create /etc/systemd/system/mybot.service:
[Unit]
Description=My Discord bot
After=network-online.target
Wants=network-online.target
[Service]
User=bot
WorkingDirectory=/home/bot/mybot
ExecStart=/usr/bin/node index.js
# Python: ExecStart=/home/bot/mybot/.venv/bin/python app.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now mybot
systemctl status mybot
enable starts the bot at every boot; Restart=always starts it again 5 seconds after a crash. Your code keeps reading the token from .env with dotenv or python-dotenv, just like on your own PC.
5. Daily use
- Live logs:
journalctl -u mybot -f(Ctrl+C to stop watching). - Restart after a code change:
su - bot -c "cd mybot && git pull", thensystemctl restart mybot. - A second bot: a second folder and a second service file, for example
otherbot.service. - Memory:
systemctl status mybotshows how much it uses. AddMemoryMax=512Munder[Service]if one bot must never take the whole server down.
Which one should I pick?
- One bot, nothing else: bot hosting, 512 MB for €0.69 per month paid per quarter (€0.79 monthly billing), 1 GB for €1.49 (€1.79), or 2 GB for €2.09 (€2.29) for music or AI bots with big caches.
- Several bots, a database server, a web dashboard or other software: VPS S with 2 vCPU, 4 GB RAM and 40 GB NVMe in Nuremberg, €7.99 per month paid per quarter (€8.29 monthly billing).
Frequently asked questions
How do I keep a Discord bot running on a VPS after I close SSH? Run it as a systemd service with Restart=always and enable it. It then keeps running after you log out, starts at boot and restarts after a crash.
Should I use pm2, screen or systemd? systemd is already on every Ubuntu, Debian, Rocky and AlmaLinux server and handles boot, crash restarts and logs. pm2 also works for Node.js, but you don't need it. screen and tmux don't restart a crashed bot.
Why does pip say externally-managed-environment? Ubuntu 24.04 and Debian 12 protect the system Python. Make a virtual environment with python3 -m venv .venv and install your packages with .venv/bin/pip.
Does a Discord bot need open ports on the firewall? No. A bot connects out to Discord's gateway. Only open ports if you also run something people visit, such as a web dashboard.
Where do I see my bot's errors? With journalctl -u mybot -f, where mybot is the name of your service file.