Digital Ocean is a fntastic hosting platform for your Django site. In this blog post, I'll walk you through the steps I take to set up a Django site with Nginx as the front-end web server, uWSGI for the Django app, and Supervisor for managing the Django process. I'll also use MySQL for the database. We'll also use a virtual environment so that you can host multiple Django sites on the same host without running into python library version conflicts.

I'm assuming that you have a working Django project either developed locally or on another host. I'm also assuming you know what python virtual environments are (and that you're using one for your project). I'm also assuming that you use a Git repo (such as on github.com) to check your code into.

I'm also assuming you have a domain name registered that you'd like to point to your Digital Ocean droplet.

Although the tutorial below assumes Digital Ocean, the majority of the steps will work on other platforms that provide a Ubuntu Linux VM, such as Linode.

Get A Digital Ocean Droplet

First you'll need a Digital Ocean droplet. A Droplet is their term for a virtual machine. They have a few options to choose from. For beginner Django sites, a $10 / month droplet will be sufficient. I personally prefer using Ubuntu Server as my OS of choice, and the instructions below assume that option when the droplet is created.

If you're new to Digital Ocean, you're welcome to use my referral code if you want to to give yourself some free credit to try it out:

https://www.digitalocean.com/?refcode=4100c8d6bb9d

Part of the setup process requires you to give Digital Ocean your public key.

How to generate your SSH public key from a terminal (command line) in either Linux or Mac OS:

  1. Execute ssh-keygen if you don't have a file named ~/.ssh/id_rsa.pub. When it asks for a password, just hit Enter a few times (don't give it a password).
  2. To print your public key, run cat ~/.ssh/id_rsa.pub
  3. Copy and paste the output from the command above. Make sure to get the entire line, from ssh-rsa all the way to the end that has your username. Paste that into the area where you can add a key when setting up your droplet.

Less than 60 seconds later you should have a shiny new droplet you can log in to. After the VM has been created in Digital Ocean, you need to log in as root. Replace the IP below with the IP address of your new droplet:

ssh -l root 104.131.217.188

Generate Requirements.txt

The first step is to create a list of python packages needed in order to execute your Django app on Digital Ocean. After activating your virtual environment on your local copy of your Django project, do the following:

pip freeze > requirements.txt  

It's a good idea to track add requirements.txt to your git repo.

git add requirements.txt  
git commit -m "My requirements file" requirements.txt  
git push  

Install Packages

Here are the steps to install the Ubuntu packages:

add-apt-repository ppa:nginx/stable

apt update && apt upgrade

apt install nginx-full mysql-server supervisor git build-essential virtualenvwrapper python-mysqldb python-dev zlib1g-dev libjpeg-dev libmysqlclient-dev vim  

The system will ask you to set a master root password for MySQL. Remember to write down your root password for mysql, as you'll need it later on in this tutorial.

Add A User Account

Although it might be ok to be the root user when setting things up, eventually you'll want to create your own user account and perform actions as that user to be on the safe side.

Replace <username> with a username you want to use on the system:

adduser <username>  
adduser <username> www-data  
adduser <username> sudo  

become your user (test to make sure it works):

su <username>  

You can go back to being root by typing exit (or CTRL-D)

IMPORTANT: for the rest of this tutorial, I'm assuming you'll be performing these actions as your user that you created, not root. It's important to now either log out as root and log in as your user, or do the su command above to log in as your user.

Set Up Your Webroot

A webroot is where the files for your website and django site will be kept. Let's get it set up with the right permissions. I like to keep my sites in subdirectories of /var/www. The chown and chmod commands below adjust permissions.

sudo mkdir /var/www  
sudo chown www-data:www-data /var/www  
sudo chmod 775 /var/www  
sudo chmod +s /var/www  

The last line above, chmod +s, sets the sticky bit such that new subdirectories created in that directory will inherit the permissions of the /var/www directory by default.

Put Your SSH key from the Droplet VM on Github

We'll want to clone, pull, and push from the VM so now is a good time to generate a private and public SSH key you can add to your Github account (or wherever you store your git repo):

As your user, do the following. When ssh-keygen asks for a password, just hit Enter and go with the defaults.

ssh-keygen  
cat ~/.ssh/id_rsa.pub  

Copy that entire key to your Github account (under Settings->SSH Keys).

Pull Down Your Repo, set up Django

We'll pull down the files for Django project to the Droplet VM in this step. In the second step below, replace <repo> with the git clone URL from your github repository (usually looks like git@githumb.com:username/projectname.git)

cd /var/www  
git clone <repo>  

We'll now set up the Python virtual environment for your repo and install requirements. Note that we generated and checked in our requirements.txt file towards the beginning of this tutorial.

cd /var/www/REPO_NAME  
virtualenv venv  
source venv/bin/activate  
pip install -r requirements.txt  
pip install mysql-python  

If you run into errors installing python packages, it could be because the versions of the python packages and libraries used in your development environment differ from what your droplet will use. One suggestion is to make sure the requirements.txt in your development environment is fresh by doing another pip freeze. As packages get upgraded over time, old ones become discontinued and no longer available on Pypi. Updating the version number in your requirements.txt may solve the problem in those cases. When in doubt, Googling the problems one at a time may be the best way to resolve these conflicts.

It would be a good idea at this point to run a Runserver to see if your site runs. You can do a python manage.py runserver 0.0.0.0:8000 and point your browser to the public IP address of your droplet at port 8000 to see if your site comes up. Note that we haven't set up the database yet (it's the next step), so you may get some database-related errors.

Configure MySQL

