Deploying web2py with NGINX and uWSGI

Install web2py

I like to install web2py under /var/www, but it can be installed anywhere you like.

Run following commands to download and setup web2py:

mkdir /var/www
cd /var/www
wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip && rm -rf web2py_src.zip
curl --output /var/www/web2py/scripts/sessions2trash.py http://web2py.googlecode.com/hg/scripts/sessions2trash.py
chown -R nginx:nginx web2py
cp -p web2py/handlers/wsgihandler.py web2py/    # need this for nginx + uwsgi

Setting up the admin password:

cd /var/www/web2py
sudo -u nginx python -c "from gluon.main import save_password; save_password(raw_input('admin password: '),443)"

Development server can be started like so:

python web2py.py -a '<ask>' -i 127.0.0.1 -p 8080

But this is for development only.

For production we need to deploy web2py behind Apache, Nginx, Lighttpd or Cherokee.

In this setup we’ll be using Nginx. So let’s do it!


Install NGINX

To install NGINX under CentOS we must first add the yum repository for it.

create a file /etc/yum.repos.d/nginx.repo

vi /etc/yum.repos.d/nginx.repo

with following configuration:

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

Now we can install nginx with yum like so:

yum install nginx
chkconfig nginx on
service nginx start

Configuration files

  • Default SSL and vhost config directory: /etc/nginx/conf.d/
  • Default document root: /usr/share/nginx/html
  • Default configuration file: /etc/nginx/nginx.conf
  • Default access log: /var/log/nginx/access.log
  • Default error log: /var/log/nginx/error.log

Update file /etc/nginx/nginx.conf and change worker_processes to match your system CPU(s).

Run following command to list the number of CPU:

grep processor /proc/cpuinfo | wc -l

Update this to match the output of above command:

worker_processes  <number_of_cpu>;

Save the config file and reload Nginx:

/etc/init.d/nginx reload

Configure web2py with NGINX

First create following two directories:

mkdir /etc/nginx/sites-available
mkdir /etc/nginx/sites-enabled

Now create the config file

vi /etc/nginx/sites-available/web2py.conf

with following configuration:

server {
    listen          ip_address:80;
    server_name     $HOSTNAME;
    location ~* /(\w+)/static/ {
        root /var/www/web2py/applications/;
        #remove next comment on production
        #expires max;
    }
    location / {
        uwsgi_pass      unix:///var/www/web2py/logs/web2py.socket;
        include         /etc/nginx/uwsgi_params;
        uwsgi_param     UWSGI_SCHEME $scheme;
        uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
    }
}
server {
    listen ip_address:443 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 SSLv3 TLSv1;
    keepalive_timeout    70;
    location / {
        root /var/www/web2py/applications/;
        uwsgi_pass      unix:///var/www/web2py/logs/web2py.socket;
        include         /etc/nginx/uwsgi_params;
        uwsgi_param     UWSGI_SCHEME $scheme;
        uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
    }
}

Link the virtual host to /etc/nginx/sites-enabled:

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/web2py.conf

Reload NGINX:

/etc/init.d/nginx reload

Install uWSGI

Run this command to install uwsgi:

pip install uwsgi

Once uWSGI successfully installed, you will see the following at the end.

    ################# uWSGI configuration #################
 
    pcre = True
    kernel = Linux
    malloc = libc
    execinfo = False
    ifaddrs = True
    ssl = True
    matheval = False
    zlib = True
    locking = pthread_mutex
    plugin_dir = .
    timer = timerfd
    yaml = True
    json = False
    filemonitor = inotify
    routing = True
    debug = False
    zeromq = False
    capabilities = False
    xml = libxml2
    event = epoll
 
    ############## end of uWSGI configuration #############
    *** uWSGI is ready, launch it with /usr/bin/uwsgi ***
Successfully installed uwsgi
Cleaning up...

We need to create a user to run uwsgi:

useradd uwsgi

Now create a log file and assign ownership to this new user. Note that we need to empty this log file from time to time.

touch /var/log/uwsgi.log
chown uwsgi /var/log/uwsgi.log

Configure web2py with uWSGI

Create the config file /etc/uwsgi/web2py.ini

mkdir /etc/uwsgi
vi /etc/uwsgi/web2py.ini

with the following configuration:

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

Run uWSGI as a service

Create file /etc/init.d/uwsgi and add this code:

#!/bin/bash
 
### BEGIN INIT INFO
# Provides:          uwsgi
# Required-Start:    $syslog $remote_fs
# Should-Start:      $time ypbind smtp
# Required-Stop:     $syslog $remote_fs
# Should-Stop:       ypbind smtp
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
### END INIT INFO
 
# Source function library.
. /etc/rc.d/init.d/functions
 
# Check for missing binaries (stale symlinks should not happen)
UWSGI_BIN=`which uwsgi`
test -x $UWSGI_BIN || { echo "$UWSGI_BIN not installed"; 
        if [ "$1" = "stop" ]; then exit 0;
        else exit 5; fi; }
 
UWSGI_EMPEROR_MODE=true
UWSGI_VASSALS="/etc/uwsgi/"
UWSGI_OPTIONS="--enable-threads --logto /var/log/uwsgi.log"
lockfile=/var/lock/subsys/uwsgi
 
UWSGI_OPTIONS="$UWSGI_OPTIONS --autoload"
 
if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then
    UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS"
fi
 
case "$1" in
    start)
        echo -n "Starting uWSGI "
        daemon $UWSGI_BIN $UWSGI_OPTIONS &
        ;;
    stop)
        echo -n "Shutting down uWSGI "
        killproc $UWSGI_BIN
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    status)
        echo -n "Checking for service uWSGI "
        status $UWSGI_BIN
        ;;
    *)
        echo "Usage: $0 {start|stop|status|restart}"
        exit 1
        ;;
esac
exit 0

Make it executable and turn on the service:

chmod +x /etc/init.d/uwsgi
chkconfig uwsgi on

Now you can open the browser to access your domain or ip, and you’ll see a Welcome sample app like this.

web2py sample app

Click on the ‘Administrative Interface’ button and enter the admin password.

administration
Play around and have fun!