Tuesday, January 22, 2013

Setting Up Django On a Raspberry Pi

This past weekend I finally got a chance to set up one of my two Raspberry Pis to use as a Django server so I thought I'd share the steps I went through both to save someone else attempting to do this some time as well as get any feedback in case there are different/better ways to do any of this.

I'm running this from my house (URL forthcoming once I get the real Django app finalized and put on the Raspberry Pi) using dyndns.org. I don't cover that aspect of things in this post but I'm happy to write that up as well if people are interested.

General Comments and Assumptions

Reconfigure the Keyboard Mapping

If you already did this at some other point with your Raspberry Pi or your keyboard is working properly you can skip this step. After the base install of Raspbian “wheezy” in my case I noticed when in vim I couldn’t type things like #, |, and @ so I had to do the following:
  1. sudo dpkg-reconfigure keyboard-configuration
  2. Follow the prompts to set things appropriate for your keyboard (US English in my case)
  3. Restart the Raspberry Pi (sudo reboot)

Install Required Server-Wide Tools and Libraries

  1. sudo apt-get install vim python-dev python-setuptools nginx supervisor
  2. sudo easy_install pip
  3. sudo pip install virtualenv virtualenvwrapper
  4. Configure virtualenvwrapper
    1. sudo vim /etc/bash.bashrc
    2. Add this line at the bottom of the file:
      source /usr/local/bin/virtualenvwrapper.sh
    3. Save the file and exit vim
    4. Log out and log back in

Create and Configure a virtualenv for Your Application

tl;dr version of the following paragraph: Run the following commands from your home directory unless you have a reason not to and know what you’re doing. For the purposes of this tutorial I’m using the default ‘pi’ user on the Raspberry Pi.

You can create a virtualenv for your application in your home directory or wherever you prefer your files to be located, just be aware that depending on where you put your files and whether or not you have to execute virtualenv-specific tasks (pip install, etc.) you may have to explicitly specify which python or pip command to use for it to work properly.

For the purposes of this example we’ll create a ‘foo’ application. The steps below where you’re running django-admin.py startproject and firing up the development server are simply to test to make sure everything’s working properly. If you’re doing this all for the first time, I’d suggest doing this both to get familiar with it as well as to double-check that everything with the environment is working as expected.

  1. mkvirtualenv foo
    1. Note that this will activate the virtualenv. You MUST have the virtualenv activated when performing the steps that follow for everything to work properly.
    2. If you’re used to using virtualenv directly as opposed to virtualenvwrapper, note that when you create a virtualenv using  mkvirtualenv, it will put the virtualenv files (again, if you’re doing this from your home directory) in ~/.virtualenvs/foo
  2. pip install django docutils south gunicorn
  3. django-admin.py startproject foo
  4. cd foo
  5. python manage.py runserver
    1. The first time you run this you may see a ton of blank lines output on the Raspberry Pi. It’ll eventually finish, or if you hit Ctrl-C when this happens and try again a couple of times it’ll eventually calm down and the next time you run things this won’t happen. I’m not sure what’s going on here since the Raspberry Pi is the only platform on which I’ve seen this happen.
    2. Once the development server starts up you should see “0 errors found [other stuff here] Development server is running at” If you don’t see that, double-check everything and try again.
    3. You can make a call to the development server by sshing into the Raspberry Pi from another machine and doing curl localhost:8000

Enable Gunicorn and South and Enable a Database in Your Application

  1. cd ~/foo/foo
  2. vim settings.py
  3. In the DATABASES section make the following changes:
    1. ‘ENGINE’: ‘django.db.backends.sqlite3’
    2. ‘NAME’: ‘/home/pi/foo/foo.db’
  4. In the INSTALLED_APPS section, enable the admin and admindocs (optional):
    1. Uncomment # ‘django.contrib.admin’
    2. Uncomment # ‘django/contribu.admindocs’
  5. Also in the INSTALLED_APPS section, add ‘south’ and ‘gunicorn’ to the list of installed apps.
  6. Save the file and exit vim
  7. Test Gunicorn:
    1. python manage.py run_gunicorn (you can hit Ctrl-C to kill it once you see it’s working)

