To host a Discord bot 24/7, run it on a Linux VPS as a systemd service. A bot is one long-running outbound connection to Discord’s gateway. It needs no open inbound port, little RAM, and a process manager that restarts it after a crash or a reboot. The whole setup is a user, a runtime, the code, a token file and a systemd unit, and it takes about fifteen minutes on Debian or Ubuntu. It works the same for discord.js (Node.js) and discord.py (Python).
/opt/mybot/app your code (git clone)
/etc/mybot.env DISCORD_TOKEN=... (root only, mode 600)
mybot.service runs the bot as user "bot", restarts it, starts it at boot
journalctl -u mybot -f the bot's logHow do I set up a Discord bot on a VPS?
These steps assume a fresh Debian 12/13 or Ubuntu 22.04/24.04 server and an SSH login with sudo. If you are still logging in as root with a password, do the initial server setup first. Replace mybot with your bot’s name throughout.
- 1Create a user for the bot
The bot gets its own system account with no login shell, so a bug in the bot cannot touch your account or the rest of the system:
service user sudo useradd --system --create-home --home-dir /opt/mybot --shell /usr/sbin/nologin bot - 2Install the runtime
For discord.js, install a current Node.js LTS release. The distribution’s
nodejspackage is often too old, so use NodeSource; other options are in installing Node.js properly.discord.js — Node.js LTS sudo apt update && sudo apt install -y ca-certificates curl git curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt install -y nodejs node --versionFor discord.py, the system Python 3 is recent enough, but packages go into a virtual environment: Debian 12/13 and Ubuntu 24.04 refuse a system-wide
pip install(see externally-managed-environment).discord.py — Python 3 and venv sudo apt update && sudo apt install -y python3 python3-venv git python3 --versionOn Rocky Linux 9,
sudo dnf install -y python3 gitis all discord.py needs, because Rocky’s Python includes the venv module. For Node.js, pick a current LTS stream fromdnf module list nodejsand install it withsudo dnf module install -y nodejs:22(use the number the list shows). - 3Get the code onto the server
Work in a shell as the bot user, so every file it creates belongs to the bot:
code and dependencies sudo -u bot -H bash # a shell as the bot user cd ~ # /opt/mybot git clone https://github.com/you/mybot.git app # discord.js cd ~/app && npm ci --omit=dev # discord.py python3 -m venv ~/venv ~/venv/bin/pip install -r ~/app/requirements.txt exit # back to your own userA private repository needs a deploy key or an access token. Without git, copy the folder from your PC with
scp -r ./mybot admin@203.0.113.10:/tmp/, thensudo mv /tmp/mybot /opt/mybot/app && sudo chown -R bot:bot /opt/mybot/app.npm cineeds apackage-lock.json; without one, usenpm install --omit=dev. - 4Put the token in a protected file
The token is the bot’s password. Keep it out of the code and out of git, and store it in a file only root can read; systemd reads it before it switches to the bot user. Use an editor rather than
echo, which would leave the token in your shell history:/etc/mybot.env sudo install -m 600 -o root -g root /dev/null /etc/mybot.env sudo nano /etc/mybot.env # one line, no spaces around "=": # DISCORD_TOKEN=paste-the-token-hereThe bot then reads it from the environment:
reading the token // discord.js client.login(process.env.DISCORD_TOKEN); # discord.py import os client.run(os.environ["DISCORD_TOKEN"]) - 5Write a systemd unit
/etc/systemd/system/mybot.service [Unit] Description=mybot Discord bot After=network-online.target Wants=network-online.target [Service] Type=simple User=bot Group=bot WorkingDirectory=/opt/mybot/app EnvironmentFile=/etc/mybot.env # discord.js ExecStart=/usr/bin/node /opt/mybot/app/index.js # discord.py: use these two lines instead # ExecStart=/opt/mybot/venv/bin/python /opt/mybot/app/bot.py # Environment=PYTHONUNBUFFERED=1 Restart=always RestartSec=10 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=true [Install] WantedBy=multi-user.targetExecStartneeds absolute paths;command -v nodeprints the right one. The Python line uses the venv’s own interpreter, so noactivateis needed, andPYTHONUNBUFFERED=1makesprint()output reach the log right away.RestartSec=10keeps a crash loop from hammering Discord’s API. Discord temporarily blocks IP addresses that send too many invalid requests, and a bot that restarts every second with a bad token can get there. If your code lives under/home, removeProtectHome=true. Every option in the unit is explained in how to create a systemd service. - 6Start it and enable it at boot
start sudo systemctl daemon-reload sudo systemctl enable --now mybot systemctl status mybot --no-pager - 7Read the logs
logs sudo journalctl -u mybot -f # follow live sudo journalctl -u mybot --since "1 hour ago"
Why does my bot go offline when I close SSH?
Because you started it in your SSH session. A program started with node index.js or python3 bot.py belongs to that session, and when the session ends the program ends with it. nohup, screen and tmux survive the logout, but not a crash or a reboot, and nothing tells you the bot is down. The systemd unit above covers all three cases.
For Node.js bots, PM2 is a common alternative. It restarts the bot, keeps a process list and can start everything at boot:
sudo npm install -g pm2
sudo install -m 600 -o bot -g bot /dev/null /opt/mybot/.env
sudo nano /opt/mybot/.env # DISCORD_TOKEN=...
sudo -u bot -H bash -c 'cd ~/app && pm2 start index.js --name mybot --node-args="--env-file=/opt/mybot/.env" && pm2 save'
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u bot --hp /opt/mybotPM2 does not read /etc/mybot.env, so the token goes into /opt/mybot/.env, owned by bot with mode 600, and Node’s own --env-file option loads it. Use PM2 or systemd, not both. For a single bot, the plain systemd unit is one moving part fewer.
Which ports do I need to open for a Discord bot?
None for the bot itself. It connects out to Discord over HTTPS and a secure WebSocket, and a default firewall allows outgoing connections. Inbound, you only need SSH. Two exceptions:
- A web dashboard or any HTTP API the bot serves: put it behind a reverse proxy and open 443.
- An Interactions Endpoint URL, if you receive slash commands over HTTP instead of the gateway: Discord must reach that URL over HTTPS, so open 443 for it.
Everything else stays closed. The UFW setup guide sets that up without locking you out of SSH.
How much server does a Discord bot need?
- A small bot, with commands, moderation and a few servers, runs on the smallest VPS plan. The runtime and the library typically use tens of megabytes to a couple of hundred, depending on how much the library caches.
- Large bots grow with the number of servers and members they cache. Memory is usually the first limit; trimming the cache in the library’s options helps more than a bigger plan.
- Voice and music bots need
ffmpeg(sudo apt install ffmpeg) plus the voice extras your library documents. They need noticeably more CPU, because every stream is decoded and encoded live.
Node.js and Python run your bot’s code mostly on one core, so the speed of a single core matters more than the number of cores until you shard. A typical bot is idle most of the time, so the disk mainly needs room for logs and perhaps a SQLite database.
How do I update the bot?
sudo -u bot -H bash -c 'cd ~/app && git pull'
# if dependencies changed
sudo -u bot -H bash -c 'cd ~/app && npm ci --omit=dev'
# or: sudo -u bot -H bash -c '~/venv/bin/pip install -r ~/app/requirements.txt'
sudo systemctl restart mybot
sudo journalctl -u mybot -n 30 --no-pager # did it come back?Update the library itself regularly too. Discord retires old API versions and changes gateway and voice behaviour from time to time, and an outdated discord.js or discord.py is a common reason a bot that ran for months suddenly stops connecting.
Why does the bot keep restarting or not respond?
- Invalid token (“An invalid token was provided” in discord.js,
LoginFailure: Improper token has been passedin discord.py): the token was reset or pasted with extra characters. Reset it under Bot in the Developer Portal and update/etc/mybot.env. Discord’s API answers a bad token with HTTP 401, which 401 Unauthorized explains. - “Used disallowed intents” in discord.js, or
PrivilegedIntentsRequiredin discord.py: the code asks for a privileged intent, such as Message Content or Server Members, that is not switched on for the bot in the Developer Portal. - Online but ignores commands: prefix commands such as
!helpneed the Message Content intent, and slash commands only appear after the bot has registered them with Discord. If new ones do not show up, reload the Discord client with Ctrl+R. - Rate limited (HTTP
429in the logs): the bot sends more requests than Discord allows. discord.js and discord.py wait and retry on their own; a bot that keeps hitting the limit needs fewer calls. See 429 Too Many Requests. - Killed without an error: the server ran out of memory. Check
sudo journalctl -k | grep -i oomand see why Linux kills your process.
When does a bot need sharding?
Discord requires sharding once a bot is in 2,500 servers. Each shard is a separate gateway connection that handles part of the servers. discord.js ships a ShardingManager that runs shards as separate processes, and discord.py has AutoShardedClient; both can ask Discord for the recommended shard count. Shards in separate processes are where a bot finally uses more than one core, so this is the point to add vCores rather than earlier.
Where should you host a Discord bot?
Any KVM VPS with a current Linux runs the setup above. On VMHeaven, the Standard KVM line starts at €4.99 a month in Amsterdam. Frankfurt, the premium location, is priced on its own, higher row; VPS in Germany shows which lines are in stock there. For busy bots and music bots, Hi-CPU KVM puts fewer vCores per plan on AMD EPYC (the 7443 in Amsterdam), for software that leans on one or two cores. You can pay by card, PayPal or crypto (Bitcoin, Ethereum, USDT, Litecoin, Monero and more), with no ID check; the crypto VPS page walks through the crypto checkout. The Discord bot hosting page compares the plans.
Frequently asked
How much RAM does a Discord bot need?
A small bot with commands and moderation in a few servers typically uses tens of megabytes up to a couple of hundred, so the smallest VPS plan is plenty. Memory grows with the number of servers and members the library caches, and voice or music bots that run ffmpeg need more CPU as well. Watch the real figure with systemctl status or top before you size up.
Do I need to open any ports to host a Discord bot?
No. A bot makes an outbound connection to Discord's gateway, and outgoing traffic is allowed by default, so the only inbound port you need is SSH for yourself. Open 443 only if the bot also serves a web dashboard or receives slash commands through an Interactions Endpoint URL.
Why does my Discord bot stop when I log out?
Because it was started inside your SSH session, and the session's processes end when you log out. nohup, screen and tmux survive the logout but not a crash or a reboot. Run the bot as a systemd service with Restart=always and systemctl enable, or with PM2 plus pm2 startup and pm2 save, so it restarts on its own and starts at boot.
Can I pay for Discord bot hosting with crypto?
Yes. VMHeaven accepts card, PayPal or crypto — Bitcoin, Ethereum, USDT, Litecoin, Monero and more, through Cryptomus. Sign-up asks for an e-mail address, a name and a postal address for the invoice, but never for an ID document, and the server is set up automatically once the payment is confirmed.