Using uWSGI Emperor Mode to Manage Multiple Vassal Apps with Zero‑Downtime Reloads
Learn how uWSGI Emperor mode watches a directory of vassal configs to spawn, reload, and isolate multiple WSGI apps with zero‑downtime updates.
06 Sept 2026, 01:46 UTC

Problem: Keeping Several WSGI Apps Up‑to‑Date Without Dropping Requests
When you run more than one Python WSGI application on the same host, you need a way to start, stop, and reload each app independently. Doing this manually with separate uWSGI instances leads to duplicated socket binding, scattered logs, and a higher chance of accidental downtime when you edit a configuration file.
Thesis: Emperor Mode Gives a Single Master Process That Vassals Your Apps
uWSGI’s Emperor mode runs a master process (the Emperor) that watches a directory for vassal configuration files (usually .ini). When a file appears, changes, or disappears, the Emperor spawns, reloads, or removes the corresponding vassal workers. Each vassal gets its own address space, so you can isolate virtual environments, UID/GID settings, and plugin sets while sharing the Emperor’s socket binding and logging infrastructure.
Setting Up a Minimal Emperor‑Vassal Layout
- Create a directory that will hold vassal configs (e.g.,
/etc/uwsgi/vassals). Ensure the user that will run the Emperor has read access to this directory. - Add two simple vassal files. Replace
YOUR_APP_MODULEwith the actual WSGI callable for each app.
# /etc/uwsgi/vassals/app1.ini
[uwsgi]
module = YOUR_APP_MODULE:application
master = true
processes = 2
socket = /tmp/app1.sock
chmod-socket = 660
vacuum = true
# /etc/uwsgi/vassals/app2.ini [uwsgi] module = YOUR_APP_MODULE:application master = true processes = 2 socket = /tmp/app2.sock chmod-socket = 660 vacuum = trueStarting the Emperor and Observing a Reload
Run the Emperor from a terminal where you have permission to bind to the sockets (or use a privileged port if you run as root, but prefer a non‑root user with
CAP_NET_BIND_SERVICE).# Run as a dedicated uwsgi user (example) uwsgi --emperor /etc/uwsgi/vassals \ --emperor-pidfile /run/uwsgi-emperor.pid \ --daemonize /var/log/uwsgi-emperor.logThe Emperor will read both
.inifiles, spawn two vassal groups, and bind each to its own socket. To test zero‑downtime reload:
- Send a few requests to one of the sockets (e.g., using
curl --unix-socket /tmp/app1.sock http:/_/) and note the response.- Touch the vassal config file to simulate a change:
sudo touch /etc/uwsgi/vassals/app1.iniCheck the Emperor log (
/var/log/uwsgi-emperor.log) for a line similar to:[emperor] vassal app1.ini has been spawnedand later a line indicating a SIGHUP was sent and the vassal reloaded:
[emperor] vassal app1.ini: sending SIGHUP to vassal for reloadIf you continue to send requests during the touch, you should see no dropped connections; the Emperor’s master process keeps the socket open while the vassal workers finish current requests and then restart.
Verifying Isolation Between Vassals
Each vassal can report its own Python interpreter to prove address‑space separation. Deploy a tiny WSGI app that returns
sys.executable:def application(environ, start_response): import sys output = sys.executable.encode('utf-8') start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', str(len(output)))]) return [output]Request the endpoint from both sockets; you should get two different paths if you configured distinct virtualenvs in each
.ini(usingvirtualenv = /path/to/venv). The Emperor process itself will show the system interpreter, confirming that vassals do not share the same interpreter instance.Trade‑offs and Limitations
- Filesystem notification dependency: Emperor relies on
inotify to detect changes instantly. On network filesystems lacking inotify support, it falls back to polling, which can increase latency and CPU usage. You can verify the mode by checking the Emperor log for "using poll mode" after startup. - Privilege risk: If the Emperor runs as root and a vassal is mis‑configured with an unsafe
uidorgid, the vassal could inherit excessive permissions. A practical safeguard is to run the Emperor as an unprivileged user that has only the rights needed to bind the sockets (or useCAP_NET_BIND_SERVICE) and to enforceuid/gidsettings inside each vassal. - Resource limits are per‑vassal: Settings like
max-requests,max-memory, or the cheap algorithm apply only to the individual vassal, which is useful for isolation but requires you to duplicate limits across many files if you want uniform enforcement.
Actionable Closing
Emperor mode is a battle‑tested way to host multiple WSGI applications on a single host while gaining zero‑downtime reloads and strong isolation. To adopt it:
- Choose a dedicated, non‑root user for the Emperor process.
- Place vassal
.inifiles in a directory backed by a local filesystem with inotify support. - Start the Emperor with daemonized logging and monitor its log for vassal spawn/reload events.
- Verify isolation by having each vassal return distinct interpreter paths or environment variables.
- Periodically check that the Emperor is not falling back to polling (look for poll mode messages) and that resource limits are behaving as expected.
When these steps are in place, you can update an app’s configuration or code by simply touching its .ini file, confident that existing requests will finish and the new version will take over without a noticeable blip for users.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.