First step is to create the database for your project. Replace PROJECTDATABASENAME with the database name you intend to use for your project (which you'll define in settings.py). The root password here is the MySQL root password you defined when you installed the MySQL server package earlier in this tutorial.

mysqladmin create PROJECT_DATABASE_NAME -u root -p  

Create the user account for the database that you'll use from your django app. In the example below, I used djangoproject as my username.

grant all privileges on PROJECT_DATABASE_NAME.* to 'djangoproject'@'%' identified by 'PROJECT_DB_PASSWORD';  

Now switch over to your project in /var/www and find your settings.py file. Edit your database settings accordingly:

DATABASES = {  
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'PROJECT_DATABASE_NAME',
        'USER': 'djangoproject',
        'PASSWORD': 'PROJECT_DB_PASSWORD',
        'HOST': 'localhost',
    }
}

Don't forget to migrate your database at this point. This will create the database tables. After activating your virtual environment (via the source venv/bin/activate command), run this:

python manage.py migrate  

Set up Nginx

In this tutorial, I'll use example.com for your website's domain name.

Create a /etc/nginx/sites-available/example.com.conf file. Here's a sample of what it should look like. I'm assuming my project got cloned down to /var/www/exampleproject and the actual Django project itself was also named exampleproject. I'm assuming my media and static folders are a subdirectory of the django project as well. I use vim as my text editor, but you can use nano or whatever you prefer to create this file.

server {  
        server_name example.com www.example.com;
        listen 80;

        root /var/www/exampleproject/exampleproject/media;

        location /static/ {
                alias /var/www/exampleproject/exampleproject/static/;
                expires 30d;
        } 

        location /media/ {
                alias /var/www/exampleproject/exampleproject/media/;
                expires 30d;
        } 

        location / {
                uwsgi_pass unix://tmp/example.com.nginx.sock;
                include uwsgi_params;
        } 
} 

Now we need to tell Nginx that this is a site to be enabled at startup:

cd /etc/nginx/sites-enabled/  
sudo ln -s ../sites-available/example.com.conf .  

Install uWSGI, set up uWSGI

uWSGI is a fantastic python package to run Django sites. It has many many features. We won't go into the details of uWSGI itself in this tutorial, but we'll cover how to install it and set it up.

First, make sure you've enabled your virtual environment:

cd /var/www/REPO_NAME  
source venv/bin/activate  

Then, install uwsgi into your virtual environment:

pip install uwsgi  

Once it's installed, make a note of where it installed to (while still in your virtual environment):

which uwsgi  

Write down the output. On my system, it puts it in /var/www/PROJECT_NAME/venv/bin/uwsgi. You'll need this full path in the section below when we set up Supervisor.

uWSGI can take a configuration file for adjusting command options. Create a /var/www/PROJECT_NAME/config.ini file. I use vim as my text editor, but you can use nano or whatever you prefer to create this file.

Some notes:

  • In the chdir line below, you'll want to set this to the same directory where your manage.py file lives.
  • For module: Recent Django setups generate a wsgi.py file for you automatically. You'll set this to your django project's foldername, "dot", wsgi, as in the example below:
[uwsgi]
home=/var/www/PROJECT_NAME/venv  
chdir=/var/www/PROJECT_NAME/DJANGO_PROJECT_NAME  
socket=/tmp/crminal.co.nginx.sock  
chmod-socket=777  
chown-socket=www-data  
module=djangoproject.wsgi  
vacuum=true  
master=true  
max-requests=2000  
touch-reload=config.ini  
workers=2  
harakiri-verbose=true  
harakiri=180  
stopsignal=TERM  

Set Up Supervisor

Supervisor is a program that starts other programs and makes sure they stay running. If the program exits for any reason, it will restart it. It's a nifty and solid way to execute your Django apps. You'll eventually want one configuration file in Supervisor for every Django app you want to run from your site.

We'll be creating a /etc/supervisor/conf.d/example.com.conf file (replace example.com with your domain name of your django project). Where it says command, put in the full path to your uwsgi executable we jotted down in the previous section (when we ran the which uwsgi command). Also, after program:, you give your program a name. I don't think it can contain dots, so I just put the domain name without the .com or whatever ending you have for your domain.

[program:website]
command = /var/www/PROJECT/venv/bin/uwsgi --ini /var/www/PROJECT/config.ini --uid=www-data --gid=www-data  

We'll want to make sure Supervisor starts automatically if the VM ever gets rebooted. Execute this command to tell Ubuntu to start the supervisor service at bootup:

sudo update-rc.d supervisor defaults  

Start It Up!

This is the most exciting step. This is when we're going to execute everything we've set up:

sudo service supervisor restart  
sudo service nginx restart  

Get Static Files Ready

You may already have this in your settings.py, but just to be safe, make sure you have your STATICROOT setting defined correctly in settings.py. Be sure to have it pointing to the full path to where STATICROOT is on your server.

If you haven't, create a static directory in your project root (same directory where manage.py lives). Note that the path to this static folder should match the line in your nginx.conf, as it will be Nginx (not uwsgi) that serves your static files.

Then, collect static files:

python manage.py collectstatic  

MISC Tips

How to reload Nginx after a configuration change:

sudo /etc/init.d/nginx reload  

How to restart uWSGI via Supervisor:

sudo supervisorctl  
restart <program name>  

After making a change to your Django app, you can restart it via the supervisor command above or do this cool trick to reload it:

touch /var/www/DJANGO_PROJECT/config.ini  

Conclusion

It's rewarding to set up and run your own Django apps on Digital Ocean (or any VM). If there are some steps that aren't clear or errors above, please leave a note in the comments and I'll see if I can clarify the steps above.