Right-Sizing Your Python App with uWSGI Cheaper
Stop wasting RAM on idle Python workers. Learn how to use uWSGI's 'cheaper' subsystem and the busyness algorithm to dynamically scale your application processes based on real-time load.
12 Jul 2025, 13:26 UTC

The Cost of Static Worker Pools
When deploying Python web applications via uWSGI, the default approach is to define a fixed number of worker processes. If you set 16 workers to handle peak traffic, those 16 processes consume RAM 24/7, even at 3:00 AM when no one is using the site. In containerized environments or shared VPS setups, this waste often leads to unnecessary infrastructure costs or Out-Of-Memory (OOM) kills when other services spike.
The solution is the cheaper subsystem. Rather than a static pool, cheaper allows uWSGI to dynamically scale workers up and down based on actual demand, freeing system memory during idle periods while maintaining the ability to burst during traffic spikes.
Choosing the Right Scaling Algorithm
uWSGI offers several ways to decide when to spawn new workers. While the basic algorithm scales based on simple availability, the busyness algorithm is generally the most effective for production workloads.
The busyness algorithm monitors how much time workers spend actually processing requests versus sitting idle. If the average busyness across all workers exceeds a specific threshold, uWSGI spawns more. If it drops below a threshold, it kills off the excess.
Key Configuration Parameters
- cheaper: The minimum number of workers to keep alive at all times.
- processes: The absolute maximum number of workers allowed.
- cheaper-step: How many workers to spawn at once when the threshold is hit (prevents spawning 10 workers one-by-one).
- cheaper-busyness-maxresp: The number of requests used to calculate the average busyness. This prevents a single long-running request from triggering a massive scale-up.
Implementation Example: The Busyness Configuration
To implement dynamic scaling, add these directives to your uwsgi.ini file. This example assumes a Python application that needs at least 2 workers for baseline availability but can scale up to 10 during peaks.
[uwsgi]
module = myapp:app
master = true
# Scaling limits
processes = 10
cheaper = 2
cheaper-step = 2
# Busyness algorithm configuration
cheaper-algo = busyness
cheaper-busyness-maxresp = 10
cheaper-busyness-min = 20
cheaper-busyness-max = 70
# Enable stats server for monitoring
stats = 127.0.0.1:9191
Execution and Permissions: Run uWSGI as a non-privileged user. Ensure the stats port is bound to localhost or protected by a firewall, as it exposes internal process data.
Verifying the Scaling Behavior
Configuration is only half the battle; you must verify that the workers are actually cycling. You can monitor this in two ways:
- Master Logs: Watch the uWSGI master log. You should see
spawnevents when load increases andkillevents when the busyness drops below 20% (in the example above). - uwsgi-top: Use the
uwsgi-toputility to connect to the stats server:uwsgi-top 127.0.0.1:9191. This provides a real-time view of how many workers are currently active and their current state.
To test this manually, use a tool like Apache Benchmark (ab) to send a burst of requests: ab -n 1000 -c 20 http://localhost/. You should observe the worker count climb toward 10 and then slowly recede after the test completes.
Trade-offs and Limitations
Dynamic scaling is not a silver bullet. There are two primary risks to consider:
The Cold-Start Latency
Spawning a new Python process is expensive. The first few requests that trigger a scale-up will experience higher latency because they must wait for the master process to fork and the worker to initialize the application code.
Memory Fragmentation
Python's memory management can be stubborn. Even when uWSGI kills a worker process, the operating system may not immediately reclaim all memory due to fragmentation. If you see memory usage staying high despite workers being killed, you may need to implement max-requests to periodically recycle workers regardless of load.
Actionable Summary
If your server is idling with high RAM usage, switch from a static processes count to the cheaper-algo = busyness configuration. Start with a conservative cheaper-busyness-max (around 70%) to avoid "thrashing"—the rapid, inefficient cycle of spawning and killing workers—and monitor your master logs to fine-tune the thresholds based on your specific request patterns.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.