Moving a Discord bot off Replit or Heroku
A lot of Discord bots started on Replit or Heroku because tutorials said so and it was free. Heroku's free plan is gone, Replit's free tier no longer keeps a program running, and both leave behind habits that break elsewhere. Here is how to move cleanly.
8 minute read
Most of the work is removing things that only existed to work around the old platform. The bot itself rarely needs to change.
Why bother moving
- Heroku ended its free dynos in November 2022. Paid dynos work fine for bots, but if you are paying anyway, you can pick somewhere built for it.
- Replit no longer keeps free Repls running once you close the tab. Keeping a bot online there means a paid deployment, and at the time of writing the old "keep_alive plus an uptime pinger" trick is unreliable.
- Shared addresses. On platforms where thousands of bots share a few IP addresses, one badly behaved bot can get the whole address temporarily blocked by Discord. The error says You are being blocked from accessing our API temporarily due to exceeding our rate limits frequently, and there is nothing you can do about it from your code. See Discord rate limits.
1. Get your code out
Replit: in the file tree's menu, choose Download as zip. If the Repl is linked to GitHub, pulling the repository works just as well.
Heroku: your code is already in a Git repository, since that is how Heroku deploys. Clone it from GitHub or from Heroku's own remote.
Unzip it and get it running on your own computer before going anywhere else. It is much easier to fix things there.
2. Delete the keep-alive server
Many Replit bots have a file called keep_alive.py or similar that starts a small Flask web server, plus a line like keep_alive() before bot.run(). It existed so an outside service could ping the Repl and stop it sleeping.
Anywhere that runs your bot continuously does not need it. Delete the file, delete the call, and remove flask from your dependencies. Also delete the monitor on UptimeRobot or wherever it pinged from.
3. Replace Replit Secrets or Heroku config vars
Both expose your secrets as environment variables, so code that reads os.environ["TOKEN"] or os.getenv("TOKEN") keeps working. You just have to set the variables in the new place.
- Write down every secret name before you leave. On Heroku,
heroku configlists them. On Replit, open the Secrets tool. - If a secret was ever visible in a public Repl or a public repository, treat it as leaked and regenerate it. Old public Repls were public by default, code and all. Why that matters for a bot token.
4. Replace Replit DB
If your code does from replit import db, that database only exists on Replit. Export it before you shut the Repl down, by running this once on Replit:
import json
from replit import db
data = {key: db[key] for key in db.keys()}
with open("db_export.json", "w") as f:
json.dump(data, f, default=str)
Values stored by the Replit library can be special "observed" types; default=str keeps the export from failing on them, but check the file looks right.
Then pick a replacement. For something the same shape as Replit DB, a JSON file or SQLite key-value table is enough:
import sqlite3, json
con = sqlite3.connect("bot.db")
con.execute("CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT)")
def db_get(key, default=None):
row = con.execute("SELECT value FROM kv WHERE key = ?", (key,)).fetchone()
return json.loads(row[0]) if row else default
def db_set(key, value):
con.execute("INSERT OR REPLACE INTO kv VALUES (?, ?)", (key, json.dumps(value)))
con.commit()
# one-time import
for k, v in json.load(open("db_export.json")).items():
db_set(k, v)
Swap db["x"] for db_get("x") and db["x"] = y for db_set("x", y) through your code.
5. Make a requirements.txt
Replit often manages packages with pyproject.toml and Poetry rather than a requirements file. Most hosts expect requirements.txt. If you have Poetry locally:
poetry export -f requirements.txt --without-hashes -o requirements.txt
Or install what your code imports into a clean virtual environment and run pip freeze > requirements.txt. Remove replit and flask from the list if they are still there.
From Heroku you probably already have a requirements.txt. The Procfile and runtime.txt are Heroku-only and can go; note which Python version runtime.txt named, so you can pick the same one.
6. Remove the platform leftovers
.replit, replit.nix, .upm, poetry.lock (if you are not using Poetry), Procfile, app.json. None of them do anything elsewhere.
7. Test it clean, then move it
python -m venv fresh
source fresh/bin/activate # Windows: fresh\Scripts\activate
pip install -r requirements.txt
export BOT_TOKEN="..." # Windows PowerShell: $env:BOT_TOKEN = "..."
python bot.py
If it starts from a fresh environment with only what is in requirements.txt, it will start anywhere. Then stop the old copy before starting the new one. Two copies on one token answer every command twice.
Where to move it
The keeping a bot online 24/7 guide compares the options, including free ones. If you want a host, SnowServers runs Python, JavaScript and Java bots with a console, file manager, GitHub deploys and daily backups from $3 a month, with a 7-day free trial, and the setup walkthrough picks up exactly where step 7 ends.