Using uWSGI lazy-apps to cut worker memory footprint
Learn how enabling lazy-apps defers Python import until the first request, reducing per‑worker RSS and when to watch out for fork‑unsafe code.
05 Jun 2026, 22:32 UTC

Problem: Workers start heavy even before serving traffic
When you run a typical uWSGI emperor or standalone setup, each worker process loads your Python application immediately after fork. If the application imports large libraries (e.g., NumPy, Pandas, TensorFlow) or builds big data structures at import time, every worker starts with a high resident set size (RSS). This can quickly exhaust memory on modest servers, especially when you run many workers to achieve concurrency.
Thesis: Enabling lazy-apps shifts import work to the first request, letting workers share memory pages via copy‑on‑write and lowering RSS.
How lazy-apps works
With lazy-apps = true in the uWSGI configuration, the master process forks workers before executing the WSGI callable. The workers initially contain only a minimal interpreter and the uWSGI framework. The actual application object is not imported until the first request hits a worker. Because the workers are forked from the same master, the memory pages that hold the shared code and read‑only data stay copy‑on‑write; each worker only incurs extra RAM for the data it actually touches during import.
Practical configuration example
[uwsgi]
master = true
processes = 4
socket = 127.0.0.1:3031
module = myproject.wsgi:application
# Enable lazy loading of the Python app
lazy-apps = true
# Optional: log when lazy mode is active
log-maxsize = 5M
Place this file (e.g., myapp.ini) in your uWSGI config directory and restart the emperor or the standalone instance:
# Run as a user with permission to bind the socket and read the code
uwsgi --ini myapp.ini
Trade‑offs and limitations
- First‑request latency: The worker that receives the inaugural request will pay the import cost, which can add a few hundred milliseconds to that request’s response time. Subsequent requests benefit from the already‑loaded modules.
- Fork‑safety requirement: Any code that performs non‑fork‑safe operations at import time (e.g., initializing a CUDA context, opening a database connection, starting a background thread, or using
fork‑unsafe C extensions) will break when executed after the fork. You may see crashes or undefined behaviour only on the first request. - Debugging visibility: Import‑time errors are deferred until the first request, making it harder to catch them during early testing or in staging environments that receive little traffic.
Verification steps
- Start uWSGI with lazy-apps enabled as shown above.
- Identify a worker PID, for example via
ps -o pid,rss,command | grep uwsgi. - Before any traffic, record the RSS of that worker:
ps -o pid,rss= -p <worker-pid>. - Send a single HTTP request (e.g.,
curl http://127.0.0.1:3031/). - Check the RSS again; you should see a lower value compared to a run with
lazy-apps = false(all else equal). - Confirm in the uWSGI log that the line
Lazy apps mode enabledappears at startup and that nofork unsafewarnings are emitted when the first request is processed. - Optionally run a light load test with
hey -n 100 -c 10 http://127.0.0.1:3031/and compare average RSS and latency against the non‑lazy baseline.
If the RSS does not drop, verify that the module path in module = points to a valid WSGI callable and that no import statements are executed before the application object is defined (e.g., avoid top‑level code that runs on import).
Actionable closing
Enable lazy-apps = true when your Python app has heavy import‑time work and you can ensure fork‑safe initialization. Measure the RSS before and after the first request to confirm the memory gain, and monitor the first‑request latency to ensure it stays within your SLA. If you encounter crashes, move any fork‑unsafe initialization inside the first request handler or use the --exec-pre-app hook to run it after fork but before the first request.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.