Epicserve

Ubuntu Server Setup Guide for Django Websites

November 3, 2011 | 2:02 p.m. CDT

This guide is a walk-through on how to setup Ubuntu Server for hosting Django websites. The Django stack that will be used in this guide is Ubuntu, Nginx, Gunicorn and Postgres. This stack was chosen solely from the reading I've done and talking to other Django developers in order to get their recommendations. This stack seems to be one of the latest "standard" stacks for Django deployment. This guide also assumes that you're familiar with Ubuntu server administration and Django. I needed an example site for this guide so I chose to use my Django Base Site which is available on Github.

I would also like to thank Ben Claar, Adam Fast, Jeff Triplett and Frank Wiles for their suggestions and input on this guide.

Step 1: Install Ubuntu Server

The version of Ubuntu I'm using for this guide is Ubuntu 11.10 64 bit Server. I've installed Ubuntu Server in a VirtualBox VM on my MacBook Pro which is currently running Mac OS X 10.7.2. During the installation of Ubuntu Server I answered the prompts with the following:


Language: English
Install Menu: Install Ubuntu Server
Select a language: English
Select your location: United States
Configure the Keyboard: No
Configure the keyboard: English (US)
Configure the keyboard: English (US)
Hostname: ubuntu-vm
Configure the clock: Yes
Partition disks: Guided - use entire disk and set up LVM
Partition disks: SCSI3 (0,0,0) (sda) - 21.5 GB ATA VBOX HARDDISK
Partition disks: Yes
Partition disks: Continue
Partition disks: Yes
Set up users and passwords: Brent O'Connor
Set up users and passwords: (Enter a username)
Set up users and passwords: ********
Set up users and passwords: ********
Set up users and passwords: No
Configure the package manager: <blank>
Configure taskse1: No automatic updates
Software selection: <Continue>
Install the GRUB boot loader on a hard disk: Yes
Installation complete: <Continue>

Step 2: Setup Port Forwarding

Under the settings for your VM in VirtualBox click on the "Network" tab and then click on the "Port Forwarding" button. Now click on the plus and add the following settings to setup port forwarding for web and ssh.

Name Protocol Host IP Host Port Guest IP Guest Port
SSH TCP   2222   22
Web TCP   8080   80

Step 3: Install Software

OpenSSH

Since I like to connect to my servers using SSH the first thing I install is openssh-server:


$ sudo aptitude install openssh-server

Since you setup port forwarding in step 2, you should now be able to open up your Terminal and connect to your Ubuntu Server using the following:


$ ssh localhost -p 2222

Python Header Files

The Python header files are needed in order to compile binding libraries like psycopg2.


$ sudo aptitude install python2.7-dev

PostgreSQL


$ sudo aptitude install postgresql postgresql-server-dev-9.1

Make your Ubuntu user a PostgreSQL superuser:


$ sudo su - postgres
$ createuser --superuser <your username>
$ exit

Restart PostgreSQL:


$ sudo /etc/init.d/postgresql restart

Nginx


$ sudo aptitude install nginx

Git


$ sudo aptitude install git

Step 4: Setup a Generic Deploy User

The reason we are setting up a generic deploy user is so that if you have multiple developers who are allowed to do deployments you can easily add the developer's SSH public key to the deploy user's /home/deploy/.ssh/authorized_keys file in order to allow them to do deployments.


$ sudo useradd -d /home/deploy -m -s /bin/bash deploy

Step 5: Install an Example Site

Setup a virtualenv:


$ sudo apt-get install python-setuptools
$ sudo easy_install pip
$ sudo pip install virtualenv
$ cd /usr/local/
$ sudo mkdir virtualenvs
$ sudo chown deploy:deploy virtualenvs
$ sudo su deploy
$ cd virtualenvs
$ virtualenv --no-site-packages example-site
$ exit

Note

I personally use and setup virtualenvwrapper on all my servers and local development machines so that I can use workon <virtualenv> to easily activate a virtualenv. This is why I put all my virtualenvs in /usr/local/virtualenvs.

Make a location for the example site:


$ cd /srv/
$ sudo mkdir sites
$ sudo chown deploy:deploy sites
$ sudo su deploy
$ cd sites
$ git clone git://github.com/epicserve/django-base-site.git example-site
$ cd example-site/
$ echo `pwd` > /usr/local/virtualenvs/example-site/lib/python2.7/site-packages/django_project_root.pth
$ mkdir -p static/cache
$ exit
$ sudo chown www-data:www-data /srv/sites/example-site/static/cache
$ sudo su deploy

Create the file /srv/sites/example-site/config/settings/local.py and add the following. Make sure to change the password and then save the file. I usually use a random string generator to generate a new password for each new Postgresql database and user:


from base import *

LOCAL_SETTINGS_LOADED = True

DEBUG = True

INTERNAL_IPS = ('127.0.0.1', )

ADMINS = (
    ('Your Name', 'username@example.com'),
)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'example_site',
        'USER': 'example_site',
        'PASSWORD': '<enter a new secure password>',
        'HOST': 'localhost',
    }
}

Install the sites required python packages:


$ source /usr/local/virtualenvs/example-site/bin/activate
$ cd /srv/sites/example-site/
$ pip install -r config/requirements.txt

Install Gunicorn:


$ pip install gunicorn

Install psycopg2:


$ pip install psycopg2

Create a PostgreSQL user and database for your example-site:


# exit out of the deploy user account
$ exit
$ createuser example_site -P
$ Enter password for new role: [enter the same password you used in the local.py file from above]
$ Enter it again: [enter the password again]
$ Shall the new role be a superuser? (y/n) n
$ Shall the new role be allowed to create databases? (y/n) y
$ Shall the new role be allowed to create more new roles? (y/n) n
$ createdb example_site -O example_site

Step 6: Daemonize Gunicorn using Ubuntu's Upstart

Create your Upstart configuration file:


$ sudo vi /etc/init/gunicorn_example-site.conf

Add the following and save the file:


description "upstart configuration for gunicorn example-site"

start on net-device-up
stop on shutdown

respawn

exec /usr/local/virtualenvs/example-site/bin/gunicorn_django -u www-data -c /srv/sites/example-site/config/gunicorn/example-site.py /srv/sites/example-site/config/settings/__init__.py

Start the gunicorn site:


$ sudo start gunicorn_example-site

Step 7: Setup Nginx to proxy to your new example site

Create a new file sudo vi /etc/nginx/sites-available/example-site.conf and add the following to the contents of the file:


server {

    listen       80;
    server_name  localhost;
    access_log   /var/log/nginx/example-site.access.log;
    error_log    /var/log/nginx/example-site.error.log;

    location = /biconcave {
        return  404;
    }

    location  /static/ {
        root  /srv/sites/example-site/;
    }

    location  /media/ {
        root  /srv/sites/example-site/;
    }


    location  / {
        proxy_pass            http://127.0.0.1:8001/;
        proxy_redirect        off;
        proxy_set_header      Host             $host;
        proxy_set_header      X-Real-IP        $remote_addr;
        proxy_set_header      X-Forwarded-For  $proxy_add_x_forwarded_for;
        client_max_body_size  10m;
    }

}

Enable the new site:


$ cd /etc/nginx/sites-enabled
$ sudo rm default
$ sudo ln -s ../sites-available/example-site.conf

Start nginx:


$ sudo /etc/init.d/nginx start

Step 8: Test the new example site

While still connected to your Ubuntu server via SSH run the following, which should spit out the HTML for your site:


$ wget -qO- 127.0.0.1:80

Since you setup port forwarding in step 2 for web, you should also be able to open up your browser on your local host machine and pull up the website using the URL, http://127.0.0.1:8080.

Related tags: Django, Gunicorn, Nginx, postgresql, ubuntu

Comments

John Hallquist
1.   At 5:30 p.m. CDT on Aug. 31, 2012, John Hallquist wrote:

I'm sort of confused, I thought Django was supposed to run as a website? Don't you need a web server like Apache in order to process requests?

Brent O'Connor
2.   At 11:52 a.m. CDT on Sept. 1, 2012, Brent O'Connor wrote:

Nginx is your web server for serving static files and Gunicorn is your web server for serving your Django application.

Manuel García
3.   At 6:06 a.m. CST on Nov. 29, 2012, Manuel García wrote:

I followed this tutorial, but at the end nginx shows an 502 bad gateway error. How can i solve this? I tried twice the same terminal commands.

Thanks in advance

Brent O'Connor
4.   At 10:36 a.m. CST on Nov. 29, 2012, Brent O'Connor wrote:

