Django + Gunicorn + Supervisor + Nginx
As a beginner, I found it quite hard to deploy a Django project in the production environment. After several tries, finally, I got it all together. In this post, I am going to show you how to deploy Django 2.0 project inside Python 3.6 virtual environment using Gunicorn, Supervisor, and Nginx in Ubuntu 16.06.
Install Necessary Packages
Open a terminal and run the following commands:
sudo add-apt-repository ppa:jonathonf/python-3.6
sudo apt-get update
sudo apt-get install python3.6 python-pip python-dev libpq-dev postgresql postgresql-contrib supervisor nginx
This will download and install Python 3.6, pip, PostgreSQL, Gunicorn, Supervisor, Nginx web server, and other necessary packages.
Create PostgreSQL DB and User
Change to the PostgreSQL system user:
sudo su postgres
psql
Create a database for the project:
CREATE DATABASE myblogdb;
Create a database user for the project:
CREATE USER mybloguser WITH PASSWORD 'password';
Give the new user access to administer the new database:
GRANT ALL PRIVILEGES ON DATABASE myblog TO mybloguser;
Exit out of the PostgreSQL user’s shell session by typing:
\q
exit
Create Virtual Environment
Update pip and install virtual environment:
sudo pip install --upgrade pip
sudo apt-get install virtualenv
sudo pip install --upgrade virtualenv
Create a project directory. I will be creating the directory in /var/www
. Don’t forget to add appropriate permissions to the directory.
mkdir /var/www/myproject
cd /var/www/myproject
Create Python virutal environment and activate it:
virtualenv --python=python3.6 venv
source venv/bin/activate
Now install Django, Gunicorn and psycopg2 using pip:
pip install django==2 gunicorn psycopg2
Create Django Project
Create a Django project:
django-admin.py startproject myblog
Change the Django database settings with our PostgreSQL database information.
vim myblog/myblog/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myblogdb',
'USER': 'mybloguser',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}
Change the static root and media root:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static")
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media")
Add our project domains in ALLOWED_HOSTS
:
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'www.localhost']
Set DEBUG to False.
DEBUG=False
Migrate the initial database schema to our PostgreSQL database:
cd myblog
./manage.py makemigrations
./manage.py migrate
Create a superuser for the project:
./manage.py createsuperuser
Collect all of the static content into the directory location we configured:
./manage.py collectstatic
Finally, run the Django development server:
./manage.py runserver 0.0.0.0:8000
Open browser and negivate to http://127.0.0.1:8000/. You will see the default index page of the application.
Create Gunicorn File
Gunicorn will enable communication between Nginx and Django project. First, we’ll use just Gunicorn to display our project on 127.0.0.1:8000
. Now navigate inside /var/www/myproject/myblog/
and run this command:
gunicorn myblog.wsgi:application
It will start Gunicorn server and we’ll be able to see the Django index page on 127.0.0.1:8000
.
Press Ctrl+C
to stop Gunicorn and to create the actual Gunicorn script in /var/www/myproject/gunicorn-start.sh
:
#!/bin/bash
NAME="myblog" # Name of the application
DJANGODIR=/var/www/myproject/myblog # Django project directory
SOCKFILE=/var/www/myproject/run/gunicorn.sock # we will communicate using this unix socket
USER=nginx # The user to run as
GROUP=webdata # The group to run as
NUM_WORKERS=4 # How many worker processes should Gunicorn spawn
DJANGO_SETTINGS_MODULE=myblog.settings # Which settings file should Django use
DJANGO_WSGI_MODULE=myblog.wsgi # WSGI module name
echo "Starting $NAME as `whoami`"
# Activate the virtual environment
cd $DJANGODIR
source /var/www/myproject/venv/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
# Start your Django Unicorn
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--user $USER \
--bind=unix:$SOCKFILE
Make the script executable:
chmod +x gunicorn-start.sh
Create Supervisor Configuration Files
Check Supervisor status:
sudo service supervisor status
If Supervisor is not running, then start Supervisor:
sudo service supervisor start
Create a Supervisor configuration file in /etc/supervisor/conf.d/
directory and name it myblog.conf
:
[program:myblog]
command = /var/www/myproject/gunicorn-start.sh ; Start app
user = nginx ; User to run as
stdout_logfile = /var/www/myproject/logs/gunicorn-supervisor.log ; Where to write log messages
redirect_stderr = true
Reread the configuration files and update Supervisor to start the project:
sudo supervisorctl reread
sudo supervisorctl update
It can also be started manually using:
sudo supervisorctl start myblog
Create Nginx Server Configuration
Create Nginx server configuration in /etc/nginx/sites-enabled/
directory and name it myblog
:
upstream myblog_app_server {
server unix:/var/www/myproject/run/gunicorn.sock fail_timeout=0;
}
server {
listen 8000;
server_name localhost;
return 301 $scheme://www.localhost$request_uri;
}
server {
listen 8000;
server_name www.localhost;
client_max_body_size 4G;
access_log /var/www/myproject/logs/nginx-access.log;
error_log /var/www/myproject/logs/nginx-error.log;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
autoindex on;
alias /var/www/myproject/static/;
}
location /media/ {
autoindex on;
alias /var/www/myproject/media/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://myblog_app_server;
break;
}
}
# For favicon
location /favicon.ico {
alias /var/www/myproject/static/img/favicon.ico;
}
# For robots.txt
location /robots.txt {
alias /var/www/myproject/static/robots.txt ;
}
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/myproject/static/;
}
}
Enable the virtual servers and restart Nginx:
sudo ln -s /etc/nginx/sites-available/myblog /etc/nginx/sites-enabled/myblog
sudo service nginx restart
That’s all. Now, Open http://127.0.0.1:8000 and if everything goes well then you will see the default index page of the application again. Our Django application is production ready.
I like to keep all the related files organized. So, finally the complete structure will look like this:
var
├── etc
| ├── supervisor
| | └── conf.d
| | └── myblog
| └── nginx
| ├── sites-available
| | └── myblog
| └── sites-enabled
| └── myblog
└── www
└── myproject
├── logs
| ├── gunicorn-supervisor.log
| ├── nginx-access.log
| └── nginx-error.log
├── myblog
| ├── manage.py
| └── myblog
| ├── __init__.py
| ├── settings.py
| ├── urls.py
| └── wsgi.py
├── run
| └── gunicorn.sock
├── static
├── media
├── gunicorn-start.sh
└── venv
To create a basic Django application, visit Writing your first Django app.
For further learning, visit Django documentation.