1. Why Your Bot Keeps Going Offline
You built a Discord bot. It answers commands, tracks levels and posts memes on time. Then you close your laptop, and the bot's green dot turns grey.
A Discord bot is a program. It works only while a computer runs it. When your laptop sleeps, your Wi-Fi drops or Windows installs an update, the program stops. Your server members notice within minutes.
Free hosting often has the same problem in a different form. Many free plans put your project to sleep when no one visits a web page. A Discord bot has no visitors in that sense, so the host decides it is idle and stops it. Some people add a tiny web server and ping it every few minutes to keep the bot awake. That trick breaks often, and some hosts forbid it.
This guide shows you how to keep a Python bot online all day, every day. It covers the code changes you need, a do-it-yourself server setup and a setup with no server work at all.
2. How a Discord Bot Stays Connected
Understanding the connection explains every hosting rule that follows.
A website waits for visitors to come to it. A Discord bot works the other way. When your bot starts, it opens a connection out to Discord's gateway and keeps that connection open. Discord sends every event your bot cares about down that connection: new messages, new members, button clicks and slash commands.
This design has three consequences for hosting.
- The bot needs no public web address. It never accepts incoming visitors, so you do not need a domain, a port or https.
- The bot must run without stopping. If the process stops, the connection closes and the bot goes offline. A host that sleeps idle apps cannot run it.
- The bot needs very little power. A small bot spends most of its time waiting for events. Most bots for one or a few servers use well under 256 MB of memory.
So the job is simple to describe. You need a computer that runs one Python process forever, restarts it if it crashes and keeps your bot token secret.
3. Four Ways to Host a Bot
Your own computer or a Raspberry Pi
A Raspberry Pi on a shelf costs little to run and works well for a bot in one friend group. You handle power cuts, internet outages and SD card failures yourself. It is a fun project, but it is only as reliable as your home internet.
Free bot hosting
Free hosts are good for testing. Read the limits before you trust one with a community. Common limits are monthly hour caps, forced restarts, sleep after inactivity and shared machines that slow down at busy times. Free plans also change often, so a host that works today may add new limits next month.
A virtual private server (VPS)
A VPS is a small Linux machine you rent by the month. Entry plans from providers such as Hetzner, DigitalOcean and Vultr cost a few dollars and run a bot well. You get full control. You also own every chore: installing Python, writing a service file, applying security updates and making backups.
A managed Python host
A managed host runs your code for you. You upload the bot, add your token and press publish. The host restarts the bot if it crashes, shows you its logs and keeps the machine patched. You pay a little more than a bare VPS, and you get your evenings back.
The rest of this guide covers the code first, because every option needs it. Then it shows the VPS route and the managed route step by step.
4. Get Your Code Ready
Four small changes make any Python bot ready for a server. Make them before you upload anything.
Read the token from an environment variable
Your bot token works like a password. Anyone who has it controls your bot. Never paste it into your code, because code ends up on GitHub, in screenshots and in zip files you send to friends. Read it from the environment instead.
import logging
import os
import discord
from discord.ext import commands
logging.basicConfig(level=logging.INFO)
intents = discord.Intents.default()
intents.message_content = True # Only if your bot reads message text
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
logging.info("Logged in as %s", bot.user)
@bot.command()
async def ping(ctx):
await ctx.send(f"Pong! {round(bot.latency * 1000)} ms")
bot.run(os.environ["DISCORD_TOKEN"])
If the token is missing, os.environ["DISCORD_TOKEN"] stops the bot with a clear error. That beats a confusing login failure later.
List your packages in requirements.txt
The server needs to know which libraries to install. Create a file called requirements.txt next to your bot.
discord.py>=2.4
python-dotenv>=1.0 # Optional, for a .env file on your laptop
Pin a minimum version, as above. This stops an old cached version from breaking your slash commands.
Log to the screen, not to a file
Use print() or the logging module. Hosts collect everything a program prints and show it to you. A log file inside the bot's folder is easy to lose and hard to read from a phone.
Turn on only the intents you need
Intents tell Discord which events to send your bot. The message_content, members and presences intents are privileged. You must switch them on in the Discord Developer Portal, under your application's Bot page, as well as in your code. Bots in 100 or more servers need Discord's approval for privileged intents. If your bot uses only slash commands, you can leave message_content off.
5. Run It on a VPS with systemd
On a Linux VPS, systemd is the standard tool for keeping a program running. It starts your bot when the machine boots and restarts it after a crash. These steps assume Ubuntu and a user called bot.
First, copy your code to the server and install its packages in a virtual environment.
sudo apt update && sudo apt install -y python3-venv
mkdir -p ~/mybot && cd ~/mybot
# Copy bot.py and requirements.txt here, then:
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
Next, store the token in a file that only your user can read.
echo 'DISCORD_TOKEN=your-token-here' > ~/mybot/.env
chmod 600 ~/mybot/.env
Then create a service file at /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
EnvironmentFile=/home/bot/mybot/.env
ExecStart=/home/bot/mybot/.venv/bin/python bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Finally, start the bot and tell systemd to start it at every boot.
sudo systemctl daemon-reload
sudo systemctl enable --now mybot
journalctl -u mybot -f # Watch the logs
Restart=always brings the bot back five seconds after a crash. The setup works well. Remember that the server is now yours to maintain. Turn on automatic security updates and copy any bot data off the machine on a schedule.
6. Run It on Cloudilio
Cloudilio runs Python bots and workers as well as websites. You do not write a service file or touch Linux. The whole process takes about five minutes.
- Zip your project. Put
bot.pyandrequirements.txtin a folder and compress it. Leave out your.envfile and any.venvfolder. - Create an app. Choose to upload your own code and drop in the zip file. Cloudilio reads your files, sees that the code uses discord.py and sets the app up as a background bot. It starts the bot with
python bot.py. - Add your token. Open the app's Secrets page and add
DISCORD_TOKENwith your token as the value. Cloudilio stores it encrypted and passes it to your bot when it starts. - Publish. Cloudilio installs your packages, starts the bot and checks that it keeps running. Open the Logs page to see your "Logged in as" message.
If your bot ever crashes, Cloudilio starts it again. If it stops for longer, Cloudilio emails you. To update the bot, upload a new zip, edit the code in the browser or link a GitHub repository.
One setting matters for bots. In the app's advanced settings, the field "Page we check to see it's working" should say none. That tells Cloudilio to check that your process is alive instead of loading a web page. Cloudilio fills this in for you when it detects a bot library.
7. Save Data That Survives Restarts
Most bots remember something. It might be XP levels, warnings, reminders or server settings. Where you save that data decides whether it survives your next update.
On many hosts, files next to your code are wiped on every deploy. On Cloudilio, your app gets a data folder at /app/data that is kept between updates and backed up every day. The path is also in the APP_DATA_DIR environment variable. Here is a simple level tracker that stores its numbers in SQLite inside that folder.
import os
import sqlite3
DATA_DIR = os.environ.get("APP_DATA_DIR", "data")
os.makedirs(DATA_DIR, exist_ok=True)
db = sqlite3.connect(os.path.join(DATA_DIR, "levels.db"))
db.execute("PRAGMA journal_mode=WAL")
db.execute("CREATE TABLE IF NOT EXISTS xp (user_id INTEGER PRIMARY KEY, points INTEGER NOT NULL)")
@bot.event
async def on_message(message):
if message.author.bot:
return
db.execute(
"INSERT INTO xp (user_id, points) VALUES (?, 1) "
"ON CONFLICT(user_id) DO UPDATE SET points = points + 1",
(message.author.id,),
)
db.commit()
await bot.process_commands(message)
On your laptop, the same code writes to a local data folder. You can open the database on Cloudilio's Data & backups page and look at its tables without downloading anything.
8. Fix the Most Common Errors
PrivilegedIntentsRequired
Your code asks for an intent that is switched off in the Developer Portal. Open your application, go to the Bot page and switch on the intent your code uses. Or remove that intent from your code if you do not need it.
LoginFailure or "Improper token has been passed"
The token is wrong, has extra spaces or was reset. Copy a fresh token from the Developer Portal and paste it into your secret again. Resetting the token in the portal makes the old one stop working at once.
The bot is online but ignores commands
Prefix commands like !ping need the message_content intent. Slash commands need syncing. Call await bot.tree.sync() once after you add or change a slash command. Global slash commands can take a while to appear in every server.
429 Too Many Requests
Your bot hit Discord's rate limits. discord.py waits and retries for you, but a loop that sends many messages can still trigger it. Add a short delay between bulk actions. If a host shares one IP address across many bots, another bot's behavior can also cause this, which is one more reason to avoid crowded free hosts.
The bot stops after a few hours
Your host is putting it to sleep or killing it for memory. Check the logs for an out-of-memory message. If the host sleeps idle apps, move to a host that runs background processes without pause.
9. Questions People Ask
How much does it cost to host a Discord bot 24/7?
A small bot runs on a VPS for about $4 to $6 a month, plus your time to manage it. Managed hosts cost more and include restarts, logs, backups and updates. Free options exist, but they come with limits that make them a poor fit for a busy community.
Do I need a domain name for my bot?
No. A bot connects out to Discord, so it needs no web address. You only need a domain if you also build a web dashboard for the bot.
Can I host several bots on one plan?
Yes. On a VPS, give each bot its own folder and service file. On Cloudilio, each bot is its own app, and your plan sets how many apps you can run.
Does this work for Telegram bots too?
Yes. A Telegram bot that uses polling, such as one built with python-telegram-bot's run_polling(), behaves just like a Discord bot. It is one Python process that must keep running. Every step in this guide applies.
Should I use discord.py or another library?
discord.py is the most widely used Python library and is actively maintained. Forks such as Pycord and Nextcord work too, and the hosting steps are identical. Pick the one whose documentation you find easiest to follow.
Cloudilio Engineering Team
Application Delivery Group • CloudilioCloudilio hosts Python apps. You publish Flask, Django, FastAPI, Streamlit and plain Python scripts without managing a server, and your app's data stays safe between updates.