Hot Reload Implementation for Django Staging Environment
Learn how to implement hot reloading for Django applications in a staging environment with Docker Compose and Gunicorn for improved development experience.
This guide describes the implementation of hot reloading for a Django application in a staging environment with Docker Compose. Hot reloading enables automatic application refreshes when code changes are detected, significantly improving the development experience by eliminating manual restarts.
Architecture Components
The hot reload implementation consists of several key components:
- Docker Compose volume mapping - To sync local files with the container
- Gunicorn configuration - With reload mode enabled
- Django settings - Optimized for development/staging
- Start script - For proper initialization and monitoring
- Traefik configuration - For routing HTTP traffic without forcing HTTPS
- Dependencies - Required packages for hot reloading
Docker Compose Configuration
The staging.yml file contains the configuration for the Docker Compose environment:
version: "3"
volumes:
staging_postgres_data_backups: {}
staging_postgres_data: {}
staging_traefik: {}
services:
django: &django
build:
context: .
dockerfile: ./compose/staging/django/Dockerfile
image: my_awesome_project_staging_django
depends_on:
- postgres
env_file:
- ./.envs/.staging/.django
- ./.envs/.staging/.postgres
command: /start
volumes:
- .:/app:rw # Key configuration for hot reloading - maps local files to container
The crucial part for hot reloading is the volume mapping (- .:/app:rw), which ensures that any changes to the local codebase are immediately reflected within the container.
Gunicorn Configuration
Gunicorn is configured to watch for file changes and automatically reload:
# config/gunicorn.config.py
bind = "0.0.0.0:5000"
# Sample Worker processes
workers = 6
try:
import uvicorn
worker_class = "uvicorn.workers.UvicornWorker"
except ImportError:
worker_class = "sync"
timeout = 180
max_requests = 1
max_requests_jitter = 5
reload = True # Enable hot reloading
reload_extra_files = [] # Add any extra files to watch if needed
# Ignore specific file patterns for reloading
reload_ignore_files = [".*\.pyc", ".*\.py~", ".*\.log"]
The key settings for hot reloading are:
reload = True- Enables automatic reloading on file changesmax_requests = 1- Makes workers restart after handling a single requestmax_requests_jitter = 5- Adds randomness to restart times to prevent all workers restarting simultaneouslyreload_ignore_files- Patterns for files to ignore during hot reloading
Django Start Script
The start script (compose/staging/django/start) initializes the environment and launches Gunicorn:
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
echo "Starting Django application in staging mode..."
# Install watchdog if not already installed
if ! pip list | grep -q "watchdog"; then
echo "Installing watchdog..."
pip install watchdog
fi
# Set environment variables if not set
if [ -z "${USE_DOCKER:-}" ]; then
echo "Setting USE_DOCKER=yes"
export USE_DOCKER=yes
fi
# Check if Django settings module is properly set
if [ -z "${DJANGO_SETTINGS_MODULE:-}" ]; then
echo "Setting DJANGO_SETTINGS_MODULE=config.settings.staging"
export DJANGO_SETTINGS_MODULE=config.settings.staging
fi
echo "Using settings module: $DJANGO_SETTINGS_MODULE"
# Run initial setup
echo "Collecting static files..."
python /app/manage.py collectstatic --noinput || echo "Warning: collectstatic failed"
echo "Running migrations..."
python /app/manage.py migrate --noinput || echo "Warning: migrations failed"
echo "Starting application server with hot reload..."
echo "Hot reload will restart the server on code changes"
# Start Gunicorn with hot reloading
exec /opt/venv/bin/gunicorn config.asgi --config config/gunicorn.config.py --chdir=/app
This script:
- Ensures watchdog is installed
- Sets necessary environment variables
- Runs Django’s collectstatic and migrate commands
- Starts Gunicorn with the hot reload configuration
Django Settings
The config/settings/staging.py file contains the Django settings optimized for staging with hot reloading:
# Key settings for hot reloading
DEBUG = True
ALLOWED_HOSTS = ["*"] # Allow all hosts for staging
# CSRF Settings for local development
CSRF_TRUSTED_ORIGINS = ["http://localhost", "https://localhost", "http://0.0.0.0", "https://0.0.0.0"]
# Security settings relaxed for development
SECURE_PROXY_SSL_HEADER = None
SECURE_SSL_REDIRECT = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
# Development tools
INSTALLED_APPS += ["django_extensions"]
if "debug_toolbar" not in INSTALLED_APPS:
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
# For Docker usage
INTERNAL_IPS = ["127.0.0.1", "10.0.2.2", "0.0.0.0"]
if env("USE_DOCKER") == "yes":
import socket
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips]
The key settings include:
DEBUG = True- Enables detailed error pagesALLOWED_HOSTS = ["*"]- Accepts requests from any host- Security settings like
SECURE_SSL_REDIRECT = False- Prevents forced HTTPS redirection - Django extensions and debug toolbar for development
Traefik Configuration
The Traefik configuration (compose/staging/traefik/traefik.yml) manages routing:
entryPoints:
web:
# http
address: ":80"
# Removed redirection to allow HTTP access
http:
routers:
# HTTP router for direct access
web-router:
rule: "Host(`localhost`) || Host(`0.0.0.0`)"
entryPoints:
- web
service: django
Key changes:
- Removed forced HTTPS redirection from the web entrypoint
- Added a direct HTTP router for localhost and 0.0.0.0
Requirements
The dependencies required for hot reloading are specified in requirements/staging.txt:
# PRECAUTION: avoid production dependencies that aren't in development
-r production.txt
# Development tools
django-extensions==3.2.0
uvicorn[standard]==0.26.0
django-debug-toolbar
# Hot reloading
watchdog==3.0.0
Key packages:
uvicorn[standard]- For ASGI support with Gunicornwatchdog- For file system monitoringdjango-extensionsanddjango-debug-toolbar- For development tools
Environment Variables
The environment variables in .envs/.staging/.django configure Django:
# General
DJANGO_READ_DOT_ENV_FILE=False
DJANGO_SETTINGS_MODULE=config.settings.staging
DJANGO_SECRET_KEY=secret
DJANGO_ADMIN_URL=admin/
DJANGO_ALLOWED_HOSTS=localhost,localhost:5000
USE_DOCKER=yes
# Security
DJANGO_SECURE_SSL_REDIRECT=False
Key variables:
DJANGO_SETTINGS_MODULE=config.settings.staging- Use the staging settingsUSE_DOCKER=yes- Indicate we’re running in DockerDJANGO_SECURE_SSL_REDIRECT=False- Disable SSL redirect
Running the Setup
To use this hot reload setup:
-
Build the images:
docker-compose -f staging.yml build -
Start the containers:
docker-compose -f staging.yml up -
Access the application:
curl http://localhost -
Monitor logs for hot reload activity:
docker-compose -f staging.yml logs -f django
When you modify the code, you should see output in the logs indicating that workers are reloading:
[INFO] Worker reloading: /app/path/to/changed/file.py modified
Handling Database Changes
For database schema changes (e.g., adding models or fields):
- Make the code changes - they will be automatically detected
- Create migrations:
docker-compose -f staging.yml run django python manage.py makemigrations - Apply migrations:
docker-compose -f staging.yml run django python manage.py migrate
Troubleshooting
Common Issues and Solutions
-
403 Forbidden Errors
- Ensure
ALLOWED_HOSTSincludes your hostname - Check
CSRF_TRUSTED_ORIGINSincludes your access URL - Verify Traefik is not forcing HTTPS redirects
- Ensure
-
Hot reload not working
- Check logs for file change detection
- Ensure volume mapping is correct in docker-compose
- Verify
reload = Truein Gunicorn config
-
Static files not loading
- Run
collectstaticmanually - Check
STATIC_ROOTandSTATIC_URLin settings
- Run
-
Package missing errors
- Update requirements/staging.txt and rebuild
Conclusion
This hot reload implementation provides a productive development environment for Django in a staging-like Docker setup. It combines the benefits of containerization with the convenience of automatic code reloading, enabling developers to iterate quickly without manual restarts.
By correctly configuring Docker volumes, Gunicorn, Django settings, and Traefik, changes to the codebase are immediately reflected in the running application, with special handling for database schema changes through Django’s migration system.
That’s it! You have successfully implemented hot reloading for Django in a Docker staging environment. I hope you found this post helpful. If you have any questions or feedback, feel free to dm me on X or mail me at [email protected].
Related Posts
How to implement soft delete in Django
Learn how to implement soft delete functionality in Django using custom managers and model mixins.
Cloudflare Bought My Framework, So I Audited My Whole Stack
Cloudflare acquired Astro in January 2026. My site is Astro on GitHub Pages, behind Cloudflare DNS, with a Cloudflare Worker running its AI chat. An audit of what the acquisition and Astro 6 actually change for a real site, and why I'm not migrating yet.
My Claude Code Setup: Rule Files, Memory Plugins, and Two Personalities
A tour of my actual Claude Code configuration: layered CLAUDE.md rule files, a lazy-senior-dev mode called ponytail, a token-saving caveman mode, persistent memory plugins, and the skills that enforce discipline. With honest notes on what actually matters.