Multiple Web2py Instances with uWSGI Emperor and NginX

I figured out how to run multiple instances of web2py. In case I ever wanted to offer hosting for this service.

Here is how I did it.

Step 1: Setup up Web2py NginX uWSGI (in emperor mode) using this setup script for ubuntu:

https://raw.github.com/web2py/web2py/master/scripts/setup-web2py-nginx-uwsgi-ubuntu.sh

This will pretty much do everything you need for the main server/hostname setup. To create separate instances of web2py we will repeat the steps as follows

in /etc/nginx/sites-available you should have a file called web2py which looks close to this.

server {
        listen          80;
        server_name     $hostname;
        location ~* ^/(\w+)/static/ {
            root /home/www-data/web2py/applications/;
        }
        location / {
            uwsgi_pass      unix:///tmp/web2py.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}
server {
        listen 443 default_server ssl;
        server_name     $hostname;
        ssl_certificate         /etc/nginx/ssl/web2py.crt;
        ssl_certificate_key     /etc/nginx/ssl/web2py.key;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        keepalive_timeout    70;
        location / {
            uwsgi_pass      unix:///tmp/web2py.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}

Copy this config and paste it in the same file beneath the original config settings. Change the server name in the newly pasted config in both the SSL and http settings. Also in the newly pasted code change this line listen 443 default_server ssl; so it looks like this listen 443 ssl;

An example completed config would be.

server {
        listen          80;
        server_name     $hostname;
        location ~* ^/(\w+)/static/ {
            root /home/www-data/web2py/applications/;
        }

	location = /robots.txt  { access_log off; log_not_found off; }
	location = /favicon.ico { access_log off; log_not_found off; }
	location ~ /\.          { access_log off; log_not_found off; deny all; }
	location ~ ~$           { access_log off; log_not_found off; deny all; }

        location / {
            uwsgi_pass      unix:///tmp/web2py.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}
server {
        listen 443 default_server ssl;
        server_name     $hostname;
        ssl_certificate         /etc/nginx/ssl/web2py.crt;
        ssl_certificate_key     /etc/nginx/ssl/web2py.key;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        keepalive_timeout    70;
        location / {
            uwsgi_pass      unix:///tmp/web2py.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}

################# END MAIN SERVER ##################

server {
        listen          80;
        server_name     worldlinkstudio.com www.worldlinkstudio.com;

        location ~* ^/(\w+)/static/ {
            root /home/www-data/worldlinkstudio/applications/;
        }

        location / {
            uwsgi_pass      unix:///tmp/worldlinkstudio.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}

server {
        listen 443 ssl;
        server_name     worldlinkstudio.com www.worldlinkstudio.com;
        ssl_certificate         /etc/nginx/ssl/worldlinkstudio.crt;
        ssl_certificate_key     /etc/nginx/ssl/worldlinkstudio.key;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        keepalive_timeout    70;
        location / {
            uwsgi_pass      unix:///tmp/worldlinkstudio.socket;
            include         uwsgi_params;
            uwsgi_param     UWSGI_SCHEME $scheme;
            uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}

Don’t forget to change the names of sockets and the ssl cert path. We will generate the ssl certs for each of our domains to be able to access web2py admin.

Ok since we are on to ssl lets cd in to /etc/nginx/ssl and generate the SSL certs for our domain.

We will do so like this

openssl genrsa 1024 > worldlinkstudio.key
chmod 400 worldlinkstudio.key
openssl req -new -x509 -nodes -sha1 -days 1780 -key worldlinkstudio.key > worldlinkstudio.crt
openssl x509 -noout -fingerprint -text < worldlinkstudio.crt > worldlinkstudio.info

The nginx part is done for now.

We will now cd to /home/www-data/ and copy the downloaded and unpacked web2py folder. by using this command

cp -R web2py worldlinkstudio

Change Directories to /etc/uwsgi you should have a file called web2py.ini copy this file using the following

cp web2py.ini wordlinkstudio.ini

now edit the worldlinkstudio.ini file vim worldlinkstudio.ini and change the settings to match what you have created.

For example:

[uwsgi]
socket = /tmp/worldlinkstudio.socket
pythonpath = /home/www-data/worldlinkstudio/
mount = /=wsgihandler:application
processes = 4
master = true
harakiri = 60
reload-mercy = 8
cpu-affinity = 3
stats = /tmp/stats-wordlinkstudio.socket
max-requests = 2000
limit-as = 512
reload-on-as = 256
reload-on-rss = 192
uid = www-data
gid = www-data
cron = 0 0 -1 -1 -1 python /home/www-data/worldlinkstudio/web2py.py -Q -S welcome -M -R scripts/sessions2trash.py -A -o
no-orphans = true

now change back to /home/www-data/worldlinkstudio and create a file called routes.py and add the following.

routers = dict(
    # base router
    BASE = dict(
        default_application = 'welcome',
        domains = {
            'worldlinkstudio.com' : 'welcome',
            'www.worldlinkstudio.com' : 'welcome',
            },
        applications = ['welcome', 'admin'],
        controllers = 'DEFAULT'
        ),
    )

In that same directory execute the following:

sudo -u www-data python -c "from gluon.main import save_password; save_password('yourpassword',443)"

now you will restart uwsgi and nginx using.

restart uwsgi-emperor
service nginx restart

and debug as necessary. After you have solved any problems you should be able to see

https://www.yourdomain.com/admin

Enter your password you saved in the last step and create a new project for your domain using the admin interface (we will change routes.py to enable the project for the domain). In this case we created the web2py project/application myworldlinkstudio

In SSH change to /home/www-data/worldlinkstudio/ and edit the routes.py file vim routes.py

change the file to look like this.

routers = dict(
    # base router
    BASE = dict(
        default_application = 'welcome',
        domains = {
            'worldlinkstudio.com' : 'myworldlinkstudio',
            'www.worldlinkstudio.com' : 'myworldlinkstudio',
            },
        applications = ['myworldlinkstudio','admin'],
        controllers = 'DEFAULT'
        ),
    )

restart uwsgi

restart uwsgi-emperor

And you can see the wonders of web2py hosting!