In another of our "you can do that?!?" with uWSGI posts, today I'll show you how to use uWSGI to host multiple sites and properly route traffic based on the hostname to those sites.
Multiple Sites (aka Emperor Mode)
uWSGI provides the ability to run multiple sites via emperor mode. This feature acts like a process manager, reading any configuration files found in a specific directory and spinning up additional uWSGI instances (aka "vassals") to execute them. Typically I'd use something like systemd for this, but in the case of most PaaS providers, that's not usually available.
The simplest way to enable emperor mode is to pass a directory where uWSGI will find the configuration files for the vassals:
emperor = %dvassals.d/*.ini
%d is a special variable in uWSGI that points to the directory of the configuration file. If we were to put this in
/etc/uwsgi/uwsgi.ini, the path for the vassal configs would be
As with most things uWSGI, the emperor has a million different options. It's worth reading the docs to see if any other features would be useful to your specific use case.
Nothing special is needed for your vassal configuration files. Do the same thing you would for any uWSGI site. You may, however, find the
vassal-set configuration option handy to prevent repeating yourself for common settings in every vassal config.
Emperor mode might be old news for you, but routing is where things start to get interesting. uWSGI's internal router is really powerful and can do all sorts of crazy things. We'll use it to route requests by hostname to the corresponding vassal. First, we need to tell the main uWSGI process to listen on a port for incoming requests:
# HTTP listener on port 8080
http = :8080
Then we can add a router that watches for requests on a specific hostname and forwards them to the listening socket of the appropriate vassal:
route-host = ^www\.example\.com$ uwsgi:/tmp/uwsgi_example_vassal.sock,0,0
In this case, we're routing any requests to
www.example.com through a Unix socket at
/tmp/uwsgi_example_vassal.sock using the uWSGI protocol. The last two numbers are "uWSGI modifiers". If you're using Python, you probably want to leave them at
You can add as many of these
route-host directives as you want. You can also get fancy and declare them dynamically with the regex:
route-host = ^([-\w]+)\.example\.com$ uwsgi:/tmp/uwsgi_$1.sock,0,0
Here we extract the subdomain of the request and try to route it to the corresponding socket. So,
site.example.com would route to
This is all novel and clever, but would I actually use it in production? Certainly specialized tools like systemd and Nginx/HAProxy are better at these jobs than what uWSGI provides, right? Like most things, it depends.
If it's straightforward to do it all in uWSGI, it's a win against complexity. Every service you add to your stack is something else you need to learn to use, maintain, and another possible point of failure. If one tool can do the job of three, why not use it?
On the otherhand, I have two reservations about cramming everything into uWSGI.
- Specialized tools are probably far more efficient and robust. If scaling your website to lots of traffic is on the radar, test and measure to make sure you don't introduce a bottleneck.
- Don't cram a square peg into a round hole. The goal here is to reduce complexity, so if you feel like things are getting too complex, that's a good sign it might be time to look at other options.
I initially used this to migrate a number of micro-sites that ran on the same code base from a VM to Heroku. Since Heroku charges per process, this little hack allowed us to run multiple sites on a single Heroku instance. It saved on the hosting costs and only required a few extra lines in the uWSGI config to accomplish it. The sites are low traffic, so scaling is not a concern. Overall, I've been happy with the results.
Have questions or a good uWSGI tip to share? Drop us a note in the comments!