Django simple deployment with gunicorn and whitenoise

If you are looking for simple production deployment without much configuration or not making much configuration from your development. The following whitenoise, gunicorn packages will help you deploy it within 10 minutes. This setup is not applicable for high traffic websites which needs load balancing and HTTPS. For that situation, You need to deploy with nginx. 1000 users per minute is an ideal candidate website.

But It is very important to change few configurations, before deploying in external server or cloud services. You have to prepare your application for production ready.

Why not default configuration

You can not use default configuration with DEBUG=True and runserver for production deployment, as it is only meant for development purpose. You have to change a bit for production deployment. This will give security-vise values to your application.

  1. Running server with DEBUG=True will display all the important trace in the response. This will expose lot of details about your application code in case if the exception is not handled in the application.

  2. Django "runserver" is not tested for production. I have seen socket errors in the runsever while in development. So I would suggest not to use this for production. 

Production mode, DEBUG=False

Now you understood why you have to set DEBUG=False and run in production or exposing to internet. So when DEBUG=True set, runserver will not serve static files like CSS, JavaScript files.

You have to set up a web server to serve static files. But for small scale Django application, you can use whitenoise, gunicorn. For my current blog, I am using the same configuration and SQLite database. It is working well for readers.

Secret Key

One important thing is that you have to remove your SECRET_KEY value from the file and set as environment variable.

SECRET_KEY = 'lkeq)cq&pIPACEDSOMEDUMMY80hnyVALsvwfwUEyvherecmlqkinDONTcr%KEEP9kmIN@cS$CeT'

SECRET_KEY should not be placed in the file for number of reasons. CSRF tokens in Django are generated based on the SECRET_KEY. 

  • Accidentally DEBUG=True is set, your key will be exposed in case of error.
  • If you share your project to some developer or git repo, it will be become security risk.
import os
SECRET_KEY = os.environ['SECRET_KEY']

If you have already shared with someelse and want to change it now. You can follow below steps to generate new SECRET_KEY from Django management utils.

from import get_random_secret_key

Allowed host

You have to set ALLOWED_HOSTS in the


Adding Gunicorn

Gunicorn is a python HTTP WSGI server. This is an application server which executes your view and send response back. Gunicorn is based on pre-fork or master-slave model where master controls N no. of workers which handles the incoming requests.

Install gunicorn with below pip command.

(blog)   thuruthuru git:(development) pip install gunicorn

Gunicorn listens to 8000 port and creates one worker and one master by default. But you can bind with -b :80 or --bind :80 to listen 80 port and assign workers with -w 2 or --workers=2. As workers are different processes, it should be carefully assigned. You can explore configuration in officials docs. But below command will get the job done for basic server.

(blog)   thuruthuru git:(development) gunicorn thuruthuru.wsgi -b :80 -w 2

[2020-05-04 02:52:39 +0530] [41797] [INFO] Starting gunicorn 20.0.4

[2020-05-04 02:52:39 +0530] [41797] [INFO] Listening at: (41797)

[2020-05-04 02:52:39 +0530] [41797] [INFO] Using worker: sync

[2020-05-04 02:52:39 +0530] [41800] [INFO] Booting worker with pid: 41800

Application access after gunicorn setup

After running your Django application with Gunicorn, try accessing your website Your website is disoriented, no static files and images are loaded. That is because gunicorn will not serve them. It is application server not a web server like Nginx.

Gunicorn is designed to act as application server which executes your application code to perform requested task and give results back to web server. Then web server serves to client.

As we are focusing on minimal production setup, we will use whitenoise to serve static files from application server (gunicorn) itself.

Adding Whitenoise

Whitenoise is simple package which facilitates serving static files from application server. It has compression storage enabled out of the box to serve the request. Install whitenoise using below pip command.

(blog)   thuruthuru git:(development) pip install whitenoise

Adding whitenoise middleware

Add whitenoise middleware to Django middleware in the file.

MIDDLEWARE += ['whitenoise.middleware.WhiteNoiseMiddleware']

Adding whitenoise storage

If you wish to add compression to your static files, add below line. You can skip if you don't want it.


Static root and URL

Make sure that you have static URL and static root is set in the files.

  • STATIC_ROOT - This is the place where static files will be collected by Django collectstatic process.
  • STATIC_URL - This is the URL path which will be referred in the application. Example: logo.png can be served from your development or production. But URL remains same as static.

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


As the name implies, It collects static files from all the INSTALLED_APPS to STATIC_ROOT directory which we configured above to serve.

(blog)   thuruthuru git:(development) python collectstatic

Application access after whitenoise setup

Now, the application will serve static files in the production mode. You see the weird alphanumeric value happened into the static files. That is MD5 hash value for versioning the static files. This is the result of adding whitenoise storage which compresses and does versioning. 

If you modify app.css and run collectstatic, next time you will see different hash for app.css. It will help to load the latest file if the file is cached at client side.

Changing admin URL

This is just for fun. If you have admin URL for your application and not comfortable to exposing admin URL like this, you can generate it with random string.

from import utils
8, 'abcdefghijklmnopqrstuvwxyz')
Update the result string in your If someone tries to access admin URL, it will not work.


Thanks for reading.!

Please comment if you have any questions or stuck somewhere with this post. Happy coding.!

Related posts
Managing media and static files in Django using S3

Managing media and static files in Django using S3

Durai Pandian May 10, 2020

Managing Django media files and static files from AWS S3 using django-storages instead of serving them from the same ser...
Continue reading...
Deploy python django app in heroku app

Deploy python django app in heroku app

Durai Pandian Mar 28, 2020

Deploying Django application in free Heroku using heroku-cli, git repository and setting environment variables to Heroku...
Continue reading...

We'll never share your email with anyone else.