Debian, Django, Nginx, FastCGI and Daemon Tools – The Perfect Combination
Deploying a Django application can be quite confusing especially if you are a developer and not normally involved in system administration work. So what is the best configuration for hosting multiple Django sites on the same server with the lowest administrative overhead possible?
Having tried Gunicorn and Nginx (which admittedly was easier to get working for a single site) I have to say that hosting Django apps using Nginx, FastCGI and Daemon Tools is by far the best method. It avoids having to write init scripts entirely (a task which is still somewhat nasty especially considering the alternatives on other non-Linux platforms such as launchd on Mac OS X which is infinitely superior).
Python
The only real requirements on the Python side are that you have virtualenv and flup installed. Having said that using Pip and virtualenvwrapper is also highly recommended in all Python based deployments no matter what the framework(s) you are using.
Also due to the fact that many Python projects make use of Mercurial you should also take the time to learn it if you do not already know how to use it.
Nginx
The first task is to install nginx. I always install nginx from source as it allows me to cut out some the modules that I have no need for and thus reduce memory footprint a bit more. You can decide if you want to install from source or not.
First we need to install the dependencies for building nginx (I am using Debian 6 here so change it to whatever your platform requires):
apt-get install gcc libpcre3-dev libssl-dev make
Then run configure, make and make install:
./configure --prefix=/opt/nginx --user=nginx \ --group=nginx --with-http_ssl_module --with-http_realip_module \ --with-http_secure_link_module --with-http_gzip_static_module \ --without-http_scgi_module --without-http_uwsgi_module \ --without-http_autoindex_module --without-http_ssi_module \ --without-http_split_clients_module --without-http_userid_module \ --without-http_memcached_module --without-mail_pop3_module \ --without-mail_imap_module --without-mail_smtp_module \ --without-http_auth_basic_module --without-http_charset_module make make install
(you need to be root or use sudo when running make install).
Now we add the user for nginx:
adduser --system --no-create-home --disabled-login \ --disabled-password --group nginx
Once that is done nginx is ready to go. We just need to setup an init script for it. Since I host with Linode (who are pretty darn good) I use the init script that they provide. If you use a distribution other than Debian / Ubuntu you might have to make some changes or get one specifically for your distribution.
wget http://library.linode.com/webservers/nginx/installation/reference/init-deb.sh mv init-deb.sh /etc/init.d/nginx chmod +x /etc/init.d/nginx /usr/sbin/update-rc.d -f nginx defaults
Now nginx is installed. So lets start it and move onto the next stage. We will configure the nginx page separately after we have configured daemon tools.
/etc/init.d/nginx start
Daemon Tools
Now lets install Daemon Tools.
apt-get install daemontools-run
Once that has been completed create a directory to store our first sites configuration file. You will create a directory for every project you wish to run on the server. I generally tend to name each folder after the domain of the site to make it easy to tell which directory holds the configuration for each site (although honestly it is unlikely you will need to play around with these configuration files once they are setup and working properly).
mkdir /etc/service/domain.com
Now just create our configuration file for each Django project. Save it as a file called “run” (without the quotes) in the folder you created above for you domain name and then enter something like this:
#!/usr/bin/env bash source /path/to/your/virtualenv/for/project PROJ_DIR=/path/to/django/project exec envuidgid username python $PROJ_DIR/manage.py \ runfcgi method=threaded minspare=1 maxspare=2 host=127.0.0.1 \ port=9001 pidfile=$PROJ_DIR/django_fcgi.pid daemonize=false
the configuration shown above is for a very low traffic site so if you want to use it in production increase the number the maxspare option to something like 5 – 10 and the minspare to at least 2. Be aware that this will require quite a bit of free RAM so you might have to play around with options depending on how much you have available. You will also need to change “username” in the above example to the name of the user you wish to run the Django apps process.
Now lets us return to our nginx configuration.
Nginx
In order to avoid problems with displaying URLs in our Django application we need to add a line to nginx’s default fastcgi_params file (note that this tutorial was written using version 0.8.54 of nginx, if you are using a different version it might be worth checking if it already exists before adding it).
So just add the following line to the fastcgi_params file:
fastcgi_param PATH_INFO $fastcgi_script_name;then we need to add the following line to our Django projects settings.py file (personally I have a special production_settings.py file which I have imported in my settings.py file which I leave commented out during development and only uncomment when I deploy to production):
FORCE_SCRIPT_NAME = ''then we just add the correct configuration for our domain to our nginx configuration file:
server { listen 80; server_name domain.com; rewrite ^/(.*) http://www.domain.com/$1 permanent; } server { listen 80; server_name www.domain.com; access_log /srv/www/domain.com/logs/access.log; error_log /srv/www/domain.com/logs/error.log; root /srv/www/domain.com/public_html; index index.html; location / { try_files $uri @django; } location /static { alias /path/to/project/static; } location /media { alias /path/to/project/media; } location @django { include /opt/nginx/conf/fastcgi_params; fastcgi_pass 127.0.0.1:9001; fastcgi_pass_header Authorization; fastcgi_intercept_errors off; } }
and away we go.
This is the basic setup fully configured. All we need to do now is make sure that Daemon tools is running the correct version of our app and that nginx has all the changes we have made loaded. So simply restart out domains service:
svc -du /etc/service/domain.com/
and restart nginx:
/etc/init.d/nginx restart
for reference if you just want to stop your Django app:
svc -d /etc/service/domain.com/
or start it:
svc -u /etc/service/domain.com/
and now you should be able to view your site (assuming DNS has propogated) and see the spender of your new Django application running on possibly the easiest to maintain and update Django web stack available. Hopefully you use some form of distributed source code control such as Mercurial or Git. If so you should be able to update you project simply by pulling the latest version and then just restart the Daemon Tools service with:
svc -du /etc/service/domain.com/
I hope you have found this article useful. If so (or even if you have not) leave a comment. Also leave a comment if you have any problems and I’ll try and help you out.
Hi,
I’m kinda new to Django (I’m following their excellent tutorial right now
) but I’m not sure I understand what you are trying to with this line of /etc/service/domain.com/run :
source /path/to/your/virtualenv/for/project
How can you source a directory?
Regards,
LiLo