Dockerize Django with Nginx and PostgreSQL
By: Sabbir Ahmed
Tags: Postgres, Django, Nginx, Docker, Python

Create a .env file at the root (the same directory that contains docker-compose.yml) and edit as following -

SECRET_KEY="YOUR SECRET KEY"

DB_HOST="db"

DB_PASSWORD="hello" #from docker-compose.yml

DB_USER="postgres" #from docker-compose.yml

DB_NAME="postgres" #from docker-compose.yml

DB_PORT="5432" #from docker-compose.yml

DB_ENGINE="django.db.backends.postgresql"

The system will be up and running with those two following commands docker-compose build

This will download all the dependencies and docker images from the docker hub

docker-compose up -d

Last we have to copy the static files to our specified directory at settings.py, here’s how we’ll do it,

docker-compose exec web python manage.py collectstatic --no-input

Hopefully, now your project will be up and running by now and you can access it at localhost:8008. If not use docker-compose logs -f for debugging purposes.

The Design/Thinking Process

Basically, the problem is to run a Django app inside docker in a production setup, means with Nginx as HTTP server, Postgresql as DB server. Here’s my plan — I will run the App, the HTTP server, and the DB server in separate containers makes it more manageable and robust. The App will be run on Gunicorn, later will user Nginx’s proxy pass. We need a shared volume for the HTTP server and the App for static and media file hosting and a persistent filesystem for DB. All the secrets are going to be stored in a totally separated .env file.

Let’s see the web service:

web:

build: .

container_name: test_deploy_web

command: gunicorn app.wsgi:application --bind 0.0.0.0:8000

volumes:

- ./app:/app/

- staticfiles:/app/static/

expose:

- 8000

env_file:

- ./.env

depends_on:

- db

the second line ‘build .’ means it will create a docker image from the root Dockerfile, let’s take a look at the file,

FROM python:latest

ENV PYTHONDONTWRITEBYTECODE 1

ENV PYTHONUNBUFFERED 1

RUN mkdir /app

WORKDIR /app

RUN pip install --upgrade pip

COPY requirements.txt .

RUN pip install -r requirements.txt

COPY ./app /app

Farley simple enough to understand. Let’s move on to the next phase, the DB

db:

image: postgres

container_name: test_deploy_db

volumes:

- postgres_data:/var/lib/postgresql/data/

environment:

- POSTGRES_DB=postgres

- POSTGRES_USER=postgres

- POSTGRES_PASSWORD=hello

We will just use the official Postgres docker image and postgres_data is the persistent data volume within docker. It should suffice.

Now, Nginx. You can do whatever you want with it. Nginx really brings you the ultimate power.

nginx:

build: ./nginx

container_name: test_deploy_nginx

volumes:

- staticfiles:/app/static/

ports:

- 8008:80

depends_on:

- web

So, let’s dig a bit with this compose configs. The second line indicates it will compile whatever Dockerfile is in the ‘nginx’ folder. Let’s follow the clue -

FROM nginx:1.19.0-alpine

RUN mkdir /app

RUN rm /etc/nginx/conf.d/default.conf

COPY nginx.conf /etc/nginx/conf.d/

WORKDIR /app

Wow, that’s easy. Simply, We just deleted the default config file and replaced it with our own. The question is what is in our config file, well let’s see a bit simplified version -

upstream app {

server web:8000;

}

listen 80;

location / {

proxy_pass

}

location /static/ {

alias /app/static/;

}

} server {listen 80;location / {proxy_pass http://app; location /static/ {alias /app/static/;

Upstream configure proxy request server for Nginx. Gunicorn is a Python WSGI server for our Django app. In the first location derivative, we proxy passed the ‘/’ root location from the app to the HTTP server. The second location derivative is just an alias for static files. Now, we can get creative with the Nginx server.

This is it, I guess. Let me know if I missed anything. The Readme has more detailed commands. Feel free to alter the codes, And I will be checking Github issues, see you there.