Run a Discord bot 24/7 for free on a computer you already own
Once you have a Discord bot working, the next problem shows up fast. Close the terminal and the bot dies. Turn off the computer and it dies. So you go looking for "free 24/7 hosting".
Before you do: if you have a computer at home that stays on, that is your host. Ours is a Mac mini that has been up 34 days straight. Hosting cost is zero.
What you get by hosting it yourself
- It never sleeps. Free tiers often idle your bot out. Waking up takes time, and commands sent in between are lost
- It can touch your files. The bot runs inside your machine, so your folders, your scripts and your installed tools are all available. This is the real difference
- No expiry. Free plans change. Your own computer does not
- You pay for electricity, nothing else
The trade is that you are tied to your home power and internet. A blackout takes the bot with it. For a bot you use, that is usually fine.
Step 1: survive closing the terminal
A program started in a terminal belongs to that window. Close it and the program goes too.
screen gives you a detached place to run it. macOS ships with it.
screen -dmS mybot python3 /Users/me/bot.py
-dmS means "start detached, give it a name". You can close the terminal now.
To look in on it:
screen -ls screen -r mybot
screen -ls prints something like this.
There is a screen on:
37448.mybot (Detached)
Detached means nobody is watching but it is running. screen -r attaches you to it. To leave, press Ctrl+A then D. Pressing Ctrl+C kills the bot. That catches everyone once.
tmux does the same job. nohup is simpler but you cannot look at the screen later, which you will want.
Step 2: survive a reboot
A screen session dies with the machine, and updates reboot it whether you like it or not.
On macOS, launchd handles this. You write one file, register it, and the thing starts at login and restarts if it dies. On Linux it is systemd.
Step 3: the shape that actually works
Here is where people usually go wrong. You can have launchd start the bot directly. We do not.
launchd starts a small script, and that script manages the screen session.
The reason is that you will want to see the bot's screen with your own eyes. A process started directly by launchd has no screen to attach to. Put screen in the middle and screen -r works whenever you need it.
The script is not complicated.
while true; do
if ! screen -ls | grep -q "\.mybot[[:space:]]"; then
screen -dmS mybot python3 /Users/me/bot.py
fi
sleep 30
done
Every 30 seconds it checks whether the session exists and starts it if not. launchd keeps the script alive, so if the script dies it comes back. Two layers.
Add a network check before the first start. Right after a reboot the network is often not up yet, and the bot fails immediately if it tries to connect.
Step 4: stop the machine from sleeping
If you skip this, your bot dies every night and you will not know why.
Turn off sleep in Energy Saver. You can confirm from the terminal:
pmset -g
sleep 0 means it will not sleep. displaysleep can stay on — the screen turning off does not stop anything. On a laptop, check what closing the lid does. This is why a headless box like a Mac mini suits the job.
The failure nobody plans for
Everything above handles a bot that dies. In practice the worse case is different.
The process is alive and the bot simply stops answering. Discord still shows it online. ps still lists it. Commands get no reply. A session expired, a quota ran out, or something inside got stuck.
Restart-if-dead never fires, because nothing died.
The fix is to watch work, not liveness. Have the bot write a timestamp every time it actually handles something, and have your script kill the process if that timestamp gets too old. Killing it is enough — the structure above brings it straight back.
We got burned here in a way worth repeating. Our watchdog checked connectivity by fetching a page, and that fetch pulled the whole 170KB body. On a slow day it blew past the timeout, so the watchdog itself failed for eight hours while the bot sat dead. Switching to a HEAD request brought it to 0.5 seconds.
If the thing doing the watching is heavy, the watching breaks first.
Checking on it
screen -ls launchctl list | grep mybot
First line tells you the session exists, second that autostart is registered.
One warning: screen -X hardcopy, which dumps the screen to a file, produces an empty file for anything that draws a text UI. We tried to use it for health checks and wasted time. Check for a child process and an established network connection instead.
Related
- Making the bot in the first place: Discord bot setup, from token to server invite
- When it is alive but silent: Discord bot not responding but still running: how to catch it
Every command and number here comes from a machine that is running right now.
Comments