Disclosure: I run natbox.io, the host used in this guide. The steps work on any Linux VPS.
A Discord bot doesn't need much. It makes outgoing connections to Discord's gateway and never needs anyone to connect to it. That means it doesn't need its own public IPv4 address, and a typical small bot uses well under 100 MB of RAM.
So a full $5–6/month VPS is overkill. A small NAT VPS (shared IPv4, a few forwarded ports, your own IPv6) is enough, and that's what we'll use here.
By the end you'll have a Python bot that:
- runs 24/7 as a systemd service
- restarts by itself if it crashes or the server reboots
- keeps its token out of the code
What you need
- A Discord bot token from the Discord Developer Portal (New Application → Bot → Reset Token)
- A small Linux server. I'm using a natbox Nano (512 MB RAM, $1/month) running Debian, but any Debian/Ubuntu box works.
1. Connect over SSH
On a NAT VPS, SSH runs on a forwarded port instead of 22. Your client area shows the exact command, something like:
ssh root@your-host -p 40001
Update the system first:
apt update && apt upgrade -y
apt install -y python3 python3-venv
2. Create a user for the bot
Don't run the bot as root:
adduser --system --group --home /opt/mybot bot
3. Set up Python and the bot code
cd /opt/mybot
sudo -u bot python3 -m venv venv
sudo -u bot ./venv/bin/pip install discord.py
Create /opt/mybot/bot.py:
import os
import discord
from discord import app_commands
intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)
@tree.command(name="ping", description="Check that the bot is alive")
async def ping(interaction: discord.Interaction):
await interaction.response.send_message(
f"Pong! {round(client.latency * 1000)} ms"
)
@client.event
async def on_ready():
await tree.sync()
print(f"Logged in as {client.user}")
client.run(os.environ["DISCORD_TOKEN"])
It uses a slash command, so it doesn't need the privileged Message Content intent.
4. Keep the token out of the code
echo 'DISCORD_TOKEN=paste-your-token-here' > /opt/mybot/.env
chown bot:bot /opt/mybot/.env /opt/mybot/bot.py
chmod 600 /opt/mybot/.env
5. Run it as a systemd service
Create /etc/systemd/system/mybot.service:
[Unit]
Description=My Discord bot
After=network-online.target
Wants=network-online.target
[Service]
User=bot
WorkingDirectory=/opt/mybot
EnvironmentFile=/opt/mybot/.env
ExecStart=/opt/mybot/venv/bin/python bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Start it and enable it at boot:
systemctl daemon-reload
systemctl enable --now mybot
6. Check that it works
systemctl status mybot
journalctl -u mybot -f
You should see Logged in as YourBot#1234. Invite the bot to your server (Developer Portal → OAuth2 → URL Generator, with the bot and applications.commands scopes). Type /ping. New slash commands can take a few minutes to appear.
To update the bot later, edit bot.py and run systemctl restart mybot.
How much does it use?
Check with:
systemctl status mybot | grep Memory
A small discord.py bot like this sits at roughly 50–80 MB, so a 512 MB server has plenty of room, even for a few bots side by side (give each one its own folder and service).
When a NAT VPS is not enough
- If your bot also serves a web dashboard, you need an inbound port. On natbox you can use one of your forwarded ports, or ports 80/443 with your own domain.
- Music bots that stream audio to big servers use more CPU and bandwidth. Pick a bigger plan or a dedicated core.
- If you need a dedicated IPv4 for some other reason, get a regular VPS.
That's it: a bot that runs around the clock for about $12 a year. If you try it on natbox, there's a free 1-month trial (new slots are posted on our Telegram channel), and plans start at $1/month: https://natbox.io/discord-bot-hosting
Top comments (0)