How to Run Tornado in WSGI Mode on DotCloud,

published at 1:06am on 06/13/11

Note: Running Tornado on DotCloud requires Tornado 2.0, which includes a bug fix to address an issue where POST requests never return and result in a 504 gateway timeout from DotCloud’s proxy servers.

So, let’s just say that you’re building a new app to deploy to your fancy new dotcloud account, and you’ve decided that, since you know it the best, you are going to build a Tornado app. Well, if you do that, you will quickly learn that this is probably not the best possible choice for you, as Tornado, and all of its async goodness is not going to be available to you yet.

So what to do? Well, let’s say you have your heart set on using Tornado. That’s cool, you’re just going to have to be comfortable running your app as a synchronous WSGI application, and make sure you haven’t used any of those async methods anywhere else in your code. Assuming the best, the first thing is to rename your main app to wsgi.py so dotcloud knows where to find it. Once that’s done, you’re going to want to do is to convert that tornado.web.Application into a tornado.wsgi.WSGIApplication:

application = tornado.web.Application([
	(r"/", MainHandler),
])

becomes

import tornado.wsgi
import wsgiref.handlers

application = tornado.wsgi.WSGIApplication([
	(r"/", MainHandler),
])

OK, so that’s cool, and you fire up your application in dotcloud, and you find that you keep getting a 404 thrown back at you from nginx. Well that’s not cool at all, and you start to poke around a bit more, and ultimately, the nice folks at dotcloud tell you that sometimes, WSGI applications just don’t want to know about the SCRIPT_NAME variable in the WSGI params. So let’s go ahead and strip those out.

tornadoapp = tornado.wsgi.WSGIApplication([
	(r"/", MainHandler),
 ])
 
def application(environ, start_response):
	if 'SCRIPT_NAME' in environ:
		del environ['SCRIPT_NAME']

	return tornadoapp(environ, start_response)

So what did we do here? Well, the WSGI handler that dotcloud uses (uWSGI) assumes that there is going to be a variable named “application,” so we define our own “application” that strips out the offending SCRIPT_NAME variable from the environment, then calls down the Tornado WSGI interface and returns it.

And really, that’s all you need to do. Now, it may seem silly to do all of this work when something like web2py would do the trick nicely, but I can think of two reasons not to do that:

First, you already know Tornado, and don’t want to learn something new, which is especially important when you’re trying to just get a Minimum Viable Product out the door.

And second, a fully-supported Tornado has to be coming somewhere down the line, and this way, you’ll be (almost) all ready to redeploy your app as an actual non-blocking Tornado app with minimal changes when the time comes.

Filed under: Technology

At 3:45 am on 06.14.11, Solomon Hykes said,

Hi Jesse, thanks for the detailed writeup!

Just wanted to confirm that, yes, we are working on official support for Tornado, and much more :)

Thanks for using dotCloud!

Solomon

At 12:26 am on 06.21.11, exavolt said,

Hi there.

Have you tried to handle POST request with Tornado on dotCloud?

I always get 504 for any POST request.

At 12:13 pm on 06.22.11, jcn said,

exavolt, thanks for pointing this out. This is actually a bug that has been fixed in Tornado 2.0. I’ve updated my post to reflect this requirement. If you already have an app deployed, just update the requirements.txt file to include the line:

tornado==2.0

This will force Tornado 2.0 to be installed during the push. Just make sure you read the 2.0 release notes as there were some changes that could break your app if you’re not aware of them.

Leave a Reply: