It's been more than a year since my first endeavor to setup a healthy environment to deploy Django (as I was rudely reminded by the Ubuntu repositories for my server's version ceasing to function). In that time I've learned a lot, and have also stretched my original setup to its breaking point, but inevitably the day comes for a fresh deployment with better configurations and more flexible folder layouts.

From my last server setup guide, a number have things have remained the same: still using Ubuntu, Memcached, Postgres and still using Nginx as a proxy server infront of Apache2. A number of things have changed as well: using mod_wsgi over mod_python , cmemcache over python-memcached , and a more intentional folder layout along with virtualenv to make it straightforward to host multiple projects and domains (including some serving only static files or PHP scripts).

More than just an update, I've also included a few side-quests like using your server as a remote Git repository over SSH, and installing pluggable Django libraries. Finally, in the vein of my previous tutorial, I tried to include every keystroke required to transform a naked Ubuntu Intrepid server into a full-featured multi-site Django-loving server.

Please let me know if you run into any problems, or have suggestions on improvements!

Somehow get a Ubuntu Intrepid server or VPS. (Perhaps go to your SliceHost console and request a new Slice running Ubuntu Intrepid (8.10). ;)

Write down root password and IP address for your box.

SSH into your server. ssh root@255.255.255.255

Update your apt-get sources. apt-get upgrade

Make sure there is an editor that suits your taste available on the system. Vim is pre-installed, but I prefer Emacs... apt-get install emacs

Setup any non-root accounts you want, and one for Django. useradd django mkdir /home/django chown django:django /home/django useradd will mkdir /home/will chown will:will /home/will passwd will

Unless you like sh , change your default shell to something more humane. chsh root -s /bin/bash chsh will -s /bin/bash chsh django -s /bin/bash (These changes won't be applied until you log in the next time.)

Give your account (but not Django) root permissions. visudo Then use the down arrow for find a line that looks like this: root ALL = (ALL) ALL Replicate that with your username: root ALL = (ALL) ALL will ALL = (ALL) ALL

Open a second terminal (leave the first one logged in while we keep configuring SSH, incase something goes horribly awry), and SSH in as your non-root user. ssh will@255.255.255.255 Verify it works, then exit back to your system.

Now it's time to setup password-less login. Return to your home system, scp ~/.ssh/id_dsa.pub will@255.255.255.255:~/ ssh will@255.255.255.255 mkdir .ssh mv id_dsa.pub .ssh/authorized_keys chmod go-w ~/.ssh/authorized_keys ~/.ssh/ Now exit and ssh back in. You should have been logged in without needing to supply your password.

Now it's time to restrict ssh a bit. sudo groupadd sshers sudo usermod -a -Gsshers will sudo emacs /etc/ssh/shhd_config Make these changes: #X11Forwarding yes X11Forwarding no And add these lines to the end of the file: UseDNS no AllowGroups sshers Save the file, and then restart the ssh service. sudo /etc/init.d/ssh restart exit your VPS, and once again try logging back in. If that worked, again open /etc/ssh/sshd_config . sudo emacs /etc/ssh/sshd_config And append this line to the end of the file: PasswordAuthentication no And once again restart the ssh service. sudo /etc/init.d/ssh restart Finally, disable password access to the root account. sudo passwd -l root Now your VPS is only accessible through your approved account from machines with the correct SSH key.

Now it's time to start installing some general libraries. sudo apt-get install subversion git-core gcc curl sudo apt-get install build-essential python-dev python-setuptools sudo apt-get install python-egenix-mxdatetime memcached postfix

Now it's time to configure Postgres, first we need to grab some libraries. sudo apt-get install postgresql-8.3 postgresql-server-dev-8.3 sudo apt-get install postgresql-8.3 postgresql-server-dev-8.3 Rather than a typo, I really did need to run the above command twice for a successful installation. Next we need to configure the postgres user. sudo -u postgres psql template1 ALTER USER postgres WITH PASSWORD 'password'; \q (Make sure that you used a real password, rather than 'password' in the above example.) We also need to modify Postgres' configuration file. sudo emacs /etc/postgresql/8.3/main/pg_hba.conf Move to the bottom of the file, and comment out (add a # at the beginning of the line) all lines which begin with host . (This prevents external access to your database.) It should look like this: # Database administrative login by UNIX sockets local all postgres ident sameuser # TYPE DATABASE USER CIDR-ADDRESS METHOD # "local" is for Unix domain socket connections only local all all password # IPv4 local connections: #host all all 127.0.0.1/32 md5 # IPv6 local connections: #host all all ::1/128 md5 Note that we switched ident sameuser to password for the second local line! Then restart Postgres to have it reload its settings. sudo /etc/init.d/postgresql-8.3 restart And there we have it, Postgres is setup and functioning.

And now it's time to setup memcached. This is a two part process. First we need to start memcached , which is very easy, and second we need to build cmemcache, which is a bit harder. sudo memcached -u www-data -p 11211 -m 32 -d That runs memcached on the standard port, with the standard www-data user (standard for Ubuntu, that is), with 32 megabytes of ram. You might want to allocate more, depending on how much data you're expecting to store in memcached, and the size of your VPS. Next we need to setup cmemcache . First grab libmemcache , sudo apt-get install libmemcache-dev Next we actually build cmemcache . su django mkdir ~/libs/ cd ~/libs/ wget http://gijsbert.org/downloads/cmemcache/cmemcache-0.95.tar.bz2 tar -xjvf cmemcache-0.95.tar.bz2 cd cmemcache-0.95 exit We'll need to actually install the library later when we setup our virtualenv . If you are unable to install cmemcache , follow these instructions. Alternatively, if you want to use the Python based python-memcached (runs a bit slower, but no C module to build and install), you can do that as follows. wget ftp://ftp.tummy.com/pub/python-memcached/python-memcached-latest.tar.gz tar -zxvf python-memcached-latest.tar cd python-memcached-1.43 # later on use python setup.py install inside virtualenv The preference for cmemcache is strictly based on speed. But python-memcached is still quite usable, and if the choice is between python-memcached and not running memcached as your caching backend, picking python-memcached is the clear winner.

Next it's time to setup Nginx, which is a lightweight server we'll use to serve static content as well as proxy requests to Apache21. sudo apt-get install nginx We can quickly verify that it installed correctly. sudo /etc/init.d/nginx start curl 127.0.0.1 Should return: <html> <head> <title> Welcome to nginx! </title> </head> <body bgcolor= "white" text= "black" > <center><h1> Welcome to nginx! </h1></center> </body> </html> Now we're going to modify the nginx.conf file a bit (based on the advice here). sudo emacs /etc/nginx/nginx.conf Right now we only need to make two minor changes, first change line 2 to use four worker processes, worker_processes 4 ; And next uncomment line 18, tcp_nopush on ; We won't need to edit this file again, because of the last line, include /etc/nginx/sites-enabled/* ; which makes it easy to declare site specific settings in their own files, and really cuts down on clutter in nginx.conf . We also need to create a proxy.conf file, which is a common Nginx practice for keeping nginx.conf clean. sudo emacs /etc/nginx/proxy.conf Which should contain these lines: # proxy.conf 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 ; client_body_buffer_size 128k ; proxy_connect_timeout 90 ; proxy_send_timeout 90 ; proxy_read_timeout 90 ; proxy_buffers 32 4k ; Finally, stop and start Nginx (I don't know if this is still the case with the current version of Nginx, but historically I've never gotten it to reload a configuration file with just a reload or restart). sudo /etc/init.d/nginx stop sudo /etc/init.d/nginx start

Now it's time to setup Apache2. First we grab the necessary libraries. sudo apt-get install apache2 libapache2-mod-wsgi Because we already have Nginx bound on port 80 it will throw a bit of a complaint (98)Address already in use: make_sock: could not bind to address 0.0.0.0:80 no listening sockets available, shutting down Unable to open logs ...fail! invoke-rc.d: initscript apache2, action "start" failed. But no worries, we're going to be alright. The first thing we need to do is to setup Apache to run on the internal 127.0.0.1:80 instead of on port 80. sudo emacs /etc/apache2/ports.confg Then modify these lines: NameVirtualHost 127.0.0.1 :80 Listen 127.0.0.1 :80 We also want to make a modification to apache2.conf : sudo emacs /etc/apache2/apache2.conf Search for KeepAlive (it's around line 77), and change its value as follows: # KeepAlive On KeepAlive Off We do this because Nginx doesn't yet support the KeepAlive option. Now let's try starting Apache, again. sudo apache2ctl start Which may complain yet, again. apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName [Thu Feb 12 17:14:46 2009] [warn] NameVirtualHost 127.0.0.1:80 has no VirtualHosts But once, again, no worries, it's just a phase in the process.

Finally we get to actually grab Django. Switch over to the /home/django folder we created earlier, and it's time for us to create some folders. su django mkdir domains mkdir libs mkdir .python-eggs exit Then let's do some permissions wrangling. sudo chown django:www-data .python-eggs sudo chmod g+w .python-eggs/ sudo usermod -a -G www-data django sudo usermod -a -G www-data will sudo chgrp -R www-data /home/django/domains sudo chmod -R 2750 /home/django/domains Then let's grab the Django source code. cd libs svn co http://code.djangoproject.com/svn/django/trunk/ django Normally this would be a great time to symlink the packages into /usr/lib/python2.5/site-packages , but we're going to go a slightly different route instead and use virtualenv to encapsulate each of our projects. sudo easy_install virtualenv We'll take care of setting up virtualenv later, because it is done on a per-project basis.

Our last step of generic preparation is to remove the default sites for both Apache and Nginx. With properly configured setup files for Nginx/Apache (we'll get to those soon ;) you won't run into many situations where you are accidentally showing the default pages, but they often will be displayed when visiting the page directly via it's IP address. While that isn't a terribly common event, it's probably undesirable nonetheless. sudo rm /etc/apache2/sites-enabled/default sudo rm /etc/apache2/sites-enabled/default sudo rm /etc/apache2/sites-enabled/000-default sudo rm /etc/nginx/sites-enabled/default You'll need to restart the server for the changes to take effect. sudo /etc/init.d/nginx stop sudo /etc/init.d/nginx start sudo apache2ctl graceful