The software that powers this site is now available for beta testing at my new site, Pyplate. Check out the installation instructions, and build your own Raspberry Pi powered site.

Follow me on Twitter and Google+


submit to reddit       

Setting up Nginx and uWSGI for CGI scripting

Nginx is an open source web server that's designed to handle heavy traffic efficiently and quickly, and it has a reputation for serving static content like images and HTML files much faster than Apache. Unlike Apache, Nginx doesn't have built in support for executing CGI scripts, so a helper application like FastCGI or uWSGI is needed to handle dynamic content.

I'm using Nginx with uWSGI. There are many different modes and plugins that you can use with uWSGI. I'm using the CGI plugin.

The first thing I needed to do was install Nginx:

sudo apt-get install nginx

I wanted to duplicate the set up that I have with Apache. I have a numerous scripts in /usr/lib/cgi-bin, and the web directory is /var/www, so I edited /etc/nginx/sites-enabled/default and set the root directive to /var/www.

I created two locations which Nginx can use to serve requests from. Locations are directories in the Pi's file system which contain files that can be served by the web server. The Nginx configuration files can be used to define locations and define rules about how requests for URLs in those locations should be handled.

The first location is the 'root' location for files in /var/www. This is the default location where static files are stored. In this location I used the try_files directive to tell Nginx to try sending a file if the URL matches a file name. If the requested URL is a directory rather than a file, the try directive will see if that directory contains a file called index.html. If there is a file called index.html in the directory, it will be sent to the user. If the requested URL wasn't a file, and it wasn't a directory, the try_directive will fall back on redirecting the request to pyindex.py, the main script of the CMS that powers my site. Instead of using this script, you can just replace /cgi-bin/pyindex.py?q=$uri with index.html.

The second location was for /cgi-bin. If a user requests a URL that references a file in /usr/lib/cgi-bin, then a CGI script needs to be executed. Nginx doesn't handle CGI, so the request has to be passed to the uWSGI daemon, which is accessible via the loopback address. In the uWSGI settings this location is mapped on to /usr/lib/cgi-bin - see below. Setting uwsgi_modifier1 to 9 tells uWSGI to handle requests for this location as CGI scripts.

This is the complete configuration file in /etc/nginx/sites-enabled/:

server { #listen 80; ## listen for ipv4; this line is default and implied #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 root /var/www; index index.html; # Make site accessible from http://localhost/ server_name localhost; location / { index index.html; # First attempt to serve request as file, then # as directory, then fall back to the CMS. try_files $uri $uri/index.html /cgi-bin/pyindex.py?q=$uri; } location /cgi-bin { include uwsgi_params; uwsgi_modifier1 9; uwsgi_pass; } location /doc/ { alias /usr/share/doc/; autoindex on; allow; allow ::1; deny all; } }

Nginx doesn't use multiple threads to serve requests in the same way that Apache does. It's recommended that you configure Nginx to use one worker process per CPU core, so just one worker when running on a Raspberry Pi. In /etc/nginx/nginx.conf, I set worker_processes to 1.

After making these configuration changes, it's necessary to restart Nginx:

sudo service nginx restart

The next step was to install uWSGI. The version of uWSGI in the Raspbian repository is out of date, and didn't seem to work properly. Instead of installing it with apt-get, use this command:

curl http://uwsgi.it/install | bash -s cgi /home/pi/uwsgi

This downloads and builds the code with the CGI plugin, and places the uwsgi binary in /home/pi/uwsgi. The uwsgi file doesn't have to be in your home directory, you can put it somewhere else if you prefer. Building the code takes a little while, so be patient.

A configuration file is needed to tell the uWSGI daemon how to handle requests. I saved the following as /home/pi/uwsgi_config.ini:

[uwsgi] plugins = cgi socket = chdir = /usr/lib/cgi-bin/ module = pyindex cgi=/cgi-bin=/usr/lib/cgi-bin/ cgi-helper =.py=python

This configuration file tells uWSGI to use the CGI plugin, listen on, and change the working directory to /usr/lib/cgi-bin/. It also specifies that the location /cgi-bin is mapped onto /usr/lib/cgi-bin/, and that uWSGI should use Python to execute .py files.

The next step is to start the uWSGI process, running it as user www-data:

sudo -u www-data ./uwsgi ./uwsgi_config.ini

The next step is to set up a simple test script to make sure everything's working. I saved this code in /usr/lib/cgi-bin/hello.py:

#!/usr/bin/env python print "Content-type: text/html\n\n" print "<h1>Hello World</h1>"

I made the file executable with this command:

sudo chmod +x /usr/lib/cgi-bin/hello.py

Now when I go to in my browser, I'm greeted with this message:

Hello World


comments powered by Disqus