Deployment of Multiple Web.py Applications using uWSGI and Nginx on Ubuntu

The scenario here is that we want to deploy multiple web.py applications under the same domain (‘apps.example.com’ for instance). These applications are independent from each other and reside in separate paths. There are quite a number of ways of achieving this using Nginx and uWSGI. What is demonstrated here is a method that uses the VirtualHosting mode and DynamicApps feature of uWSGI to achieve miminal and once for all setup.

We will skip the installation part (see the previous post for detailed instructions) and dive right into configuration. For this demonstration, we use modified versions of the minimalist ‘Hello World’ example from web.py tutorial as our web applications. Save the following as ‘/var/www/apps.example.com/hello/index.py’:

import web

urls = (
  '/hello.*', 'index'
)

app = web.application(urls, globals())

class index:
        def GET(self):
                return "Hello, world!"

if __name__ == "__main__": app.run()

application = app.wsgifunc()

Save the following as ‘/var/www/apps.example.com/bye/index.py’:

import web

urls = (
  '/bye.*', 'index'
)

app = web.application(urls, globals())

class index:
        def GET(self):
                return "Bye, world!"

if __name__ == "__main__": app.run()

application = app.wsgifunc()

Now that we have the ‘hello’ and ‘bye’ applications, we set up a single uWSGI configuration for both of them, as well as any future applications we might deploy under the same domain. We will be using uWSGI in VirtualHosting mode, so that more applications can be added later dynamically without having to change uWSGI configuration. Create ‘/var/www/apps.example.com/uwsgi.ini’ with the following contents:

[uwsgi]
gid = www-data
uid = www-data
vhost = true
logdate

#Using named socket file makes it a bit easier to match Nginx and uWSGI configurations
socket = /tmp/apps.example.com.sock
#socket = 127.0.0.1:3031

master = true
processes = 1
harakiri = 20
limit-as = 128
memory-report
no-orphans

#This avoids mistakenly serving an app not intended for the request
no-default-app

We then symlink this configuration file so that it could be picked up by uWSGI service:

ln -sf /var/www/apps.example.com/uwsgi.ini /etc/uwsgi-python/apps-enabled/apps.example.com.ini

You can see there is nothing special about the uWSGI configuration, but next comes the Nginx setup, wherein the real ‘magic’ lies. Create ‘/var/www/apps.example.com/nginx.conf’ with the following contents (pay attention to the comments):

server {
  # Change this if you want to serve your application on another port
  listen 80;

  # Replace this with your domain name
  server_name apps.example.com;

  # We will be using this a lot, so better save it as variable
  set $dir /var/www/$server_name;

  # Serve static files from /var/www/apps.example.com/static/
  location ^~ /static/ {
    access_log off;

    # Prevent hotlinking
    valid_referers none blocked server_names;
    if ($invalid_referer) { return 444; }

    alias $dir/static/;
    expires 7d;
  }

  # The location directive for all applications
  # This will match any non-empty uri and save the highest level path in $1
  location ~ ^/([^/]+) {

    # All of our applications are located in respective folders ($1)
    # Redirect requests for non-existent applications
    if (!-d $dir/$1) { rewrite ^ / redirect; }

    # Load default uwsgi parameters
    include uwsgi_params;

    # This must match uWSGI configuration
    uwsgi_pass unix:/tmp/$server_name.sock;

    # This is where application code resides
    uwsgi_param UWSGI_CHDIR $dir/$1;
    uwsgi_param UWSGI_PYHOME $dir/$1;

    # All of our applications define 'application' in index.py
    uwsgi_param UWSGI_SCRIPT index;

    # This is used to isolate different applications served by a single uWSGI instance
    uwsgi_param SERVER_NAME $server_name.$1;
  }

  # Requests for non-existent applications will be redirected here
  # Best place to set up gateway to all applications
  location / {
    # Make sure correct MIME type is served
    default_type text/plain;
    echo 'Looking for something?';
  }
}

Note that by using the powerful variable feature of Nginx, we have abstracted all references to the folders containing our application code and any subsequent applications we added under /var/www/apps.example.com/ will be automatically picked up by Nginx and forwarded to uWSGI with appropriate parameters for the latter to locate and serve the application. In addition, the server configuration could be easily adapted for any other domain by changing only the ‘server_name’ definition (and if really necessary, ‘/var/www’ location in $dir). We also symlink this configuration file so that it could be picked up by Nginx service:

ln -sf /var/www/apps.example.com/nginx.conf /etc/nginx/sites-enabled/apps.example.com

Now that everything is in place, we can (re)start or (re)load Nginx and uWSGI:

service nginx restart
service uwsgi-python restart

And we are all set. Try browsing ‘http://apps.example.com/hello’ and ‘http://apps.example.com/bye’, as well as any other uri you might want to test. Remember that deployment of future applications is as simple as putting your code under ‘/var/www/apps.example.com/’ using desired application uri as folder name. No need to change anything or restart/reload anything, at all.

This entry was posted in Web and tagged , , , , . Bookmark the permalink.

2 Responses to Deployment of Multiple Web.py Applications using uWSGI and Nginx on Ubuntu

  1. Joao Kho says:

    This does not work:
    ” location ~ ^/([^/]+) ”
    error on nginx configurations