Django Docker

  1. Deploying Django to AWS with Docker and Let's Encrypt In this tutorial, we'll deploy a Django app to AWS EC2 with Docker. The app will run behind an HTTPS Nginx proxy with Let's Encrypt SSL certificates. We'll use AWS RDS to serve our Postgres database along with AWS ECR to.
  2. This is a step-by-step tutorial that details how to configure Django to run on Docker with Postgres. For production environments, we'll add on Nginx and Gunicorn. We'll also take a look at how to serve Django static and media files via Nginx. Dependencies: Django v3.0.7; Docker v19.03.8; Python v3.8.3; Django on Docker Series.

Django is a Python-based web framework for model-template-view (MTV) based architectural patterns. It's open-source and is maintained by the Django Software Foundation. Django provides an easier path for developers to produce database-driven websites. There is lots of other cases where loaddata and dumpdata commands don't work. So pgdump and psql are good, but the downside of them is that you lose the database configuration that is stored in the project settings (or the environment if you are 12factor aware).

Getting your Django app on the internet is - pardon my language - a pain in the ass.


You need to daemonize your app with something like Supervisor, then you need to setup a production ready database, then you need to setup a web-server using the likes of Nginx or Apache. And when it's all done, you need to setup files and directories to collect and maintain logs from your app.

You could do all that, or you could use Docker.

Here's what you'll learn

In this tutorial you'll learn to deploy a Django app with Docker and docker-compose. By the end of the tutorial, you'll have a single command that you can use with any Django app to deploy it to a server.

The prerequisites

To complete this tutorial, you will need:

Django Docker Logs

  • Familiarity with using the command line.
  • Familiarity with Python virtual environments. We will use a virtual environment to isolate the dependencies of our project. To install the python virtual environment module use: pip install virtualenv.
  • A basic understanding of YAML files.

Step 0 — Installing Dependencies

To follow this tutorial, you will need both Docker and Docker-Compose on your machine. Follow the steps below depending on your operating system.

Installing on Ubuntu

You can install Docker Engine on your system by using the convenience script provided by Docker.

To install Docker-Compose, first download the binaries from the Github repo. Then apply executable permissions to the binary.

Installing on a Mac

Docker Desktop for Mac already includes Docker-Compose. To install Docker Desktop on Mac, download the package from here and run the installer.

Note: If the above steps don't work for you, you can go to this link and find more information depending on your operating system.

Step 1 — Setting Up The Django Project

For following along, you can make a sample Django project or follow the steps with your existing project. I suggest making a sample app and then implementing it on your existing project so that you can tweak things to suit your purpose.

Create a new virtual environment.

Install Django and make a new Django project.

List out the requirements in a requirements.txt file.

In the djangoproject/settings.py file, change the ALLOWED_HOSTS to the following:

Doing this allows us to access the Django app from outside the container.

Step 1 — Writing The Dockerfile

The first step to containerising our Django app is making a Dockerfile. In the djangoproject directory, make a new file named Dockerfile and put the following in it:

This Dockerfile is used to build the container for your Django app. Each line is a step of the build. We will make a lot of changes in this, but for now, it works.

Why you should copy the requirements.txt file before copying the code
While building the container, Docker caches stages of the build. So if you change your code but the requirements.txt file is the same, Docker doesn't have to install all the requirements again for building the container.

Step 2 — Writing the Docker-Compose File

To use Docker-Compose, we need to make a docker-compose.yaml file. In this file, we will define three different containers as services and then connect them with each other. Then we can use a single command to run our containers together. These services are:

  • the Django app container,
  • the database container and
  • the Nginx webserver container.

We will add each of these services to the docker-compose.yaml file one by one, starting with the web app. Make a new docker-compose.yaml file in the deployment-project directory with the following contents:

Above, we defined a service for the web app. Let's it breakdown and see what each option does:

  • container_name defines the hostname of the container. This can be used by other containers to refer to this container.
  • build takes the path of the Dockerfile directory.
  • command takes a command you can run in the container after start up. In this case, we run the Django server on all available interfaces at port 8000.
  • ports takes a list of ports to bind to the ports of your host machine. Above, we bind the port 8000 of the container to the port 8000 of your host. This means that you can access the Django app at localhost:8000 of your machine.

Let's test if everything works until now. Run the following command.

You should see the Django app running inside the container. The --build flag is used to tell Docker to build the container.

Step 3 — Setting Up The Database

Let's set up the database now. We will be using a PostgreSQL database for our app. To set this up, we will have to complete the following steps:

  • Add a database service to the docker-compose file.
  • Define environment variables for the PostgreSQL database.
  • Configure our Django app to use the database we just created. This includes connecting to the database, making migrations and migrating.

Change the docker-compose.yaml file to look like the following:

Again, a breakdown of the changes:

  • image tells docker which image to use. In this case, we tell it to pull whichever image is associated with the latest tag for postgres.
  • restart: always makes sure the database always restarts when it exits. Other options for this are no, on-failure and unless-stopped
  • env_file is used to put the environment variables in a file and tell Docker to initialize the container with those variables. We will create this file soon.
  • volumes is used for data persistency. It would be a shame if our database was empty every time we started it and destroyed its data everytime we turned it off. Docker uses volumes to store data that must be persistent on disk. Above, we define a new volume and tell docker to save the contents of /var/lib/postgresql/data inside that volume. Note that we have to define volumes in the volumes section in the bottom.
  • depends_on tells Docker that the web service depends on the db service.

