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.


alexjj said...

I'm guessing pycharm is too big/java for raspberrypi?

Matt Woodward said...

In my case I wasn't using the Raspberry Pi as something I'd do development on so I didn't try putting PyCharm on there. My guess is it wouldn't work all that well but it'd be interesting to try.

Java webapps will run fine on the Pi I'd think -- can't imagine running Tomcat or Jetty would be too taxing, depending on what the Java apps are doing of course. I've read several blog posts where people are doing that.

milhouse31 said...

Looks like the nginx is missing something

"Put the following in the foo file: CONTENTS OF /etc/nginx/sites-available/foo" doesn't seem right ;)

Matt Woodward said...

D'OH! That was a placeholder and I didn't replace it with the contents of that file. I'll update that in a bit and comment again when it's up there.

Thanks for letting me know.

Matt Woodward said...

OK, got that added. Barring any typos it should get you going!

milhouse31 said...

A semicolon is missing on the proxy_pass line.

Thanks for the tutorial

Matt Woodward said...

Thanks -- fixed.

David said...

Tanks for this great tutorial!

Had some real issues with the nginx conf after I copy-pasted your code into pico(don't know how to use vim yet) but those issues were fixed by manually typing in everything.

curl www.foo.com works great. Will move on to setting up a "real" site now that I have been developing in Aptana on my workstation.

Messages like these popped up after a "sudo service nginx restart":
Restarting nginx: nginx: [emerg] unknown directive " " in /etc/nginx/sites-enabled/foo:18

Restarting nginx: nginx: [emerg] unknown directive "location @gunicorn" in /etc/nginx/sites-enabled/foo:18

Restarting nginx: nginx: [emerg] invalid number of arguments in "try_files" directive in /etc/nginx/sites-enabled/foo:16

Matt Woodward said...

Glad you found it helpful! My guess is copy/paste didn't work due to encoding and line termination issues, so I'm glad that typing everything worked for you.

In the future I'll be sure and put all the code samples in a Gist on GitHub so they can successfully be copied/pasted.

Matt said...

Must I first configure a static IP address on the RPi before running as a web server?

Matt Woodward said...

Hi Matt -- you don't HAVE to set a static IP to get it working but it certainly makes life easier.

NickOS - Nick Harvey said...

Awesome. Thanks Matt

After realizing that run_gunicorn -w 4 had to be on the same line as the 'command' in the supervisor conf file and bounced supervisor(d|ctl) all over the place, it's all working!

And as a Linux noob, that's quite a compliment!!

Matt Woodward said...

Excellent. Glad it's working for you.

Ben Macadangdang said...

This tutorial was great! Thanks for the awesome instructions.

Peter Brookes-Smith said...

Hi Matt. Have you tried putting openbd or another coldfusion server on a pi?



Peter Brookes-Smith said...

Hi Matt. Have you tried putting openbd or another coldfusion server on a pi?



Matt Woodward said...

I haven't since I don't do CFML anymore but it'd probably run OK. You'd just have to be aware of the limited memory and configure things accordingly.

Jake Schmitz said...

Hey Matt, I get this error thrown at me when i test supervisor after writing the gunicorn.conf file
here is the error "unix:///var/run/supervisor.sock refused connection" any idea on what I can do to fix this?

Alexis Ferreira said...

I got this to work, however, when I try to add my own pre-programmed django project I can't get the raspberry pi to run the apps nor install the database properly... Help is extremely appreciated.

Alexis Ferreira said...
This comment has been removed by the author.
Vince Rothenberg said...

Excellent tutorial! Everything's running smoothly on my Pi.

One update is that it may be necessary to add logging in the settings.py file:

'version': 1,

Felix said...


when i call "curl foo.com" i get "502 Bad Gateway" as result. Could you give me a hint?

Felix said...

python manage.py run_gunicorn
gives me !!! WARNING: This command is deprecated.

could be the reason :-)

Owen Imholte said...

@Felix, I ran into this as well, must be something with the latest versions of gunicorn and/or django.

I modified my gunicorn.conf "command" line to be:
command = /home/pi/.virtualenvs/foo/bin/gunicorn foo.wsgi:application -w 4

Do the steps to restart supervisor/gunicorn and it should start working.

Matt Woodward said...

It doesn't exist until you create it, which you do in the /etc/supervisor/conf.d directory.

luko said...

If I changed something how do I make that change happen on the page?

David said...

I guess it would help to suggest that developers take the raspberrypi.org advice of insuring that they use an 8 gig SD card instead of the readily available 4 gig SD card.

Me and my SD card got to the "virtualenvs" section before "IOError: No Space Left on device" error occurred.

Great post! I'll give it another shot when I get an 8 gig card. Thanks.