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.

  • http://vinitkumar.me Vinit Kumar

    We have used waitress in production but it doesn’t work well with load when used as a server for Django app. It gave us some downtime, then we switched back again to gunicorn + nginx combo which has been working like charm for us. I highly recommend using gunicorn with nginx everywhere. It is the best solution AFAIK.

    • Hatem Nassrat

      I highly agree nginx + gunicorn is the way to go. That being said there is a few web servers out there that will start to give nginx a run for its money like they did to apache earlier.

      • http://vinitkumar.me Vinit Kumar

        Can you share the name of these servers?

        • wutwut

          yaws

  • unbit

    Obviously nothing to say against gunicorn, but you talk about a “bug” (sorry but i am pretty doubtful about it as it is really easy to mess things up as zeromq does its magic using a background thread) you did not reported and i am pretty sure it is something really easy to address. Instead, regarding supervisord, i am not agree 😛 I am pretty sure the Emperor is a way better (and more focused) approach 🙂

    • Hatem Nassrat

      I agree regarding uWsgi’s emperor. It is such a clean setup, including how it can use a virtualenv, a feature that without it gunicorn’s emperor like implementation is 100% broken! Because of the emperor’s cleanliness I never needed to use supervisord or similar. That being said what I mentioned was a special situation, for some reason our uwsgi workers are occasionally dying in an alpha app we built, we will fix it; but having a parent process to autorestart the workers is a quick fix.

      Re: the 0mq bug, it very well might be 0mq related but I did see a bunch of threads on the topic. Here is a couple:

      * http://grokbase.com/t/zeromq/zeromq-dev/10bqyxdybk/incompatibility-between-pyzmq-and-uwsgi
      * http://lists.unbit.it/pipermail/uwsgi/2010-November/000913.html

      We had it working with 1 worker (which was a slow solution) and we had multiple 1 worker deploys which made the deployment complicated. When we moved to AWS, it doesn’t even work even with 1 worker, perhaps something about the AWS virt drivers making this bug even worse. This is what prompted the look for an alternative as mentioned.

      • unbit

        Since 2010 (the time of that posts) we introduced dozens of fork()-related modes 🙂 There are ways to setup uWSGI like mod_wsgi or gunicorn. Writing a post in the ml or a issue on github would have been faster than changing deployment strategy (well i hope ;). The role of restarting a broken/stuck worker is the role of the master, the Emperor operates exacly at the supervisord level. If the master is stuck for zeromq, then something is seriously wrong in the whole setup.

        Probably there are other reason (it looks like you were using debian/ubuntu packages, that if i remember correctly are linked with libzmq, so you could have hit some incompatibilities between library version).

        Do not get me wrong, gunicorn is great, i think mod_wsgi, uWSGI and gunicorn offer 3 different approaches (both in spirit and in technologies) so every comparison is simply useless (is like comparing religions).

        But in this specific scenario, reporting your problem (and discussing about it) could have been more useful (from various point of views). Integration between uWSGI and ZeroMQ is strong by years (there are 4 zeromq-related plugins in the official distribution and two third party ones), covering more aspects is very important

        • Hatem Nassrat

          fair enough

  • Eric Wilson

    “With regards to deployment, uWSGI is a dream! usually you install the uWSGI package and the uwsgi-python plugin and you are all set.”

    You lost me here. Installing uWSGI is my nightmare. Pip or apt? Virtualenv or not? Emperor mode? Demonized?

    Makes me wish for the simplicity of Tomcat

    • hatem

      Well I wasn’t clear, i meant with apt. Gunicorn, as explained, doesn’t work for most people in its emperor mode since it doesn’t have the ability to switch into a virtualenv. For gunicorn i opt to install it inside the virtualenv with pip and run it with supervisord.

  • https://www.smartfile.com Travis Cunningham

    We tried waitress in production recently but I don’t think it is stable enough to be a general purpose wsgi server. It creates a lot of temp files for handling requests and we were constantly running out of disk space.

  • Francesco Leacche

    Instagram has switched to uWSGI afterall. http://reinout.vanrees.org/weblog/2016/11/04/instagram.html