django
Markdown

docker full

  1. Prerequisites Setup:
  • Create/update requirements.txt
  • for uv:
uv add gunicorn
uv pip compile pyproject.toml -o requirements.txt
gunicorn==23.0.0  # Add this line
  1. Update settings.py:
  • Add environment variable handling:
from environ import Env  # type: ignore
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Initialize environ
env = Env()

# Read .env file with explicit path
env.read_env(os.path.join(BASE_DIR, ".env"))

# Set environment
ENVIRONMENT = env("ENVIRONMENT", default="production")

# Required settings
SECRET_KEY = env('SECRET_KEY', default='fallback-value')
DEBUG = env.bool('DEBUG', default=False)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])
CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=[])

# Static files configuration
STATIC_URL = 'static/'
STATIC_ROOT = 'static/'

The .env.prod example is


# Django Settings
DEBUG=False
DJANGO_SECRET_KEY=
DJANGO_ALLOWED_HOSTS=localhost
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:8001

# Database Settings
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=dockerdjango
DATABASE_USERNAME=dbuser
DATABASE_PASSWORD=dbpassword
DATABASE_HOST=db
DATABASE_PORT=5432

# Postgres Settings
POSTGRES_DB=dockerdjango
POSTGRES_USER=dbuser
POSTGRES_PASSWORD=dbpassword
  1. Create entrypoint.pr.sh
  • ( note: mysite is your app name):
python manage.py collectstatic --noinput
python manage.py migrate
gunicorn --bind :8000 --workers 3 mysite.wsgi:application
  1. Create .dockerignore:
.env
.env*
# Add other development files to ignore
```

- example template:
```
# Byte-compiled / optimized / DLL files

**pycache**/
_.py[cod]
_$py.class

# C extensions

\*.so

# Distribution / packaging

.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
_.egg-info/
.installed.cfg
_.egg
MANIFEST

# PyInstaller

# Usually these files are written by a python script from a template

# before PyInstaller builds the exe, so as to inject date/other infos into it

_.manifest
_.spec

# Installer logs

pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports

htmlcov/
.tox/
.nox/
.coverage
.coverage._
.cache
nosetests.xml
coverage.xml
_.cover
\*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations

_.mo
_.pot

# Django stuff

\*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
static

# pyenv

# For a library or package, you might want to ignore these files since the code is

# intended to run in multiple environments; otherwise, check them in

# .python-version

# pipenv

# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control

# However, in case of collaboration, if having platform-specific dependencies or dependencies

# having no cross-platform support, pipenv may install dependencies that don't work, or not

# install all needed dependencies

# Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow

**pypackages**/

# Celery stuff

celerybeat-schedule
celerybeat.pid

# Environments

.env
.env\*
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# mkdocs documentation

/site

# mypy

.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker

.pyre/

# pytype static type analyzer

.pytype/

# Cython debug symbols

cython_debug/

# Dependency directories

node_modules/

# VS Code

.vscode
  1. Create Dockerfile:
# Stage 1: Base build stage
FROM python:3.13-slim AS builder

RUN apt-get update && apt-get install -y \
  libpq-dev \
  gcc \
  && rm -rf /var/lib/apt/lists/*

# Create the app directory
RUN mkdir /app

# Set the working directory
WORKDIR /app

# Set environment variables to optimize Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Install dependencies first for caching benefit
RUN pip install --upgrade pip
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt

# Stage 2: Production stage
FROM python:3.13-slim

RUN apt-get update && apt-get install -y \
  libpq5 \
  && rm -rf /var/lib/apt/lists/*

RUN useradd -m -r appuser && \
  mkdir /app && \
  chown -R appuser /app

# Copy the Python dependencies from the builder stage
COPY --from=builder /usr/local/lib/python3.13/site-packages/ /usr/local/lib/python3.13/site-packages/
COPY --from=builder /usr/local/bin/ /usr/local/bin/

# Set the working directory
WORKDIR /app

# Copy application code
COPY --chown=appuser:appuser . .

# Set environment variables to optimize Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Switch to non-root user
USER appuser

# Expose the application port
EXPOSE 8000

# Make entry file executable
RUN chmod +x  /app/entrypoint.prod.sh

# Start the application using Gunicorn
CMD ["/app/entrypoint.prod.sh"]

To test

docker build . -t image-name
  1. Create nginx.conf:
# Sets the max number of simultaneous connections that can be opened by a worker process
events {
   worker_connections 1024;
}

http {
   server {
       include mime.types;
       # for css
       default_type application/octet-stream;
      types {
        text/css css;
        text/javascript js;
      }
       sendfile on;
       keepalive_timeout 65;
       listen 80;

       # Requests to /static/ are served directly from the /static/ directory
       location /static/ {
           alias /static/;
           expires 7d;
       }

       # Configuration for serving media files
       # location /media/ {
       #     alias /home/app/web/mediafiles/;
       # }

       # Handles all other requests
       location / {
           # Forward requests to Django application
           proxy_pass http://django-web:8000;

           # Pass important headers to Django for proper request handling
           proxy_set_header Host $host;                          # Original host header
           proxy_set_header X-Real-IP $remote_addr;             # Client's real IP
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  # Chain of IP addresses
           proxy_set_header X-Forwarded-Proto $scheme;          # Original protocol (http/https)
       }
   }
}
  1. create docker-compose.yml:
services:
  db:
    image: postgres:17
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env.prod

  django-web:
    build: .
    container_name: django-docker
    depends_on:
      - db
    volumes:
      - ./static:/app/staticfiles
    env_file:
      - .env.prod

  frontend-proxy:
    image: nginx:latest
    ports:
      - "8001:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./static:/static:ro
    depends_on:
      - django-web
volumes:
  postgres_data:
  1. create environment files:
  • .env.local for local development
  • .env.prod for production settings

to run:

docker compose up

access the application at localhost:8012