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 does not work:
” location ~ ^/([^/]+) ”
error on nginx configurations
This does work.