Memory leaks are real. Instead of hunting down every one, we take the lazy but effective approach. Here is the setup and when it makes sense.
This sounds like a cop-out, and honestly it kind of is. But there is a pragmatic case for scheduling a daily restart of your background services when you are running a small team with limited time to profile memory usage.
Flask apps that handle many thousands of requests develop memory bloat over time. It is usually not a classic memory leak in the traditional sense, more like a combination of small things: Python's garbage collector not cleaning up in time, some library caching more than expected, connection pool state accumulating. The app does not crash, it just uses progressively more RAM until the VPS starts swapping and response times degrade.
The professional solution is to instrument everything with a memory profiler, identify the specific sources of growth, and fix them one by one. We have done that for the worst offenders. But for the long tail of small services where the memory growth is slow and the cost of investigation is high, a scheduled restart is a reasonable middle ground.
PM2 has a cron_restart option built in:
pm2 start "gunicorn -w 2 -b 127.0.0.1:5000 app:app" \
--name myservice \
--cron-restart "0 3 * * *"This restarts the process at 3am every day. PM2 does a graceful restart by default, which means it starts a new process, waits for it to be ready, then stops the old one. No downtime, no dropped requests.
If you want to add it to an existing process:
pm2 restart myservice --cron-restart "0 3 * * *"
pm2 saveChoose a time when your traffic is lowest. For our LINE bot services that is around 3-4am Singapore time. For the download tools which have global traffic, 3am UTC tends to be a reasonable low point.
When this is not enough
If your service is growing noticeably within a single day, a daily restart is a band-aid on something that needs real attention. Check with:
pm2 monitIf you see memory climbing steeply within a few hours, you need to profile. A few common culprits we have found: unclosed database connections, a requests.Session that keeps growing its connection pool, and logging that writes to an in-memory buffer that never flushes.
For everything else, a 3am restart costs nothing and keeps the service running cleanly.