Docs academy · Guide 6 of 8 · 12 min read
Hosting 24/7: VPS, Docker, process managers
Get your bot off your laptop and keep it running through crashes and reboots.
Your bot only runs while its program runs. To stay online all the time it needs a computer that's always on, which usually means a small rented server.
Choosing a host
| Option | Good for | Watch out for | | --- | --- | --- | | VPS (a small Linux server you rent) | Most bots. You get full control at a low monthly cost. | You're responsible for updates and security. | | Bot or app hosting platforms | Getting started without learning Linux | Check how they treat long-running processes, and watch usage limits. | | Your own always-on machine | Testing | Home internet and power cuts mean downtime. |
A small bot runs happily on a VPS with 1 vCPU and 1 GB of memory. This guide uses a VPS running Ubuntu.
Prepare the server
Log in over SSH, then install updates and create a normal user, because your bot should never run as root:
sudo apt update && sudo apt upgrade -y
sudo adduser --disabled-password --gecos "" bot
Turn on the firewall. A Discord bot only makes outgoing connections, so nothing needs to come in except SSH:
sudo ufw allow OpenSSH
sudo ufw enable
Log in with SSH keys rather than a password if you can. Password logins are attacked constantly.
Get your code onto the server with git clone. Create your .env by hand on the server, since it isn't in your repository (and must never be). Protect it so that only your bot's user can read it:
chmod 600 .env
Option 1: systemd
Ubuntu already includes systemd, and it restarts your bot after a crash or a reboot. It's the option we recommend for a single bot.
Create /etc/systemd/system/mybot.service. For a Node.js bot:
[Unit]
Description=My Discord bot
After=network-online.target
Wants=network-online.target
[Service]
User=bot
WorkingDirectory=/home/bot/my-bot
EnvironmentFile=/home/bot/my-bot/.env
ExecStart=/usr/bin/node index.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
For Python, change ExecStart to use your virtual environment:
ExecStart=/home/bot/my-bot/.venv/bin/python bot.py
Then start it, and have it start at boot:
sudo systemctl daemon-reload
sudo systemctl enable --now mybot
These are the commands you'll use day to day:
systemctl status mybot
journalctl -u mybot -f
sudo systemctl restart mybot
Option 2: PM2
PM2 is a popular process manager for Node.js, if you'd rather not write a service file.
npm install -g pm2
pm2 start index.js --name my-bot --node-args="--env-file=.env"
pm2 save
pm2 startup
pm2 startup prints one command. Run it to have your bot start after a reboot. Then use pm2 logs my-bot and pm2 restart my-bot.
Option 3: Docker
Docker packages your bot with everything it needs, so it runs the same way everywhere. It's worth it once you run several services together, such as a bot and a database.
A Dockerfile for a Node.js bot:
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
CMD ["node", "index.js"]
For Python:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER nobody
CMD ["python", "bot.py"]
Add a .dockerignore containing .env, node_modules and .venv, so that secrets don't get baked into the image.
A compose.yaml to run it, with restarts:
services:
bot:
build: .
restart: unless-stopped
env_file: .env
volumes:
- ./data:/app/data
Anything inside the container disappears when it's rebuilt, so keep a SQLite file in the mounted data folder and point your bot at data/bot.db.
docker compose up -d --build
docker compose logs -f bot
Handling shutdowns
When you restart, your process gets a signal asking it to stop. Close things cleanly so that you don't lose data:
process.on("SIGTERM", async () => {
await client.destroy();
db.close();
process.exit(0);
});
In discord.py, bot.run handles the Discord connection for you. Close your own resources by overriding close on your client:
async def close(self):
await self.db.close()
await super().close()
Updating your bot
A simple routine that works:
git pull
npm ci
sudo systemctl restart mybot
journalctl -u mybot -n 50
Always read the logs after restarting, to confirm that it came back up.
Know when it's down
Hosting isn't finished until you find out about problems before your users do:
- Log errors somewhere you'll see them. A private Discord channel through a webhook is an easy start.
- Post stats to Nexobots regularly (see guide 8). It keeps your listing fresh, and it's one of the ways we know your bot is alive. Listings that look offline for 7 days get a warning, and they're archived after 30.
Next
Your bot runs around the clock. Now let it grow, with Sharding and getting verified.