Setting up Django for Deployment: Gunicorn

When developing a Django web application, you normally use

python manage.py runserver

to start the built-in Django web server and run it on the default port of 8000. This development server automatically restarts when code changes, and is easy to run without any configuration. This is perfect for a development server, so if you’re looking to get up and running with Django, use the development server. The downside of using this server is that it is not built for production. It is slow, and it cannot handle many requests at a time.

To deploy your web application to production you need a production ready Web Server like Apache or NGINX. These web servers typically cannot communicate with Python applications directly. So the first step is to install and configure a WSGI server. The Web Server Gateway Interface (WSGI) is a standard for allowing web applications written in Python to communicate with HTTP Web servers.

Gunicorn is an example of a WSGI server. In this article I will show you how to install, configure and run gunicorn with a Django application.

Virtual Environment

The first step is to setup your environment by creating a virtual environment and installing Django and Gunicorn. I use Linux and my favourite way to create a virtual environment is to use the built in venv module:

$ sudo apt-get install python3-venv
$ python3 -m venv .venv
$ source .venv/bin/activate

Once the virtual environment is created and activated, install Django and Gunicorn:

$ pip install django
$ pip install gunicorn

Next, create a new Django project called myproject, note the period(“.”) at the end:

$ django-admin startproject myproject .

Now, confirm that you can run the project using the gunicorn server:

gunicorn myproject.wsgi

Gunicorn takes an argument that is the name of the application it should run. For Django projects, point it to the wsgi file in the project directory. You’ll see output similar to this:


[2021-06-03 21:48:52 +0200] [8618] [INFO] Starting gunicorn 20.1.0
[2021-06-03 21:48:52 +0200] [8618] [INFO] Listening at: http://127.0.0.1:8000 (8618)
[2021-06-03 21:48:52 +0200] [8618] [INFO] Using worker: sync
[2021-06-03 21:48:52 +0200] [8620] [INFO] Booting worker with pid: 8620

Open your browser and navigate to http://127.0.0.1:8000. If everything worked as expected, you should see this familiar screen:

Configuring Gunicorn

In the last section, you saw that it is possible to run gunicorn from the command-line without many arguments. In reality, you will want to run gunicorn with parameters to control things such as logging, debugging, SSL, and the number of workers to use. While it is possible to pass many of these parameters to the gunicorn script at the command-line, it is easier to do this via a configuration file.

To create a gunicorn config file, you can create a directory named config in your project root(same level as manage.py) and in it, create a file with a .py file extension and call it anything you like. The convention is to name it gunicorn.conf.py. This is where you’ll add any valid settings or parameters you want to use to configure the gunicorn server.

I have my project configured this way:

# gunicorn.conf.py

command = "/home/vndlovu/django/.venv/bin/gunicorn"
pythonpath = "/home/vndlovu/django/myproject"
bind = "0.0.0.0:8000"
workers = 3

The first setting, command is a path to the gunicorn executable. Gunicorn gets installed in the virtual environment in the bin folder by default. The next line specifies a path to add to the Python path. This is basically a path to your working directory. The bind setting tells gunicorn what IP and Port it should listen to. You can also bind gunicorn to a unix socket here. The last setting is the number of worker processes that gunicorn should use for handling requests.

Using these new settings, you can now run gunicorn this way:

$ gunicorn -c conf/gunicorn.conf.py myproject.wsgi

Conclusion

This post showed how to install, configure and serve up applications using gunicorn. Gunicorn is useful for translating HTTP requests into Python but it isn’t enough. In most cases, you would want to have a web server acting as the entrance point to the public. The web server will handle HTTP requests. In the next article in this series, I will show you how to setup the Nginx Web Server.