Deploying Django app with Docker, Docker-Compose and Certbot

Deploying a Django application using Docker Compose with Nginx, Gunicorn, PostgreSQL, and Certbot.

Deploying a Django application with Docker Compose simplifies the configuration and management of complex, containerized environments. This tutorial demonstrates how to set up a Django app with a PostgreSQL database, reverse proxy with Nginx, SSL certificate management via Certbot, and an application server using Gunicorn.

Project Structure

  • Let’s create the following project structure to organize the Docker configuration files
my_project/
├── app/
│   └─ config
│   ├── settings.py
│   ...
├── nginx/
│   ├── nginx.conf
│   ...
├── certbot/
│   ├── www/
│   ├── conf/
│   ...
├── docker-compose.yml
├── Dockerfile
└── .env

Define Environment Variables

  • Create .env file in root directory and add the sensitive information like database username, password, etc.

.env

POSTGRES_DB=mydatabase
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
DB_HOST=db
DB_PORT=5432

DJANGO_SECRET_KEY=mysecretkey
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=mydomain.com

Configure Dockerfile for Django

  • Create the file named Dockerfile in root directory and add configuration.

Dockerfile

FROM python:3.9
WORKDIR /app
COPY ./app/requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app/ /app/
RUN python manage.py collectstatic --noinput
CMD ["gunicorn", "--config", "gunicorn.conf.py", "my_project.wsgi:application"]

Configure Nginx

  • In the nginx directory, create an nginx.conf file to configure Nginx as a reverse proxy

nginx/nginx.conf

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;
    gzip on;

    server {
        listen 80;
        server_name mydomain.com;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        server_name mydomain.com;

        ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem;

        location / {
            resolver 127.0.0.11;
            set $web web:8000;
            proxy_pass http://$web;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /static/ {
            alias /app/staticfiles/;
        }

        location /media/ {
            alias /app/media/;
        }
    }
}

Replace mydomain.com with your actual domain name.

Configure Gunicorn

  • Create a gunicorn.conf.py file in the app directory

app/gunicorn.conf.py

import multiprocessing

bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1
loglevel = "info"
accesslog = "/usr/src/app/logs/gunicorn_access.log"
errorlog = "/usr/src/app/logs/gunicorn_error.log"
max_size = 10 * 1024 * 1024  # 10 MB
# log rotation
access_log_max_size = max_size
error_log_max_size = max_size

Configure Docker Compose

  • Create a docker-compose.yml file in the project root

docker-compose.yml

version: '2'

services:
  db:
    image: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped
    env_file: ".env"
    networks:
      - django_network

  web:
    container_name: django-web
    build: .
    command: bash -c "gunicorn --config gunicorn.conf.py config.wsgi:application"
    volumes:
      - ./app:/app
    ports:
      - "8000:8000"
    depends_on:
      - db
    restart: unless-stopped
    env_file: ".env"
    networks:
      - django_network

  nginx:
    image: nginx:latest
    volumes:
      - ./app/media/:/app/media/
      - ./app/staticfiles/:/app/staticfiles/
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./certbot/www:/var/www/certbot:ro
      - ./certbot/conf:/etc/letsencrypt:ro
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - certbot

  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./certbot/www:/var/www/certbot
      - ./certbot/conf:/etc/letsencrypt
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

  certbot-init:
    image: certbot/certbot:latest
    container_name: certbot-init
    volumes:
      - ./certbot/www:/var/www/certbot
      - ./certbot/conf:/etc/letsencrypt
    entrypoint: "/bin/sh -c 'certbot certonly --webroot --webroot-path=/var/www/certbot --email [email protected] --agree-tos --no-eff-email --force-renewal -d mydomain.com'"

networks:
  django_network:
    external: true

volumes:
  postgres_data:

Build docker image and deploy

  • Run the following command to build and deploy the services:
docker-compose up -d --build
  • This will start all containers and serve the Django application on the domain specified in the Nginx configuration.

Test and verify the app

  • Visit your domain to ensure the application is live.
  • Check that SSL certificates are generated in /etc/letsencrypt.

Conclusion

  • we now have a Dockerized Django application running in production with Nginx as a reverse proxy, Certbot for SSL, and PostgreSQL as the database.
  • Deploying Django with Docker Compose brings consistency and ease to the deployment process, enhancing both security and scalability.

References:

  • https://docs.djangoproject.com/en/stable/howto/deployment/
  • https://docs.docker.com/compose/
  • https://docs.gunicorn.org/en/stable/configure.html
  • https://nginx.org/en/docs/

Thank you for reading the Agiliq blog. This article was written by Anjaneyulu Batta on Nov 11, 2024 in DjangoDockerDocker-Compose .

You can subscribe ⚛ to our blog.

We love building amazing apps for web and mobile for our clients. If you are looking for development help, contact us today ✉.

Would you like to download 10+ free Django and Python books? Get them here