Manuel, did you look in /var/log/nginx at the nginx error logs? It should tell you what's going on.

Manuel García
5.   At 12:57 p.m. CST on Nov. 29, 2012, Manuel García wrote:

Hi Brent!

This is the content of my /var/log/nginx/example-site.error.log

$ cat /var/log/nginx/example-site.error.log 2012/11/29 13:04:25 [error] 15227#0: 1 connect() failed (111: Connection refused) while connecting to upstream, client: 10.0.2.2, server: localhost, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8001/", host: "127.0.0.1:8080" 2012/11/29 13:04:26 [error] 15227#0: 1 connect() failed (111: Connection refused) while connecting to upstream, client: 10.0.2.2, server: localhost, request: "GET /favicon.ico HTTP/1.1", upstream: "http://127.0.0.1:8001/favicon.ico", host: "127.0.0.1:8080" 2012/11/29 13:04:32 [error] 15227#0: *5 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8001/", host: "127.0.0.1"

Manuel García
6.   At 12:59 p.m. CST on Nov. 29, 2012, Manuel García wrote:

Seems that listening to the port 8001 is failing. Don't you think?

Brent O'Connor
7.   At 1:10 p.m. CST on Nov. 29, 2012, Brent O'Connor wrote:

Is gunicorn running? Do you see the gunicorn process if you run, "ps aux | grep gunicorn"? If it isn't running, try running it from the terminal to see what's going on with something like this, "sudo /usr/local/virtualenvs/example-site/bin/gunicorn_django -u www-data -c /srv/sites/example-site/config/gunicorn/example-site.py /srv/sites/example-site/config/settings/init.py".

Manuel García
8.   At 5:28 a.m. CST on Nov. 30, 2012, Manuel García wrote:

Yes, its already running.

Here you have the "ps aux | grep gunicorn":

root 558 0.2 2.2 51184 11340 ? Ss 12:24 0:00 /usr/local/virtualenvs/example-site/bin/python /usr/local/virtualenvs/example-site/bin/gunicor_django -u www-data -c /srv/sites/example-site/config/gunicorn/example-site.py /srv/sites/example-site/config/settings/init.py www-data 719 0.2 4.5 132376 22684 ? S 12:24 0:00 /usr/local/virtualenvs/example-site/bin/python /usr/local/virtualenvs/example-site/bin/gunicor_django -u www-data -c /srv/sites/example-site/config/gunicorn/example-site.py /srv/sites/example-site/config/settings/init.py manolo 1496 0.0 0.1 11912 924 pts/0 S+ 12:27 0:00 grep --color=auto gunicorn

Manuel García
9.   At 5:29 a.m. CST on Nov. 30, 2012, Manuel García wrote:

If I run the command you posted I got this:

2012-11-30 12:28:30 [1499] [INFO] Starting gunicorn 0.16.1 2012-11-30 12:28:30 [1499] [ERROR] Connection in use: ('127.0.0.1', 8000) 2012-11-30 12:28:30 [1499] [ERROR] Retrying in 1 second. 2012-11-30 12:28:31 [1499] [ERROR] Connection in use: ('127.0.0.1', 8000) 2012-11-30 12:28:31 [1499] [ERROR] Retrying in 1 second. 2012-11-30 12:28:32 [1499] [ERROR] Connection in use: ('127.0.0.1', 8000) 2012-11-30 12:28:32 [1499] [ERROR] Retrying in 1 second. 2012-11-30 12:28:33 [1499] [ERROR] Connection in use: ('127.0.0.1', 8000) 2012-11-30 12:28:33 [1499] [ERROR] Retrying in 1 second. 2012-11-30 12:28:34 [1499] [ERROR] Connection in use: ('127.0.0.1', 8000) 2012-11-30 12:28:34 [1499] [ERROR] Retrying in 1 second. 2012-11-30 12:28:35 [1499] [ERROR] Can't connect to ('127.0.0.1', 8000)

Brent O'Connor
10.   At 11:36 a.m. CST on Nov. 30, 2012, Brent O'Connor wrote:

Have you tried running, "wget -qO- 127.0.0.1:8000", to see what gunicorn is serving. Turn on debug mode in your django settings and then trying running the site using 'django-admin.py runserver". There might be a bug in the django site.

Comments are closed.

Comments have been close for this post.