Django is an open source web application framework that can help you get your Python application or website off the ground.
In this guide, we will demonstrate how to install and configure Django, Gunicorn and Nginx on Ubuntu 18.04 to support and serve Django applications. We will be setting up a PostgreSQL database in Cloud SQL instead of using the default SQLite database.
Prerequisites
- Your Compute Engine Instance running, see the Setting up Compute Engine Instance.
- Domain name is pointed to your virtual machine.
- For setting up Cloud DNS, see the Setting up Google Cloud DNS for your domain.
Install required packages
SSH to your Compute Engine instance and begin typing the following commands to start installing Django
sudo apt update
sudo apt install python3-pip python3-dev libpq-dev postgresql-contrib nginx curl
Creating Cloud SQL Instance with PostgreSQL
Go to your Google Cloud Console and navigate to Storage >> Cloud SQL
Click Create Instance
Choose PostgreSQL
Click Next
Enter Instance ID
Set a password for the postgres
user
Choose the region and zone same as your Compute Engine Instance
Configure machine type and storage
Click Create
Once your Cloud SQL Instance is created go to the Connections tab inside your instance and under Authorized Networks click Add network
In Network enter the IP address of your Compute Engine

Click Save
Now your Compute Engine is authorized to connect to your Cloud SQL Instance
Creating the PostgreSQL Database and User
Go back to your Compute Engine Instance and create a database and user.
Make sure to replace the CLOUD_SQL_IP_ADDRESS with your Cloud SQL IP Address and PASSWORD with the password you set up while creating the Cloud SQL Instance.

