How to run multiple python bots in a single process using StayPresent’s bot_file list and bots parameter, with independent crash recovery for each.
Running Multiple Bots in One Process with StayPresent
If you’re managing a Telegram bot pipeline with several workers, or a Discord bot that runs alongside a separate polling worker, deploying each one as its own hosting service quickly gets expensive and repetitive. StayPresent’s multi-bot support lets you run multiple python bots from a single staypresent.run() call, each fully and independently supervised, while sharing one HTTP server and one deployment.
Table of Contents
- Why Run Multiple Bots Together
- The Simple Way: A List of Files
- Per-Bot Configuration with
bots - How Independent Supervision Works
- Shared Failures vs Independent Failures
- Log Labels When Filenames Collide
- Full Example
- Best Practices
- Common Mistakes
- FAQs
- Conclusion
Why Run Multiple Bots Together
A common real-world setup is a Telegram ingestion bot and a separate upload/processing worker that share a queue or database — logically two processes, but deployed together because they belong to the same service. Running each as a separate hosting deployment means twice the port-binding boilerplate, twice the health checks, and twice the deployment overhead for something that’s conceptually one unit.
The Simple Way: A List of Files
bot_file accepts either a single string or a list of strings:
import staypresent
staypresent.run(["telegram_bot.py", "discord_bot.py"])
Enter fullscreen mode Exit fullscreen mode
Both scripts launch as independent subprocesses, sharing the same bot_args/env if provided.
Per-Bot Configuration with bots
When each bot needs different arguments or environment variables, use bots — a list of dicts, one per bot:
import staypresent
staypresent.run(bots=[
{"file": "telegram_bot.py", "args": ["--verbose"]},
{"file": "discord_bot.py", "env": {"SHARD": "0"}},
{"file": "worker.py"},
])
Enter fullscreen mode Exit fullscreen mode
bots is mutually exclusive with bot_file/bot_args/env — passing both raises TypeError, so there’s no ambiguity about which configuration style is active. Each entry requires a "file" key; "args" and "env" are optional per entry.
How Independent Supervision Works
Every bot in the list gets its own restart counter and its own dedicated monitoring thread. A crash in one bot — and any subsequent restart attempts — has zero effect on the others. If your Telegram bot hits an unhandled exception and restarts three times, your Discord bot keeps running the entire time, completely unaffected.
Before anything starts, every bot file listed is validated to exist — a missing file raises FileNotFoundError immediately, before any process launches. And if the very first launch attempt of any bot fails outright (a bad interpreter path, the system running out of file descriptors), StayPresent terminates any bots that already started and raises the failure immediately, so you never end up with orphaned, untracked processes left running in the background.
Shared Failures vs Independent Failures
staypresent.run() waits for every bot to finish — or fail permanently — before returning or exiting the process. If a bot exhausts its max_restarts budget, it’s marked permanently failed, but the other bots keep running normally. Only once every bot has finished does StayPresent decide the process’s own exit code: non-zero if any bot ended in a permanently-failed state, and specifically the exit code of the lowest-indexed failing bot if more than one failed — deterministic, regardless of which bot’s monitor thread happens to finish first.
staypresent.run(
bots=[
{"file": "critical_bot.py"},
{"file": "optional_worker.py"},
],
max_restarts=5,
)
Enter fullscreen mode Exit fullscreen mode
If optional_worker.py permanently fails but critical_bot.py keeps running fine, the whole process only exits (with optional_worker.py‘s exit code) once critical_bot.py itself eventually finishes too.
Log Labels When Filenames Collide
Bots are tracked by their position in the list, not by filename — two bots can share the exact same filename (shard_a/bot.py and shard_b/bot.py) with no functional conflict. But identical filenames in the logs would make crash/restart messages ambiguous, so StayPresent detects the collision automatically and logs each bot’s full file path instead of just the filename in that case:
bot[0] 'shard_a/bot.py' crashed, restarting...
bot[1] 'shard_b/bot.py' crashed, restarting...
Enter fullscreen mode Exit fullscreen mode
Bots with non-colliding filenames still get the shorter, plain filename label.
Full Example
import os
import staypresent
staypresent.web.json({"status": "running", "bots": 3})
staypresent.run(
bots=[
{"file": "telegram_bot.py", "args": ["--verbose"]},
{"file": "discord_bot.py", "env": {"SHARD": "0"}},
{"file": "queue_worker.py"},
],
port=int(os.getenv("PORT", 8080)),
max_restarts=5,
restart_delay=2.0,
)
Enter fullscreen mode Exit fullscreen mode
Best Practices
- Use
bots(notbot_file+ sharedenv) whenever different processes genuinely need different configuration — it keeps the config next to the file it applies to instead of implicitly shared. - Give each bot its own status endpoint with
web.text()/web.json()at a distinctpathso you can check any individual bot’s state without guessing which one a shared root response refers to. - Keep a “critical” bot and “optional” bots in mind when tuning
max_restarts— a bot you can’t afford to lose deserves a higher budget than a background helper.
Common Mistakes
-
Mixing
bot_fileandbotsin the same call. This raisesTypeErrorimmediately — pick one style perrun()call. - Assuming one bot’s crash stops the others. It doesn’t — each bot is supervised entirely independently, which is often surprising the first time you see it in the logs.
-
Forgetting that
run()only returns once every bot is done. If one bot runs forever and another exits cleanly, the process is still alive waiting on the first.
FAQs
Can I mix bot_file-style and bot_module-style bots?
Yes, inside bots — set "file" for some entries and "module" for others, freely mixed in the same list.
Does each bot get its own restart budget?
Yes — max_restarts, restart_delay, and restart_reset_after apply per bot, not shared across the whole group.
What if I only need one bot?
bot_file="bot.py" still works exactly as before — multi-bot support doesn’t change single-bot usage at all.
Conclusion
Whether you’re pairing a Telegram ingestion bot with a processing worker, or running several unrelated scripts side by side, StayPresent lets you run multiple python bots from one deployment, one port, and one run() call — with each bot’s crashes, restarts, and logs kept fully independent of the others.
pip install staypresent[prod]
Enter fullscreen mode Exit fullscreen mode
답글 남기기