A tutorial for adding automatic crash recovery to an existing python script with zero code changes to the script itself, using StayPresent.
Add Crash Recovery to Any Python Script in One Line
If you have a Python script that runs continuously — a bot, a scraper, a queue worker — and it currently just dies and stays dead the moment something throws an unhandled exception, this tutorial covers the fastest way to fix that: automatic restarts, with a sensible ceiling so a genuine crash loop doesn't run forever, and without touching a single line of the script itself.
Table of Contents
- Why Python Doesn't Restart Itself
- Step 1: Install StayPresent
- Step 2: Wrap Your Script (No Code Changes)
- Step 3: Confirm It Restarts on a Real Crash
- Step 4: Tune the Restart Behavior
- Step 5 (Optional): Prevent Endless Crash Loops
- Step 6 (Optional): Reset the Counter After Stability
- What Counts as a "Clean Exit" (No Restart)
- FAQs
- Conclusion
Why Python Doesn't Restart Itself
Once an unhandled exception reaches the top of a Python script, the interpreter exits — nothing built into the language relaunches it. For a one-off script that's exactly the right behavior. For something meant to run indefinitely, it means a single uncaught error ends its uptime until a human manually restarts it.
Step 1: Install StayPresent
pip install staypresent[prod]
Step 2: Wrap Your Script (No Code Changes)
Say your script is worker.py. It stays completely untouched:
# main.py
import staypresent
staypresent.run("worker.py", web_server=False)
web_server=False is the key detail here — it gives you crash recovery with no HTTP server at all, which is exactly what you want for a script that doesn't need to satisfy a hosting platform's port requirement (a local script, a VPS process with no platform-level health check, or anything you're deliberately keeping off the network).
Run it:
python main.py
Step 3: Confirm It Restarts on a Real Crash
Test it deliberately — add a line that raises an exception partway through worker.py, run main.py, and watch the console. You should see the crash logged, a short delay, and the script relaunching automatically.
Step 4: Tune the Restart Behavior
staypresent.run(
"worker.py",
web_server=False,
restart_on_crash=True,
max_restarts=5,
restart_delay=2.0,
)
-
max_restartscaps consecutive crashes — this is what stops a genuine crash loop (a missing config value, a bad credential) from restarting forever. -
restart_delayadds a pause before each relaunch, useful if the crash was caused by something transient like a rate limit.
Step 5 (Optional): Prevent Endless Crash Loops
If your script hits max_restarts, staypresent.run() exits the whole process with the script's original exit code — this matters if you're also running under Docker or systemd, since it lets that outer layer's own restart policy act as a final backstop instead of the process quietly staying dead.
Step 6 (Optional): Reset the Counter After Stability
staypresent.run(
"worker.py",
web_server=False,
max_restarts=5,
restart_reset_after=60.0,
)
If your script stays up for at least 60 seconds after a restart, its crash counter resets to zero — so a script that crashes once every few days doesn't slowly creep toward its restart ceiling from unrelated, infrequent failures.
What Counts as a "Clean Exit" (No Restart)
An exit code of 0 is treated as intentional and never triggers a restart — useful if your script has legitimate logic to shut itself down on purpose (a scheduled one-time job, a manual stop condition):
import sys
# inside worker.py, when the script should stop on purpose
sys.exit(0) # StayPresent will NOT restart this
Anything else — an unhandled exception, sys.exit(1), a segfault — is treated as a crash and restarted according to your configuration.
FAQs
Do I need an HTTP server for this to work?
No — web_server=False gives you crash recovery with zero HTTP surface, which is the right choice for a script that doesn't need to satisfy a platform health check.
Does this work for scripts with multi-line entry points, not just simple loops?
Yes — staypresent.run() launches your script as a subprocess exactly the way python worker.py would from the command line; it doesn't care what's inside it.
Can I add crash recovery to more than one script at once?
Yes — pass a list: staypresent.run(["worker1.py", "worker2.py"], web_server=False), each supervised independently.
Conclusion
Adding real crash recovery to an existing Python script doesn't require rewriting anything inside it — staypresent.run("worker.py", web_server=False) gives you automatic restarts, a sensible ceiling against crash loops, and a counter that resets after genuine stability, all without a single line of change to the script being supervised.
pip install staypresent[prod]
Top comments (0)