How to host Django application and LEMP site on the same server using Nginx and Gunicorn on Ubuntu 16.04

You can host django application and PHP site on the same VPS. This is easy to set up and you don’t need to be an expert to do this.

Prerequisite:
* At least one domain name (i.e. example.com)

You must have a domain in order to host both django and php site. You cannot do this with just ip address.

Suppose you have one domain i.e., ‘example.com’, then for the django app we will use ‘example.com’ and ‘www.example.com’. And for php site we will use ‘php.example.com’.
To do this your domain’s ‘A’ record must be pointing towards the IP address of your server and in the host field you must have ‘*’. As shown below:

a_record_godaddy

Ok, let’s do it!

First we will start with Django site

Install required packages:

$ sudo apt-get update

for python2:

$ sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx gunicorn

for python3:

$ sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx gunicorn

Install python virtual environment:

for python2:

$ sudo -H pip install --upgrade pip
$ sudo -H pip install virtualenv

for python3:

$ sudo -H pip3 install --upgrade pip
$ sudo -H pip3 install virtualenv

Set up Postgresql Database and User

You can skip this step if you are using sqlite or some other DBMS system.

Log into the postgresql interactive session in order to create new database and user.

$ sudo -u postgres psql

Now create database for your project:

postgres=# CREATE DATABASE project;

Now create new database user:

postgres=# CREATE USER projectuser WITH PASSWORD 'password';

Grant the user access to his database:

postgres=# GRANT ALL PRIVILEGES ON DATABASE project TO projectuser;

Now quit from PostgreSQL session

postgres=# \q

Initialize django project

Now its time we initialize our django projects and set it up.

First go to home directory and make directory for our django project.

$ cd ~
$ mkdir project
$ cd project

Now we will initialize virtual environment and install required python packages:

$ virtualenv venv
$ source venv/bin/activate
(venv) $ pip install django gunicorn psycopg2

Now, we will create our project and set it up.

(venv) $ django-admin startproject project .

Don’t forget to type the period at last, it will tell django to create project at the current location.

By now your directory structure would be like:

project/
|
| ---project/
    |---__init__.py
    |---settings.py
    |---urls.py
    |---wsgi.py
|---venv/
|---manage.py

Now we will edit our settings.py to add ALLOWED_HOSTS and DATABASE credentials:

(venv) $ nano project/settings.py

Add your domain or sub-domain or IP address to the allowed host:

ALLOWED_HOSTS = ['your_server_domain_or_IP', ]

and your database credentials (not required if you are using sqlite):

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'project',
'USER': 'projectuser',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
  }
}

and set your static root directory:

STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

Now migrate the database:

(venv) $ python manage.py makemigrations
(venv) $ python manage.py migrate

collect all the static files into root directory by typing:

(venv) $ python manage.py collectstatic

We will now test the server by running it:

(venv) $ python manage.py runserver 0.0.0.0:8000

Go to http://domain_or_ip:8000 you will see default django index page or your project’s index page.

Note:

If you are unable to see the page and if your firewall is enabled then you have to run following commands:
$ sudo ufw allow 8000
$ sudo ufw allow ‘Nginx Full’

Moving forward we will test our site with gunicorn. To do so run the below command:

(venv) $ gunicorn --bind 0.0.0.0:8000 project.wsgi

Replace the project.wsgi with your project’s name or with the name of the folder which contains wsgi.py.
You must see the same page as before, however without css and js as gunicorn does not have their location.

Deactivate the virtual environment:

(venv) $ deactivate

Now, we will create gunicorn systemd service file to start the application server.

$ sudo nano /etc/systemd/system/gunicorn.service

In gunicorn service file we will specify the dependencies of the django application, so that gunicorn would be able to execute them correctly.
Copy the code snippet below and paste it on the service file. Change the path relative to your django app.

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/project # location of directory which contains your manage.py file (remove this comment)
ExecStart=/home/user/project/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/user/project/project.sock project.wsgi:application

[Install]
WantedBy=multi-user.target

Replace user with the name of the user you logged in as (Note: it should not be root). Be careful with the path you have provided. Make sure that the user you are using must be able to access that path with using ‘sudo’.

Now we will start gunicorn service:

$ sudo systemctl start gunicorn
$ sudo systemctl enable gunicorn

Print the list of content on the project directory.

$ ls /home/user/project

There must be a socket file with the name of your project i.e., project.sock, if it is not there then restart the gunicorn.

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

Check for socket file again, if the problem persist, check the status of gunicorn

$ sudo systemctl status gunicorn

If gunicorn is not running then check the error log with:

$ sudo journalctl -u gunicorn

These type of errors occur because of:
* the wrong path provided in the gunicorn service file,
* user does not have permission to access the project directory
* gunicorn is not installed correctly

After changing the gunicorn system file, always run the below command and check for socket file:

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

Configure Nginx to serve django app

First create a server block for our django app

$ sudo nano /etc/nginx/sites-available/project

In the server block paste the following code and change it according to your project:

server {
listen 80;
server_name example.com www.example.com; #replace with your domain

location =  /favicon.ico { access_log off; log_not_found off; }

location /static/ { #if you are using amazon s3, then skip this block
root /home/user/project;
}

location / {
include proxy_params;
proxy_pass http://unix:/home/user/project/project.sock; #path to your socket file
}
}

Now enable the file by linking it to sites-enabled directory:

$ sudo ln -s /etc/nginx/sites-available/project /etc/nginx/sites-enabled

Test the nginx configuration:

$ sudo nginx -t

If test failed then check the socket path and make sure socket file is there and check the nginx error log:

$ sudo tail -F /var/log/nginx/error.log

Make corrections and test again until test is passed.

If test is passed then restart the nginx:

$ sudo systemctl restart nginx

Go to your domain i.e. example.com and you will see your site up and running.

Set up LEMP

Install mysql server

$ sudo apt-get install mysql-server

and then complete the mysql set up with

$ sudo mysql_secure_installation

Follow the instruction and you are done.

Install php

$ sudo apt-get install php-fpm php-mysql php-curl php-gd php-mbstring php-mcrypt php-xml php-xmlrpc nginx

There is a flaw in php that will allow attackers to exploit php site. So, to fix this, open php configuration file:

$ sudo nano /etc/php/7.0/fpm/php.ini

and add

cgi.fix_pathinfo=0

at the end of the file.

Now, restart the php processor:

$ sudo systemctl restart php7.0-fpm

Configure Nginx to use php

Open the default server block configuration file:

$ sudo nano /etc/nginx/sites-available/default

and replace the code with the following one:

server {
listen 80 default_server;
listen [::]:80 default_server;

root /var/www/html; #directory path where your php files must be located
index index.php index.html index.htm index.nginx-debian.html;

server_name php.example.com; #your domain or sub-domain or server's ip address

location / {
try_files $uri $uri/ =404;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}

location ~ /\.ht {
deny all;
}
}

Test the server block configuration file:

$ sudo nginx -t

If test failed then recheck your file and re-run the test.

If test is passed, then restart the nginx:

$ sudo systemctl reload nginx

Test the LEMP installation

Go to /var/www/html

$ cd /var/www/html

Create the file index.php

$ sudo nano index.php

Paste the following code in it:

<?php
phpinfo();

Go to php.example.com or whatever domain you have set. You must see information about your server and php installation. After checking the site, remove the file, it is not safe to show server information.