psql -h CLOUD_SQL_IP_ADDRESS -U postgres
CREATE DATABASE myproject;
CREATE USER myprojectuser WITH PASSWORD 'PASSWORD';
ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
\q
Cloud SQL is now set up so that Django can connect to and manage its database information.
Creating a Python Virtual Environment for your Project
sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv
mkdir ~/myprojectdir
cd ~/myprojectdir
virtualenv myprojectenv
Activate the virtual environment by typing
source myprojectenv/bin/activate
Your prompt should change to indicate that you are now operating within a Python virtual environment. It will look something like this: (myprojectenv)username@host:~/myprojectdir$
With your virtual environment active, install Django, Gunicorn, and the psycopg2 PostgreSQL adaptor with the local instance of pip
pip install django gunicorn psycopg2-binary
You should now have all of the software needed to start a Django project.
Creating the Django Project
django-admin.py startproject myproject ~/myprojectdir
Configure the project settings
Update the settings.py
for allowed hosts and connect to Cloud SQL database
sudo nano ~/myprojectdir/myproject/settings.py
Be sure to include localhost
as one of the options since we will be proxying connections through a local Nginx instance
ALLOWED_HOSTS = ['.yourdomainname.com', 'localhost']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproject',
'USER': 'myprojectuser',
'PASSWORD': 'PASSWORD',
'HOST': 'CLOUD_SQL_IP_ADDRESS',
'PORT': '',
}
}
Now add a setting indicating where the static files should be placed. Nginx can handle requests for these items.
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
Save and close the file when you are finished
Completing the Initial Project Setup
~/myprojectdir/manage.py makemigrations
~/myprojectdir/manage.py migrate
~/myprojectdir/manage.py createsuperuser
You will have to select a username, provide an email address, and choose and confirm a password.
We can collect all of the static content into the directory location we configured by typing:
~/myprojectdir/manage.py collectstatic
We’re now finished configuring our Django application. We can back out of our virtual environment by typing
deactivate
Creating Socket and Service Files for Gunicorn
sudo nano /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=username
Group=username
WorkingDirectory=/home/username/myprojectdir
ExecStart=/home/username/myprojectdir/myprojectenv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
myproject.wsgi:application
[Install]
WantedBy=multi-user.target
Start and enable Gunicorn socket
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
Checking for the Gunicorn Socket File
Check the status of the process to find out whether it was able to start:
sudo systemctl status gunicorn.socket
You will get the status as Active: active (listening)
Next, check for the existence of the
gunicorn.sock
file within the /run
directory:
file /run/gunicorn.sock
Output
/run/gunicorn.sock: socket
Testing Socket Activation
sudo systemctl status gunicorn
Output
● gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
Active: inactive (dead)
To test the socket activation mechanism, we can send a connection to the socket through curl by typing:
curl --unix-socket /run/gunicorn.sock localhost
You should receive the HTML response
sudo systemctl status gunicorn
If you make changes to the /etc/systemd/system/gunicorn.service
file, reload the daemon to reread the service definition and restart the Gunicorn process by typing:
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
NGINX Proxy Pass to Gunicorn and setup HTTPS
Create a new Nginx configuration for your website in the sites-available
directory
sudo nano /etc/nginx/sites-available/yourdomainname.com
Copy and paste the following configuration, ensure that you change the server_name
, error_log
to match your domain name. Hit CTRL+X
followed by Y
to save the changes.
server {
listen 80;
listen [::]:80;
server_name yourdomainname.com www.yourdomainname.com;
location = /favicon.ico {
access_log off;
log_not_found off;
}
location /static/ {
root /home/username/myprojectdir;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
To enable this newly created website configuration, symlink the file that you just created into the sites-enabled directory.
sudo ln -s /etc/nginx/sites-available/yourdomainname.com /etc/nginx/sites-enabled/yourdomainname.com
Check your configuration and restart Nginx for the changes to take effect
sudo nginx -t
sudo service nginx restart
Now visit your domain name in your web browser, you can view the Django congratulations page.

You can view the admin page with the following url yourdomainname.com/admin

Create SSL certificate and enable HTTP/2
HTTPS
HTTPS is a protocol for secure communication between a server (instance) and a client (web browser). Due to the introduction of Let’s Encrypt, which provides free SSL certificates, HTTPS are adopted by everyone and also provides trust to your audiences.
HTTP/2
HTTP/2 is the latest version of the HTTP protocol and can provide a significant improvement to the load time of your sites. There really is no reason not to enable HTTP/2, the only requirement is that the site must use HTTPS.
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
Now we have installed Certbot by Let’s Encrypt for Ubuntu 18.04, run this command to receive your certificates.
sudo certbot --nginx certonly
Enter your email
and agree to the terms and conditions, then you will receive the list of domains you need to generate SSL certificate.
To select all domains simply hit Enter
The Certbot client will automatically generate the new certificate for your domain. Now we need to update the Nginx config.
Redirect HTTP Traffic to HTTPS with www in Nginx
Open your site’s Nginx configuration file add replace everything with the following. Replacing the file path with the one you received when obtaining the SSL certificate. The ssl_certificate directive
should point to your fullchain.pem file, and the ssl_certificate_key
directive should point to your privkey.pem file.
server {
listen [::]:80;
listen 80;
server_name yourdomainname.com www.yourdomainname.com;
# redirect http to https www
return 301 https://yourdomainname.com$request_uri;
}
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
server_name www.yourdomainname.com;
ssl_certificate /etc/letsencrypt/live/yourdomainname.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomainname.com/privkey.pem;
root /home/username/myprojectdir;
# redirect https non-www to https www
return 301 https://yourdomainname.com$request_uri;
}
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
server_name yourdomainname.com;
ssl_certificate /etc/letsencrypt/live/yourdomainname.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomainname.com/privkey.pem;
location = /favicon.ico {
access_log off;
log_not_found off;
}
location /static/ {
root /home/username/myprojectdir;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
add_header Content-Security-Policy "img-src * 'self' data: blob: https:; default-src 'self' https://*.googleapis.com https://*.googletagmanager.com https://*.google-analytics.com https://s.ytimg.com https://www.youtube.com https://www.yourdomainname.com https://*.googleapis.com https://*.gstatic.com https://*.w.org data: 'unsafe-inline' 'unsafe-eval';" always;
add_header X-Xss-Protection "1; mode=block" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Access-Control-Allow-Origin "https://www.yourdomainname.com";
add_header Referrer-Policy "origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
}
}
The http2
value is all that is needed to enable the HTTP/2 protocol.
Hit CTRL+X
followed by Y
to save the changes.
Check your configuration and restart Nginx for the changes to take effect.
sudo nginx -t
sudo service nginx restart
Renewing SSL Certificate
Certificates provided by Let’s Encrypt are valid for 90 days only, so you need to renew them often. Now you set up a cronjob to check for the certificate which is due to expire in next 30 days and renew it automatically.
sudo crontab -e
Add this line at the end of the file
0 0,12 * * * certbot renew >/dev/null 2>&1
Hit CTRL+X
followed by Y
to save the changes.
This cronjob will attempt to check for renewing the certificate twice daily.
if I don’t have yourdomainname.com can I use public ip only? thank you if you please answer
You can use IP address instead of your domain name, but you cannot install SSL
Hi sir, I’m using django + nginx with angular. I am hosting angular on port 443 and django on port 5000. Im getting this error ERR_TOO_MANY_REDIRECTS.
server {
listen [::]:80;
listen 80;
server_name newmarg.com http://www.newmarg.com;
# redirect http to https www
return 301 https://newmarg.com$request_uri;
}
server {
listen [::]:5000 ssl http2;
listen 5000 ssl http2;
server_name http://www.newmarg.com;
ssl_certificate /etc/letsencrypt/live/newmarg.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/newmarg.com/privkey.pem;
root /home/sarvesh/battery_app;
# redirect https non-www to https www
return 301 https://newmarg.com$request_uri;
}
server {
listen [::]:5000 ssl http2;
listen 5000 ssl http2;
server_name newmarg.com;
ssl_certificate /etc/letsencrypt/live/newmarg.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/newmarg.com/privkey.pem;
location = /favicon.ico {
access_log off;
log_not_found off;
}
location /static/ {
root /home/sarvesh/battery_app;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
add_header Content-Security-Policy “img-src * ‘self’ data: blob: https:; default-src ‘self’ https://*.googleapis.com https://*.googletagmanager.com https://*$
add_header X-Xss-Protection “1; mode=block” always;
add_header X-Frame-Options “SAMEORIGIN” always;
add_header X-Content-Type-Options “nosniff” always;
add_header Referrer-Policy “origin-when-cross-origin” always;
add_header Strict-Transport-Security “max-age=31536000; includeSubdomains; preload”;
}
}
this is my nginx site file. What am I doing wrong? Please help!
Please remove the
http://
from theserver_name
directive.