uWSGI
Overview
uWSGI is a fast application server for hosting Python WSGI web applications. It runs your app in a supervised pool of worker processes, load balances requests across them, restarts crashed workers automatically, and exposes a live stats interface. nginx fronts it over a local unix socket.
This SecureAnyCloud image builds a pinned uWSGI 2.0.31 from the official source, with the Python 3 (WSGI) plugin compiled in. It runs uWSGI in Emperor mode, so one node can host one or many apps, each as an isolated vassal. uWSGI has no admin console or login, so the image ships a small Python WSGI demo app at / that proves the server executes code on every request.
What you get:
- uWSGI 2.0.31, the latest stable release (open source, GPL-2.0 with a linking exception), built from the official PyPI source with the Python 3 WSGI plugin embedded
- The uWSGI Emperor running as a systemd service (
uwsgi-emperor.service), supervising a vassal that spawns a master plus two workers - A plain-WSGI (PEP 3333) Python demo app at
/, served by uWSGI over a unix socket and fronted by nginx on:80 - A live server timestamp and worker PID, computed in Python on every request, so you can see uWSGI executing real code rather than serving a static file
- A uWSGI stats server bound to loopback only (
127.0.0.1:9191) for live worker inspection uwsgi-firstboot.service, a systemd oneshot that starts the Emperor and nginx, confirms the demo app answers, and writes a non-secret endpoints notes file- Secure defaults: the stats server is never exposed off-box,
server_tokens offis set, and there is no admin login surface and no baked credential - nginx on
:80as the only public entry point - An Ubuntu 24.04 LTS base with the latest security patches applied at build time
- The Azure Linux Agent for cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA
Prerequisites
You need an active Azure subscription, an SSH public key, and a VNet with a subnet in the target region. Your subscription must also be subscribed to the uWSGI listing on Azure Marketplace.
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for development and light application workloads. For higher concurrency or CPU-bound apps, use Standard_D4s_v5 (4 vCPU, 16 GB RAM) or larger.
Deploy from the Azure Portal
Search the Marketplace for uWSGI, select the SecureAnyCloud publisher, and click Create. In the network security group (NSG), allow TCP 22 (admin) and TCP 80 (HTTP) from your client networks. Also allow TCP 443 if you terminate TLS on the instance.
Deploy from the Azure CLI
RG="uwsgi-prod"; LOCATION="eastus"; VM_NAME="uwsgi-01" IMAGE_URN="sac1647283583153:uwsgi-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name uwsgi-vnet --address-prefix 10.100.0.0/16 --subnet-name uwsgi-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name uwsgi-nsg az network nsg rule create -g "$RG" --nsg-name uwsgi-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name uwsgi-nsg --name allow-http --priority 110 \
--destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:uwsgi-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name uwsgi-vnet --subnet uwsgi-subnet --nsg uwsgi-nsg --public-ip-sku StandardConnect via SSH
ssh azureuser@<vm-ip>Connect as azureuser. uwsgi-emperor.service and nginx.service start automatically on first boot. There is no admin account to set up, because uWSGI has no login.
Verify uWSGI and nginx
uwsgi --version sudo systemctl is-active uwsgi-emperor.service
sudo systemctl is-active nginx.service
sudo ss -tln | grep ':80 'Run these commands to check the version and services. Expected output: the pinned uWSGI is present, both services are active, and nginx is listening on :80.
2.0.31 active active LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 511 [::]:80 [::]:*Request the Python WSGI demo app
uWSGI has no admin UI, so the demo app is the proof that the image works. Request the demo app at / twice, one second apart:
curl -s http://127.0.0.1/ sleep 1
curl -s http://127.0.0.1/Expected output: the epoch and Worker PID show that uWSGI executes the app on every request.
<!doctype html><html><head><meta charset='utf-8'><title>uWSGI on sac</title></head><body><h1>uWSGI (open source)</h1><p>This page is created by a Python WSGI application running under uWSGI, fronted by nginx over a unix socket.</p><p>Server time: 2026-07-20T06:42:49Z epoch=1784529769</p><p>Worker PID: 7320</p><p>The epoch and PID above are computed in Python on every request — this is a live application, not a static file.</p></body></html>The epoch value increments between calls. It is computed in Python on every request, so it cannot come from a cached or static file. This is work an application server does that a plain static web server cannot. The request travels nginx :80 → uwsgi_pass → unix socket /run/uwsgi/demo.sock → a uWSGI worker → your Python callable.
Inspect live workers with the stats server
The stats server shows the worker processes uWSGI manages. It is bound to loopback only (127.0.0.1:9191) and never exposed off-box. Warm the app, then read the stats socket:
curl -s -o /dev/null http://127.0.0.1/ python3 - <<'PYEOF' import socket, json s = socket.create_connection(('127.0.0.1', 9191), timeout=5); s.settimeout(2) chunks = [] try: while True: c = s.recv(65536) if not c: break chunks.append(c) except socket.timeout: pass d = json.loads(b''.join(chunks)) print('uWSGI', d['version'], 'master pid', d['pid']) for w in d['workers']: print('worker id=%s pid=%s status=%s requests=%s' % (w['id'], w['pid'], w['status'], w['requests'])) PYEOFExpected output: the master plus two live workers, each with its PID, status and request count.
uWSGI 2.0.31 master pid 7319 worker id=1 pid=7320 status=idle requests=2 worker id=2 pid=7321 status=idle requests=3uWSGI spawns these workers under the Emperor and restarts any that crash. The master respawns a replacement transparently, and the stats server reports the new PID.
Read the endpoint notes and vassal config
The first-boot service writes a non-secret notes file that documents the demo URL and how to deploy your own app. uWSGI has no credentials to rotate. A single Emperor vassal .ini defines the demo app. Print the notes file and list the vassals:
sudo cat /var/lib/sac/data/uwsgi-endpoints.notes ls /etc/uwsgi-emperor/vassals/Expected output: the notes file and the demo vassal.
# uWSGI — Endpoint Notes
# Generated on first boot by uwsgi-firstboot.service
# UWSGI_DEMO_URL=http://<vm-ip>/ UWSGI_VERSION=2.0.31 UWSGI_APP_ROOT=/opt/uwsgi-demo UWSGI_VASSAL=/etc/uwsgi-emperor/vassals/demo.ini UWSGI_SOCKET=/run/uwsgi/demo.sock UWSGI_STATS=127.0.0.1:9191 (loopback only) demo.iniDeploy Your Own WSGI App
uWSGI serves any Python WSGI application: Flask, Django, FastAPI (via a WSGI adapter), or a plain callable. Copy your app to the VM:
scp -r ./myapp azureuser@<vm-ip>:/tmp/myapp ssh azureuser@<vm-ip> 'sudo mv /tmp/myapp /opt/myapp && sudo chown -R uwsgiapp:uwsgiapp /opt/myapp'Next, give the app its own Emperor vassal. Copy the bundled vassal as a template, point it at your app, and place it in the vassals directory. The Emperor loads it automatically, with no restart:
sudo cp /etc/uwsgi-emperor/vassals/demo.ini /etc/uwsgi-emperor/vassals/myapp.ini
sudo sed -i 's#/opt/uwsgi-demo#/opt/myapp#; s#demo.sock#myapp.sock#; s#9191#9192#' /etc/uwsgi-emperor/vassals/myapp.iniAdd a matching nginx location with uwsgi_pass unix:/run/uwsgi/myapp.sock;. The bundled /etc/nginx/sites-available/sac-uwsgi vhost is a working reference. Then validate the configuration and reload nginx:
sudo nginx -t && sudo systemctl reload nginxRun these commands against your own app on your own VM.
Server Components
Component Path
uWSGI binary (pinned 2.0.31) /usr/local/bin/uwsgi
Emperor systemd unit /etc/systemd/system/uwsgi-emperor.service
Demo app vassal /etc/uwsgi-emperor/vassals/demo.ini
Python WSGI demo app /opt/uwsgi-demo/app.py
App socket /run/uwsgi/demo.sock
Stats server 127.0.0.1:9191 (loopback only)
nginx vhost /etc/nginx/sites-available/sac-uwsgi
Application user uwsgiapp (unprivileged system user)
Logs /var/log/uwsgi-emperor.log + systemd journal + /var/log/nginx/
Firstboot script /usr/local/sbin/uwsgi-firstboot.sh
Firstboot service /etc/systemd/system/uwsgi-firstboot.service
Endpoint notes /var/lib/sac/data/uwsgi-endpoints.notes (mode 0644)
Firstboot sentinel /var/lib/sac/data/uwsgi-firstboot.done
Manage uWSGI and nginx
sudo systemctl status uwsgi-emperor.service --no-pager sudo systemctl status nginx.service --no-pager sudo systemctl reload nginx.serviceTo pick up new application code without a full restart, touch the vassal. The Emperor then performs a graceful vassal reload:
sudo touch /etc/uwsgi-emperor/vassals/demo.iniSecurity recommendations
- Keep the stats server on loopback. It is already configured this way. Never bind
127.0.0.1:9191to a public address. - Add TLS. Front nginx with Let's Encrypt/Certbot and open
:443. - Restrict the NSG. For a private service, make
:80and:443reachable only from your client networks. - Run apps as an unprivileged user. The demo app runs as
uwsgiapp. Keep your own apps off root. - Replace the demo app. Remove
/opt/uwsgi-demo/and its vassal once you deploy your own application. - Patch the OS monthly with
sudo apt-get update && sudo apt-get upgrade && sudo reboot.
Support and licensing
uWSGI is licensed under the GNU General Public License version 2 with a linking exception. The exception explicitly permits linking and serving your own applications, including closed-source ones, without restriction from the uWSGI license. There is no per-CPU or per-deployment fee for uWSGI itself.
SecureAnyCloud provides commercial support for this image separately from the upstream project.
- Email: sales@secureanycloud.com
- Website: secureanycloud.com
- Support hours: 24/7 with a guaranteed 24-hour response SLA
Deploy on Azure
Launch uWSGI on Ubuntu 24.04 with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Deploy on Azure
Launch uWSGI on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
uWSGI is a mature application server for hosting Python WSGI web applications and, through its plugins, other languages. It runs your app in a supervised pool of worker processes, load balances requests across them, restarts crashed workers automatically, and exposes a live stats interface. In Emperor mode it hosts one or many apps on a single node, each as an isolated vassal, with nginx as the front door over a unix socket.
This SecureAnyCloud image is infrastructure, not a finished end-user application. It gives you a wired-up, hardened application server that you drop your own WSGI/Python app into. There is no admin console or login, so the image ships a tiny Python WSGI demo app as a health and example endpoint. Replace it with your own Flask, Django or FastAPI application.
What you get:
- uWSGI 2.0.31 (open source, GPL-2.0 with a linking exception that explicitly permits serving your own closed-source apps), built from the official PyPI source with the Python 3 WSGI plugin embedded
- nginx as the
:80front door, proxying to uWSGI over a unix socket withuwsgi_pass - A plain-WSGI Python demo app at
/, served by uWSGI through nginx over the unix socket/run/uwsgi/demo.sock - A live server timestamp and worker PID that change on every request, showing uWSGI executing real Python code rather than serving a static file
- A dedicated EBS application data volume mounted at
/opt/uwsgi-demo, so your app code and vassal configs live on their own independently resizable disk uwsgi-firstboot.service, a systemd oneshot that starts the Emperor and nginx, confirms the demo app answers, resolves the instance public IP, and writes a non-secret endpoints notes file- Secure defaults: the uWSGI stats server binds to loopback only (
127.0.0.1:9191) and is never exposed off-box, nginx setsserver_tokens off, and the Emperor runs no privileged vassal - nginx on
:80as the only public entry point, with no admin login surface and no baked credential - An Ubuntu 24.04 LTS base with the latest security patches applied at build time, and the AWS SSM agent installed for Systems Manager integration
- 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA
Upstream, uWSGI is a stable, widely deployed application server in maintenance mode: it receives security and correctness fixes rather than new-feature development. SecureAnyCloud ships the latest stable release and keeps the image patched.
Prerequisites
You need an AWS account subscribed to the uWSGI listing on AWS Marketplace, an EC2 key pair, and a VPC with a public subnet. The security group must allow inbound TCP 22 (SSH) and TCP 80 (HTTP) from your client networks.
Recommended instance type: m5.large (2 vCPU, 8 GB RAM) for development and light-to-moderate application workloads. For higher concurrency or CPU-bound apps, use m5.xlarge or larger.
Connecting to your instance
Each OS variant of this listing has its own default SSH login user. Use the row matching the AMI variant you launched.
OS variant SSH login user Example
Ubuntu 24.04 LTS ubuntu ssh -i your-key.pem ubuntu@<instance-public-ip>
Launch from the AWS Console
In the AWS Marketplace or the EC2 console, subscribe to the uWSGI listing and choose Launch. Select an m5.large (or larger) instance type, your key pair, and a security group that allows:
- TCP 22 (SSH) from your management network
- TCP 80 (HTTP) from your client networks
Also allow TCP 443 if you plan to terminate TLS on the instance.
Launch from the AWS CLI
AMI_ID=<the-uwsgi-ami-id> aws ec2 run-instances \
--image-id "$AMI_ID" \
--instance-type m5.large \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--subnet-id subnet-xxxxxxxx \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=uwsgi-01}]'Connect via SSH
ssh -i your-key.pem ubuntu@<public-ip>Connect as ubuntu. uwsgi-emperor.service, nginx.service and uwsgi-firstboot.service start automatically on first boot. There is no admin account to set up, because uWSGI has no login.
Verify uWSGI and nginx
/usr/local/bin/uwsgi --version sudo systemctl is-active uwsgi-emperor.service nginx.service
sudo ss -tln | grep ':80 'Expected output: uWSGI reports the pinned version, both services are active, and nginx is listening on :80.
2.0.31 active active LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 511 [::]:80 [::]:*nginx proxies port 80 to uWSGI over the unix socket /run/uwsgi/demo.sock. The Emperor supervises the vassal that serves the app.
Request the Python WSGI demo app
uWSGI has no admin UI, so the demo app is the proof that the image works. Request the app at / and confirm it returns HTTP 200 through nginx to the uWSGI socket:
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/
curl -s http://127.0.0.1/Expected output: the epoch and Worker PID show that uWSGI executes the Python app on every request.
HTTP 200 <!doctype html><html><head><title>uWSGI on sac</title></head><body><h1>uWSGI (open source)</h1><p>This page is created by a Python WSGI application running under uWSGI, fronted by nginx over a unix socket.</p><p>Server time: 2026-07-23T16:35:41Z epoch=1784824541</p><p>Worker PID: 4263</p></body></html>Request the page twice, a couple of seconds apart. The epoch increments because it is computed in Python on every request, so it cannot come from a cached or static file. A plain static web server cannot do this:
curl -s http://127.0.0.1/ | grep -oE 'epoch=[0-9]+' sleep 2
curl -s http://127.0.0.1/ | grep -oE 'epoch=[0-9]+'Expected output: two different epoch values.
epoch=1784824558 epoch=1784824560You can also open http://<instance-public-ip>/ in a browser.
Inspect live workers with the Emperor and stats server
The uWSGI Emperor supervises the demo vassal, which runs a master and a small pool of worker processes. Inspect the Emperor's process tree, then read the loopback-only stats server:
sudo systemctl status uwsgi-emperor.service --no-pager cat < /dev/tcp/127.0.0.1/9191Expected output: the Emperor is active (running) and supervises the vassal master plus worker processes. The stats server returns JSON that lists the live workers and their PIDs.
● uwsgi-emperor.service - uWSGI Emperor (sac) Active: active (running) Main PID: 4256 (uwsgi) CGroup: /system.slice/uwsgi-emperor.service 4256 uwsgi --master --emperor /etc/uwsgi-emperor/vassals 4260 uwsgi --ini demo.ini 4262 uwsgi --ini demo.ini 4263 uwsgi --ini demo.ini {"version":"2.0.31", ... "workers":[{"id":1,"pid":4262,"status":"idle", ...},{"id":2,"pid":4263,"status":"idle", ...}], "sockets":[{"name":"/run/uwsgi/demo.sock","proto":"uwsgi", ...}]}The stats server is bound to 127.0.0.1 only, so it is never reachable off-box. uWSGI respawns any worker that crashes.
Run a burst test and read the endpoint notes
Send a 25-request burst and confirm that each request returns 200. Then read the non-secret endpoints notes file that the first-boot service wrote:
OK=0; for i in $(seq 1 25); do
C=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/); [ "$C" = 200 ] && OK=$((OK+1)); done; echo "burst 200s: $OK/25"
sudo cat /var/lib/sac/data/uwsgi-endpoints.notesExpected output: all 25 requests return 200, and the notes file documents the demo URL and where to deploy your own app. uWSGI has no credentials to rotate.
burst 200s: 25/25
# uWSGI - Endpoint Notes UWSGI_DEMO_URL=http://<instance-public-ip>/ UWSGI_VERSION=2.0.31 UWSGI_APP_ROOT=/opt/uwsgi-demo UWSGI_VASSAL=/etc/uwsgi-emperor/vassals/demo.ini UWSGI_STATS=127.0.0.1:9191 (loopback only) DEPLOY_YOUR_OWN_APP=Put your WSGI app under /opt/uwsgi-demo/<name>/, copy the demo vassal, point wsgi-file/chdir/socket at it, then reload nginx.Deploy Your Own WSGI App
uWSGI serves any Python WSGI app, plus other languages via plugins. Your application tier lives on the dedicated EBS volume mounted at /opt/uwsgi-demo. Run the commands below against your own app on your own instance. First, copy your app onto the data volume:
$ scp -r -i your-key.pem ./myapp ubuntu@<public-ip>:/tmp/myapp $ ssh -i your-key.pem ubuntu@<public-ip> 'sudo mv /tmp/myapp /opt/uwsgi-demo/myapp && sudo chown -R uwsgiapp:uwsgiapp /opt/uwsgi-demo/myapp'Next, copy the demo vassal and point it at your app's WSGI callable. Use a new socket path, because each vassal needs its own socket. Place the file in the vassals directory and the Emperor picks it up automatically:
$
sudo cp /etc/uwsgi-emperor/vassals/demo.ini /etc/uwsgi-emperor/vassals/myapp.ini
# edit chdir, wsgi-file, callable and socket=/run/uwsgi/myapp.sock in myapp.ini $
sudo nginx -t && sudo systemctl reload nginxServer Components
Component Path
uWSGI binary /usr/local/bin/uwsgi
Emperor systemd unit /etc/systemd/system/uwsgi-emperor.service
Vassal directory /etc/uwsgi-emperor/vassals/
Demo app vassal /etc/uwsgi-emperor/vassals/demo.ini
Python WSGI demo app /opt/uwsgi-demo/app.py
Application data volume /opt/uwsgi-demo (dedicated EBS volume)
Unix socket /run/uwsgi/demo.sock
Stats server 127.0.0.1:9191 (loopback only)
nginx vhost /etc/nginx/sites-available/sac-uwsgi
Application user uwsgiapp (unprivileged system user)
Emperor log /var/log/uwsgi-emperor.log
Firstboot script /usr/local/sbin/uwsgi-firstboot.sh
Firstboot service /etc/systemd/system/uwsgi-firstboot.service
Endpoint notes /var/lib/sac/data/uwsgi-endpoints.notes (mode 0644)
Firstboot sentinel /var/lib/sac/data/uwsgi-firstboot.done
Manage uWSGI and nginx
sudo systemctl status nginx.service --no-pager sudo systemctl status uwsgi-emperor.service --no-pagerThe Emperor loads, reloads and stops vassals as their .ini files appear, change or are removed in /etc/uwsgi-emperor/vassals/. To pick up new application code, touch the vassal or use uWSGI's own reload. To change the front door, edit the nginx vhost and run sudo systemctl reload nginx.
Security recommendations
- Keep the stats server on loopback. It is already bound to
127.0.0.1:9191and must never be exposed off-box. - Add TLS. Front nginx with Let's Encrypt/Certbot and open
:443, or terminate TLS at an Application Load Balancer with AWS Certificate Manager. No TLS private key is baked into the image. - Restrict the security group. For a private service, make
:80and:443reachable only from your client networks. - Run apps as an unprivileged user. The demo app runs as
uwsgiapp. Keep your own apps off root. - Replace the demo app. Remove
/opt/uwsgi-demo/app.pyand its vassal once you deploy your own application. - Patch the OS monthly with
sudo apt-get update && sudo apt-get upgrade && sudo reboot.
Support and licensing
uWSGI is licensed under the GNU General Public License v2 with a linking exception. The exception explicitly permits linking and serving your own applications, including closed-source ones, without restriction. There is no per-CPU or per-deployment fee.
SecureAnyCloud provides commercial support for this image separately from the upstream project.
- Email: sales@secureanycloud.com
- Website: secureanycloud.com
- Support hours: 24/7 with a guaranteed 24-hour response SLA
Deploy on AWS
Launch uWSGI with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Deploy on AWS
Launch uWSGI Application Server on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com