A note on databases: I’m not addressing the broader topic of which database server you should use simply because it’s a separate discussion that’s potentially fraught with zealous opinions and general peril, and is a bit tangential to the present discussion.

If you’re doing all this just to learn, or for your own personal low-use “screwing around” type applications, SQLite (http://www.sqlite.org/), which is what comes with Django, will work just fine, so that’s what I outlined above.

I’m writing this up as part of setting up a site for alt.* Radio (http://codebassradio.net/shows/alt/), which is a weekly show I do on the super-awesome CodeBass Radio (http://codebassradio.net), so for that I’ll be running PostgreSQL on a separate machine. For low-traffic stuff you might even be able to get away with running MySQL or Postgres directly on the Raspberry Pi but I have a couple of unused machines laying around so I figured I might as well keep the Raspberry Pi focused on the Django side of things.

Note that if you DO want to use PostgreSQL, you’ll have to do this on the Raspberry Pi:

  1. sudo apt-get install libpq-dev
  2. pip install psycopg2 (with your virtualenv active)

Configure Supervisor

Supervisor (http://supervisord.org/), which we installed earlier, is a tool that lets you easily create startup/restart scripts for services that aren’t installed and managed through apt-get. In our case we’ll need this for Gunicorn so it’ll fire up when the machine boots and automatically restart if it crashes.

  1. cd /etc/supervisor/conf.d
  2. sudo vim gunicorn.conf
  3. Put the following in the new file:
    command = /home/pi/.virtualenvs/foo/bin/python /home/pi/foo/manage.py run_gunicorn -w 4
    directory = /home/pi/foo
    user = pi
    autostart = true
    autorestart = true
    stdout_logfile = /var/log/supervisor/gunicorn.log
    stderr_logfile = /var/log/supervisor/gunicorn_err.log
  4. Save the file and exit vim
  5. sudo service supervisor restart
  6. sudo supervisorctl start gunicorn
    1. Note that you may get an “already started” error here. If you get a “no such process” error, that means supervisor didn’t load the new configuration file. If that happens:
      1. sudo ps -wef | grep supervisor
      2. sudo kill -9 SUPERVISOR_PROCESS_ID (use the real process ID)
      3. sudo service supervisor start

Configure Nginx

  1. sudo rm -f /etc/nginx/sites-enabled/default
  2. sudo vim /etc/nginx/sites-available/foo
  3. Put the following in the foo file:

# upstream server for gunicorn
upstream gunicorn {
  server localhost:8000;

# nginx server for the host
server {
  listen 80;

  server_name foo.com www.foo.com;

  root /home/pi/foo;

  access_log /var/log/nginx/foo_access.log;
  error_log /var/log/nginx/foo_error.log;

  # try to serve a static file and if it doesn't exist, pass to gunicorn
  try_files $uri @gunicorn;

  # rules for gunicorn
  location @gunicorn {
    proxy_pass http://gunicorn;
    proxy_redirect off;
    proxy_read_timeout 5m;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Fowarded-For $proxy_add_x_forwarded_for;

  1. Save the file and exit vim
  2. sudo ln -s /etc/nginx/sites-available/foo /etc/nginx/sites-enabled/foo
  3. sudo service nginx restart

Verify Everything’s Working

If you’re not using a real hostname/URL, make sure and add it to /etc/hosts on the Raspberry Pi so you can easily test. In this case we used foo.com as the hostname, so do the following:
  1. sudo vim /etc/hosts
  2. Add this line: foo.com
  3. Save the file and exit vim

If you want to hit the Raspberry Pi from a browser on another machine you’ll have to also add the IP and host names to /etc/hosts on any machines from which you want to hit that host, but use the IP of the Raspberry Pi instead of

With that modification in /etc/hosts locally on the Raspberry Pi, you can do a curl foo.com to see if you get the default Django page (or your application if you took things a bit further than are outlined in this tutorial). If you do, everything’s working! If not, start over and don’t screw up this time!

Seriously though if you need any help just comment here or email/Google+/Twitter me and I’ll be happy to assist.