- 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
- 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
- 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
- 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
- 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
- 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)
}
}
}
- 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:
- create environment files:
.env.localfor local development.env.prodfor production settings
to run:
docker compose up
access the application at localhost:8012