em Django, English, Git, Github, Programming, Python

Deploy for kids – Django & Python 3

crianças fazendo deploy

here is a lot of tutorials out there, especially in English. Here it goes another one. I wrote it originally in Portuguese.

The reason many people has problems deploying is because they don’t pay enough attention to details. Deploying is easy when you are familiarized with all parts envolved. You must know how to authenticate through ssh, be used to command line and Linux, understand how to configure and set up your project, have an idea of what serving static files is, what is Gunicorn… Ok, it’s not that simple. That’s why there is a lot of deploy tools, kits, and tutorials. Currently, with Ansible, Docker and whatever kids are using these days it’s easier to deploy, but what happens under the hood gets more abstract.

Maybe in a couple of years, this post is going to be obsolete if it’s not already with serverless and everything else. Anyway, just a few people want to learn how to deploy Django as I’ll show here, but if it helps at least one person, I’ll be satisfied.

Enjoy this Old-Style guide!

The Server

I presume you don’t have a server or AWS account, DigitalOcean, Linode… Nothing! You have to create an account in one of them and launch a server with the distro you want. If it’s your first time, don’t go with AWS because it’s way more complicated than the others.

In this tutorial, I’m using an Ubuntu 16.04, the most common distro you’ll see around. You can also pick a Debian if you like.

Initial Set Up

Configure server timezone

sudo locale-gen --no-purge --lang pt_BR  # I'm using pt_BR, because HUE HUE BR BR
sudo dpkg-reconfigure tzdata

Update and upgrade OS Packages:

sudo apt-get update 
sudo apt-get -y upgrade

Installing Python 3.6 over Python 3.5

Replace Python 3.5 which is default on our distro with Python 3.6.

sudo apt-get update
sudo add-apt-repository ppa:jonathonf/python-3.6
sudo apt-get install python3.6
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2

You can choose which Python version the OS will call when you type python3.

sudo update-alternatives --config python3

Having trouble, take a look here:

How to Install Python 3.6.1 in Ubuntu 16.04 LTS

Install OS requirements

sudo apt-get install python3-pip nginx supervisor git git-core libpq-dev python-dev 

If your project has more OS requirements, install them as well.

VirtualEnvWrapper for Python3

I’m a fan of VirtualEnvWrapper. It’s super easy and creates all my virtual environments in the same place. That’s a personal choice, if you don’t like it, use what you know how to use.

First, you install virtualenvwrapper, and then define where to put your virtualenvs. (WORKON_HOME).

If you need to use it with multiple Python versions, you must define VIRTUALENVWRAPPER_PYTHON. Here I’m using always with python3. It’s not a problem since you can create a virtualenv pointing which Python that env will use.

sudo pip3 install virtualenvwrapper
echo 'export WORKON_HOME=~/Envs' >> ~/.bashrc
echo ‘export VIRTUALENVWRAPPER_PYTHON=`which python3`’ >> ~/.bashrc
echo 'source /usr/local/bin/virtualenvwrapper.sh' >> ~/.bashrc
source ~/.bashrc

Now, create your virtualenv and define what Python is going to use.

mkvirtualenv name_venv --python=python3

VirtualEnvWrapper is really easy to use. If you want to activate a virtual env, you can use workon.

workon name_venv

To deactivate this virtualenv:


To remove a virtualenv:

rmvirtualenv name_venv

Generate SSH for GitHub Authentication

You don’t want (neither should) write your password to git pull your project on the server.

Generating SSH Keys:

cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "[email protected]"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa

See and copy the content of your public key (id_rsa.pub)

cat ~/.ssh/id_rsa.pub

Then sign in your GitHub account and go to Settings > SSH and GPG Keys. Click in New SSH Key, give it a name, like (“test server keys”) and in Key paste the content of your  id_rsa.pub

Clone your Django Project

Copy the SSH link from GitHub to clone your project. In this case, I’m using a project that I just have found as an example.

git clone [email protected]:kirpit/django-sample-app.git

In the project folder, install the project requirements.

Remember that you have to be in your virtual environment

cd django-sample-app/
pip install -r requirements.txt

Now, make the necessary alterations for your deploy, such as create a settings_local.py file, change database settings or anything specific to your project.

After you’re done, run your migrations and collect your static files (if you’re using it).

python manage.py migrate
python manage.py collectstatic

Configuring NGINX

Nginx, like Apache, is an entirely separate world. Right now, you just need the basics.

/etc/nginx/sites-available/ is a directory where you have to put the config files of available sites. There is another directory, /etc/nginx/sites-enabled/ that shows which sites are enabled. They are the same thing, but what is put on enabled will be served by Nginx.

It’s usual to create your config file on sites-available and create just a symlink to sites-enabled.

First of all, I’ll remove the default site from Nginx.

sudo rm /etc/nginx/sites-enabled/default

Now, create the config file for your site. (If you don’t know how to use VIM, use nano instead of vi)

sudo vi /etc/nginx/sites-available/mysite

Past this on your file, changing the necessary paths:

server {
 listen 80;
 access_log /home/username/logs/access.log;
 error_log /home/username/logs/error.log;

 server_name nome-site.com.br;

 location / {

 proxy_pass_header Server;
 proxy_set_header X-Forwarded-Host $server_name;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header Host $http_host;


 location /static {

   alias /home/username/project_path/static/;


And create a symlink to sites-enabled:

sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

Restart Nginx:

sudo service nginx restart


Ok, if you made it till here, if you access your website you will see a 502 Bad Gateway from Nginx. That’s because it’s nothing here:

Now, configure the website to run on 8000 port.

Configuring Gunicorn

Are you guys alive? Don’t give up, we’re almost there.

In your virtualenv (remember workon name_env?) install Gunicorn

pip install gunicorn

In your project’s directory, make a gunicorn_conf file:

bind = ""
logfile = "/home/username/logs/gunicorn.log"
workers = 3

Now, if you run Gunicorn you will see your website working!

/home/username/Envs/name_venv/bin/gunicorn project.wsgi:application -c gunicorn_conf

But what are you going to do? Run this command inside a screen and walk away? Of course not! You’ll use Supervisord to control Gunicorn.

Configuring Supervisor

Now create a gunicorn.conf:

sudo vi /etc/supervisor/conf.d/gunicorn.conf

That’s the content:

command=/home/username/Envs/name_venv/bin/gunicorn project.wsgi:application -c /home/username/project/project_django/gunicorn_conf

And now, you just tell Supervisor that there is a new process in town and Supervisord will take care of it:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart gunicorn

And voilá! A new running you will have.


There is a lot of things envolved on a deploy process. You have to configure a firewall, probably you’ll have to serve more than one static folder, etc, etc… But you have to start somewhere.

I can’t believe I wrote a whole post without using any GIF. So, just to finish, pay attention in all paths I’ve used here.