Stop Your Python Bots From Crashing: Complete Reliability Guide
Every bot crashes eventually — a malformed API response, a rate limit, a dependency that briefly goes down. The difference between a hobby project and something you can trust is what happens in the seconds after that crash. This guide covers python bot restart strategy end-to-end: how deployment failures actually happen, how to configure recovery properly with StayPresent, and how to avoid turning a single crash into a full crash loop.
Table of Contents
- Common Deployment Failures
- Understanding Crash Loops
- Configuring Auto Restart
- Restart Delays Explained
- Restart Limits and Counter Resets
- Health Monitoring
- Logging for Reliability
- Production Deployment Checklist
- Full Reliability Example
- Best Practices
- Common Mistakes
- FAQs
- Conclusion
Common Deployment Failures
Most Python bot outages in production trace back to one of a small set of causes:
- An unhandled exception in an event handler
- A third-party API returning an unexpected response shape
- A dropped network connection during a long-running request
- Running out of memory on a constrained free-tier container
- A missing environment variable on a fresh deploy
None of these are exotic — they're the normal cost of running software against the real world. The question isn't whether they'll happen, it's whether your bot comes back on its own afterward.
Understanding Crash Loops
A crash loop happens when a bot restarts, hits the exact same failure immediately (a missing config value, a bad credential), crashes again, restarts again — indefinitely, often silently burning through hosting resources with no useful uptime to show for it.
This is why unlimited automatic restarts are dangerous on their own. Restart logic needs a ceiling.
crash -> restart -> crash -> restart -> crash -> restart -> ...
(no ceiling = infinite loop)
Enter fullscreen mode Exit fullscreen mode
Configuring Auto Restart
StayPresent restarts your bot process automatically whenever it exits with a non-zero code:
staypresent.run(
"bot.py",
restart_on_crash=True,
)
Enter fullscreen mode Exit fullscreen mode
An exit code of 0 is always treated as an intentional, clean shutdown and never triggers a restart — useful if your bot has a legitimate "shut down now" command.
Restart Delays Explained
Restarting instantly after a crash can make things worse, particularly if the crash was caused by something transient like a rate limit — hammering the same failing request in a tight loop just makes the underlying problem worse. restart_delay adds a pause before each relaunch:
staypresent.run(
"bot.py",
restart_on_crash=True,
restart_delay=2.0,
)
Enter fullscreen mode Exit fullscreen mode
For bots that talk to rate-limited APIs, a longer delay (5–10 seconds) is often safer than the default.
Restart Limits and Counter Resets
max_restarts puts a ceiling on consecutive crashes, which is exactly what prevents the infinite crash loop described above:
staypresent.run(
"bot.py",
restart_on_crash=True,
max_restarts=5,
restart_reset_after=60.0,
)
Enter fullscreen mode Exit fullscreen mode
The key detail is restart_reset_after: if the bot stays up successfully for that many seconds, the crash counter resets back to zero. This means a bot that crashes once a week doesn't slowly use up its restart budget over time — only a genuine, rapid crash loop hits the ceiling.
If max_restarts is exhausted, StayPresent exits the entire process using the bot's final exit code. This matters because it lets your hosting platform's own restart policy — Render, Railway, Docker, systemd — take over as a last resort, rather than StayPresent quietly exiting 0 as though nothing happened.
Health Monitoring
A dedicated /health endpoint, provisioned automatically by StayPresent, returns {"status": "ok"} independently of whatever you've configured at /:
staypresent.web.json({"status": "running", "version": "1.4.2"})
# /health still returns {"status": "ok"}
Enter fullscreen mode Exit fullscreen mode
Pointing your platform's health check — or an external uptime monitor — at /health gives you a stable signal that isn't affected by changes you make to your root route's response.
Logging for Reliability
Being able to see why a bot crashed matters as much as recovering from it. StayPresent logs under its own "staypresent" logger, so raising its verbosity during an incident doesn't pollute or restructure your bot's existing logs:
import logging
logging.getLogger("staypresent").setLevel(logging.INFO)
Enter fullscreen mode Exit fullscreen mode
Production Deployment Checklist
- [ ]
restart_on_crash=Truewith a sensiblemax_restarts - [ ]
restart_delaytuned to the kind of failures you expect - [ ]
restart_reset_afterset so long-term stability doesn't get penalized - [ ] Platform health check pointed at
/health - [ ]
prodextra installed so Waitress serves in production - [ ] Logging level configured for the
"staypresent"logger
Full Reliability Example
import os
import logging
import staypresent
logging.getLogger("staypresent").setLevel(logging.INFO)
staypresent.web.json({"status": "running"})
staypresent.run(
"bot.py",
port=int(os.getenv("PORT", 8080)),
restart_on_crash=True,
max_restarts=5,
restart_delay=3.0,
restart_reset_after=90.0,
)
Enter fullscreen mode Exit fullscreen mode
Best Practices
- Treat
max_restartsas a safety net, not a substitute for fixing the underlying bug — a bot that crashes every few minutes needs a code fix, not a higher restart limit. - Set
restart_delayhigher for bots hitting rate-limited third-party APIs. - Use
restart_reset_aftervalues that roughly match how long your bot typically runs between deploys, so genuinely stable bots aren't treated as fragile.
Common Mistakes
-
Setting
max_restartsvery high "just in case." This turns a real crash loop into a slow, expensive one instead of catching it quickly. -
Disabling restarts entirely in production (
restart_on_crash=False) without a plan for what happens after — the process exits and stays down until someone notices. -
Treating
/healthreturningokas proof the bot itself is fine. It only confirms the HTTP server is responsive; pair it with your own bot-side logging or metrics for full visibility.
FAQs
What happens after max_restarts is exhausted?
StayPresent exits the whole process with the bot's last exit code, allowing the hosting platform's own crash-restart policy to take over.
Does a SIGTERM from the platform count as a crash?
No — signals like SIGINT and SIGTERM trigger a clean, coordinated shutdown of both the server and the bot, not a restart.
Can I log restart events somewhere external?
StayPresent logs restarts through its own logger; you can attach a handler to logging.getLogger("staypresent") to forward those events wherever you want.
Conclusion
A python bot restart strategy isn't just "turn on auto-restart and hope." It's a combination of sensible delays, a real ceiling on consecutive failures, a counter that resets during genuine stability, and visibility into what's actually happening. StayPresent gives you all of that through a handful of parameters on staypresent.run(), so reliability stops being something you bolt on after your first outage.
pip install staypresent[prod]
Enter fullscreen mode Exit fullscreen mode
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.