Common Free Tier Mistakes That Crash Bots
The mistakes we see most often on free servers — uploading node_modules, hard-coded tokens, JSON databases, unbounded logs, duplicate copies — and how to avoid each one.
On this page
- 1. Uploading node_modules or a virtual environment
- 2. Hard-coding the token
- 3. The wrong runtime version
- 4. Using JSON files as a database
- 5. Running two copies
- 6. Logs that never stop growing
- 7. Registering slash commands on every start
- 8. Requesting every intent
- 9. Unbounded caches and collections
- 10. Blocking the event loop
- 11. Swallowing errors
- 12. Printing secrets while debugging
- A pre-flight checklist
- Summary
Most free-tier bots that struggle aren’t limited by the free plan at all. They’re tripped up by a small set of avoidable mistakes that would cause problems on any server — they just show up sooner on 256 MB. Here are the ones we see most often, with the fix for each.
1. Uploading node_modules or a virtual environment
The mistake: uploading your entire project folder, including node_modules/ or venv/, from your computer.
Why it breaks: these folders are large — often hundreds of megabytes — eating into your 2 GB of storage and making uploads slow. Worse, they contain binaries compiled for your operating system. A package built on Windows or macOS can fail with confusing errors on Linux.
The fix: upload only your source code and dependency files (package.json plus package-lock.json, or requirements.txt). Dependencies install automatically on the server, built for the right platform.
2. Hard-coding the token
The mistake: client.login("MTA2...") in your main file.
Why it breaks: the first time you share your code, push it to GitHub or post a screenshot, the token leaks. Discord may reset a leaked token automatically — taking your bot offline — or someone else may use it to take over your bot.
The fix: read the token from an environment variable and set it in the panel, where it’s stored encrypted. See keeping your bot token safe.
3. The wrong runtime version
The mistake: developing on Node.js 22 or Python 3.12 and deploying on an older version, or the reverse.
Why it breaks: newer syntax fails on older runtimes (SyntaxError), and some packages require a minimum version. discord.js v14 needs Node.js 16.11 or newer; newer Python syntax like X | None type hints fails on 3.9.
The fix: choose the same runtime version on the server that you use locally. Kerit Cloud offers Node.js 16–22, Python 3.9–3.12 and Java 11/17/21.
4. Using JSON files as a database
The mistake: storing balances, settings or levels in data.json and rewriting the whole file on every change.
Why it breaks: two commands saving at the same time overwrite each other’s changes. If the bot is restarted mid-write, the file can be left half-written, and on the next start JSON.parse throws — sending the bot into a crash loop. Large JSON files also get read into memory in full.
The fix: use the shared MySQL database included with the free plan, or SQLite for small bots. See using the shared MySQL database.
5. Running two copies
The mistake: leaving the bot running on your PC after deploying it, or starting it twice on the server.
Why it breaks: on Discord, every command gets two replies, and interactions fail with “already acknowledged”. On Telegram, both copies poll and Telegram returns 409 Conflict errors while splitting updates between them. It also doubles memory use.
The fix: stop the local copy once the server is running. If you can’t find a stray copy, reset the token and set the new one only on the server.
6. Logs that never stop growing
The mistake: writing every event to a log file with no rotation, or logging full message contents at debug level in production.
Why it breaks: log files grow until the disk is full. Then writes fail everywhere — SQLite can’t save, uploads fail, and the bot crashes in confusing ways.
The fix: log to standard output (the panel console captures it) at info level in production. If you must write files, rotate them and delete old ones.
7. Registering slash commands on every start
The mistake: calling your command registration inside the ready event.
Why it breaks: every restart — and there are many with auto-restart and deploys — re-uploads the full command list. You waste API calls and can hit Discord’s rate limits, after which registration starts failing.
The fix: register commands from a separate script or an owner-only command, only when they change. See slash commands not showing up.
8. Requesting every intent
The mistake: enabling all intents “just in case”, including presences and members.
Why it breaks: privileged intents must be enabled in the Developer Portal or the bot can’t connect (“disallowed intents”). The presence intent in particular floods the bot with status updates and fills memory — the quickest way to overflow 256 MB.
The fix: request only what your features need. Slash-command bots often need just Guilds. See gateway intents explained.
9. Unbounded caches and collections
The mistake: keeping every message, every user or every result in a list or map that only grows.
Why it breaks: memory rises steadily until the process is killed for exceeding the limit, restarts, and does it again.
The fix: limit library caches, add expiry to your own caches, and keep persistent data in the database. See what fits in 256 MB.
10. Blocking the event loop
The mistake: calling time.sleep(), requests.get() or synchronous file operations inside async code, or running heavy loops in a handler.
Why it breaks: in async bots, one blocking call freezes everything, including the heartbeat. The bot appears online but stops responding, and Discord may disconnect it for missing heartbeats.
The fix: use async equivalents (asyncio.sleep, aiohttp, aiofiles) and move heavy work out of the event loop.
11. Swallowing errors
The mistake: try: ... except: pass, or .catch(() => {}) everywhere.
Why it breaks: errors disappear, so when something fails you have no idea why. Bugs pile up silently until users complain.
The fix: catch errors per command, log them with context, and reply with a friendly message. Let truly fatal errors crash the process — the watchdog will restart it in seconds.
12. Printing secrets while debugging
The mistake: console.log(config) or print(os.environ) to check whether variables loaded.
Why it breaks: your token and database password end up in logs, which get copied into support requests and screenshots.
The fix: log whether a variable is set, not its value: console.log('token set:', Boolean(process.env.DISCORD_TOKEN)).
A pre-flight checklist
Before your first deploy:
- [ ] Only source and dependency files uploaded
- [ ] Token and secrets in environment variables
- [ ] Runtime version matches your local one
- [ ] Data in MySQL or SQLite, not JSON
- [ ] No second copy running anywhere
- [ ] Logging to the console at a sensible level
- [ ] Commands registered separately from startup
- [ ] Minimal intents, bounded caches
- [ ] No blocking calls in async code
- [ ] Errors logged, never silently swallowed
Summary
Most crashes on free servers come from avoidable mistakes: uploading dependencies instead of installing them, hard-coding tokens, mismatched runtimes, JSON databases, duplicate copies, unbounded logs and caches, re-registering commands on every start, excess intents, blocking the event loop and swallowing errors. Fix these and 256 MB goes a very long way — and your bot will be ready for a bigger plan when it grows.