uWSGI vs Gunicorn

TLDR;

Gunicorn isn’t just “trendy” it actually works really well, in some cases (like mine) works much better than uWSGI. (also it seems that uwsgi is now becoming trendy, so am I just a hipster for switching to the uncool option :P)

History

I have always been a big fan of the uWSGI project. It has always been my number one choice of deployment of python web apps, running behind nginx as a reverse proxy. I have always checked to see if there is anything better out there and have always decided to stick to uWSGI. Data like [Nicholas 2010] [Kgriffs 2012] [DCramer 2013] made it easy for me to stick to my uWSGI choice and make my argument for uWSGI over the more “trendy” gunicorn [DCramer 2013].

I have bumped into / used a couple of alternatives namely phusion passenger (on dreamhost) and “The Green Unicorn”gunicorn (on heroku) which worked well for those use cases. I was always under impressed with the way phusion worked, so it was never a tool I considered deploying.

I had bumped into a bug with uWSGI that seems to be a tricky one to solve. The bug is related to the use of ZeroMQ in a wsgi app. To not sidetrack and get into the gory details of the bug, uWSGI and ZeroMQ use low level socket apis that seem to conflict (when using socket polling). Using one uWSGI worker seemed to mitigate the issue (lazy loading didn’t), but I needed a permanent solution. The use of a single wsgi worker meant that my previous argument that uwsgi is faster/better is out of the door, so I decided to give gunicorn a whirl.

Deployment

With regards to deployment, uWSGI is a dream! usually you install the uWSGI package and the uwsgi-python plugin and you are all set. With gunicorn it is not that easy. Because gunicorn does not have a feature yet to use an app inside a Virtualenv, you can’t really (in a clean setup) use Gunicorn installed globally from your package manager, instead you have to install it inside your virtualenv. Because of this, you will need to either write your own init.d (or compatible) script, or use a process manager (my current choice is supervisor, but keeping a close eye on gaffer). I decided to use supervisor and it wasn’t too much of a hastle to get a clean deployment out of gunicorn. Creating a salt state (sls) to allow the choice between uwsgi sls vs gunicorn sls was really easy. In terms of packages installed, instead of installing the uwsgi package (and daemon), installed gunicorn with pip and the supervisor package (and daemon) so conceptually two things but not really much more.

Note: having a process manager instead of a simple init script system (like what uwsgi allows for and provides) seems like a disadvantage and is definitely something the gunicorn guys probably want to fix (they have a broken init script system as mentioned earlier) but I am starting to think its a good thing. The process manager can restart the web app if it gets wonky. We have a new app that seems to just die under uwsgi, so gunicorn (or even uwsgi) running under supervisord would help mitigate that until we solve that problem.

Performance

Well gunicorn solved my zeromq compatibility problem and I am able to run the server with many workers. So far its working very well and I am starting to let go of my obsession with uWSGI.

Companies like heroku still recommend using gunicorn in their docs, Instagram uses (old blog post) gunicorn to serve requests. So it clearly can handle load.

Alternatives

Recently found out about waitress from the pyramid project. I used to be a big fan of pyramid back when it was still called pylons, I definitely considered myself a pylons guru; since its move to pyramids I have not used it at all. Will definitely check out waitress and compare it to gunicorn.