Now, lets create the project.env file we mentioned above. Make a new file called project.env in the deployment-project directory with the following contents. We'll add more to this file later.

Django docker image

Next, we will configure the Django app to connect to the database. To connect to the database, Django needs to have a driver installed. For PostgreSQL, that driver is psycopg2.

You should install psycopg2-binary instead of psycopg2 if you want to install psycopg2 without install PostgreSQL on your system. Now, update the requirements.txt file

Now we will configure the Django settings. First, make a new file called keyconfig.py in the djangoproject/djangoproject directory. We will save credentials and other sensitive information here. The purpose of using a keyconfig instead of directly loading environment variables in settings.py is that you can separate parts of your config using classes. Add the following contents to the keyconfig.py file:

Open djangoproject/djangoproject/settings.py and import the keyconfig.py file like this:

Now, set the Secret key:

And the database:

Now, we want to makemigrations and migrate everytime the container starts up. But the problem is that the database container can take longer to initialise and if we run the above commands before the database is up and running, it can result in an error. So we need a way to make sure the database has started. For this, we use an entrypoint script. In the djangoproject directory, make a new file called entrypoint.sh with the following contents.

The above script first waits for the database to start up. We do this by using netcat to ping the server in a while loop. Then it makes migrations and performs the migrations. The --noinput flag is used when we are using the command inside a script so that it does not prompt the user for input. Next, we need to make the file executable so that Docker can run it.

Django docker nginx

Now change the Dockerfile to use this script. Add the following lines at the end of the Dockerfile:

We need to install netcat as it is not installed by default. Once again, let's test if everything is running. Go to the deployment-project directory and spin up the containers.

You can use the -d flag to make the containers run in the background.

Step 4 — Using Nginx to Serve the Django app

In this step, we will set up another container to use Nginx to serve our django project. For this, we will do the following:

  • Install and use gunicorn as our WSGI server
  • Configure nginx as a reverse proxy for the gunicorn server.
  • Add an nginx service to the docker-compose file.

Gunicorn is a production level WSGI server we will use to serve our Django project. To begin let's, install gunicorn.

To use gunicorn as our WSGI server, we use the following command:

Docker Django Mysql

In the above command, djangoproject.wsgi is the name of the WSGI module to use. The --bind takes address of the socker to bind to. In this case, we tell it to bind to port 8000 of all available interfaces. The --workers flag takes the number of workers to be initialized by gunicorn, we set it to 4 here.

Once again, update the requirements.txt file.

Next, let's make a configuration file for the nginx server.

Inside the deployment-project directory, make a directory called nginx.

Open the nginx.conf file and add the following:

Next, we add the nginx service to the docker-compose file and also use gunicorn for the Django app. Open the docker-compose.yaml file and change it to the following:

  • In the volumes section in nginx, we mount the local ./nginx directory to the /etc/nginx/conf.d directory in the container.
  • In depends_on, we say that the nginx service depends on the web service.
  • Instead of using ports in the web service, we use expose to expose the port 8000 of the container to other containers on the network. But, in the nginx service, we use ports to bind the port 80 of the container to the port 1337 of the host machine.
The difference between ports and expose
Use ports when you want to access the port from the host machine's localhost. Use expose when you want to expose the port to other containers in the network.

Again, test that everything is working by spinning up the containers.

Test that you can see the webserver by going to localhost:1337 in your webbrowser.

Step 5 — The Dockerfile, Revisited

Before configuring the staticfiles, we will take another look at the Dockerfile and restructure it. Previously, we were running the processes inside the Django container as a root user. But, according to the Docker documentation, the best practice is to run your processes as non-privileged user within the containers. So, in this step, we will restructure the Dockerfile and also make a new user to run our app inside the container.

Make a new Dockerfile with the following contents.

Copy and install the requirements.

Copy the code and make the app user the owner of the directory.

Change the user to app and run the entrypoint.

Test that everything works by spinning up the containers.

Verify that the server is running by going to localhost:1337 in your browser.

Step 6 — Serving Static Files with Nginx

We need to configure nginx to serve our staticfiles because Django does not serve staticfiles in production. First, we configure Django to use a staticfiles directory. Open settings.py and add the following in the staticfiles section:

We tell Django that the staticfiles are going to be served at the /static/ path. And that the files are going to be in the staticfiles directory.

Next, open the nginx.conf file and add the following:

We tell Nginx to serve the contents of the staticfiles directory at the /static/ path.

Django Docker File

Next, we need to edit the entrypoint script to collect the static files. Add the following at the end of the entrypoint.sh file.

To let Nginx access the staticfiles collected by Django, we will store them in a volume and add this volume to both the web and nginx services.

Django Docker Compose

And add this volume to the volumes section:

Check that the staticfiles have loaded by spinning up the containers and going to the admin page at localhost:1337/admin/ you should see the CSS loaded.


If you reached until here, you will have a ready to deploy Django app which will be up and running in just one command. You can use this configuration for your own Django apps and deploy them to a server with only a few extra steps.

The full code for this guide is available on Github here.