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 annginx.conf
file to configureNginx
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 theapp
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 Django , Docker , Docker-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