uWSGI Cheaper Subsystem: Dynamic Process Scaling
Idle workers waste RAM. Discover how uWSGI’s cheaper subsystem dynamically scales worker processes, saving memory while keeping performance high.
27 Jan 2026, 21:02 UTC

The cost of static worker pools
In many Python web deployments, a fixed number of uWSGI workers (e.g., workers = 8) is the default. This provides predictable performance but consumes the same RAM at 3 AM as at noon. For memory‑intensive or multi‑tenant applications, this often means over‑provisioning hardware.
How the cheaper subsystem works
The cheaper subsystem transforms the worker pool from static to dynamic. uWSGI maintains a minimum baseline of workers and spawns additional processes only when the request queue indicates demand. The cheaper-algo setting chooses the scaling logic: spare keeps a pool of idle workers, while busyness monitors average occupancy over a time window.
cheaper-algo = spare: keeps idle workers ready; scales up if idle count drops below a threshold.cheaper-algo = busyness: measures average worker busyness over a window; scales up if occupancy exceeds a percentage threshold.
Concrete configuration: busyness‑based scaling
Below is a working configuration example using the busyness algorithm. Adjust values to match your server’s RAM and typical traffic patterns.
[uwsgi]
module = myapp.wsgi:application
master = true
# Worker limits
workers = 16 # maximum number of worker processes
cheaper = 4 # minimum workers to keep alive
# Scaling logic (busyness algorithm)
cheaper-algo = busyness
cheaper-busyness-max = 70 # scale up if workers are 70% busy on average
cheaper-busyness-min = 30 # scale down if average busyness drops below 30%
cheaper-step = 2 # spawn 2 workers at a time to limit CPU spikes
cheaper-busyness-interval = 10 # evaluate busyness every 10 seconds
To apply this, place the file as uwsgi.ini and start uWSGI with uwsgi uwsgi.ini. Ensure you have appropriate permissions to bind the desired HTTP port.
Verification steps
- Check uWSGI logs for
spawned worker Xandkilled worker Xentries as request volume changes. - Monitor OS processes using
htoporps aux | grep uwsgito see the actual process count fluctuate in real time. - Run a load test with tools like
wrkorabto send burst traffic and show the worker count climb toward theworkersceiling.
Trade‑offs and limitations
Dynamic scaling introduces operational risks that require careful tuning:
- If your application performs heavy initialization (large model loads, complex DB connections), new workers cause CPU spikes that may trigger further scaling in a potential death spiral.
- Setting
cheaper-busyness-maxandcheaper-busyness-mintoo close together causes repeated spawn/kill cycles. Always leave a wide gap between thresholds. - Ensure your
workersmaximum is calculated from the worst‑case memory per worker. Sixteen workers at 500 MB each on a 4 GB server will cause out‑of‑memory errors during peak load.
Decision guide
Use the table below to choose the right approach for your deployment:
| Approach | When to use |
|---|---|
| Static pool (no cheaper) | Consistent traffic, abundant memory |
| Cheaper + spare | Sporadic, high‑intensity bursts |
| Cheaper + busyness | Predictable traffic curves, maximize hardware efficiency |
Start with conservative thresholds (e.g., cheaper = 2, workers = 10, cheaper-algo = spare) and adjust based on real‑time process monitoring. The cheaper subsystem is a powerful lever for resource efficiency, but it rewards measured tuning over aggressive defaults.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.