Docker
Docker Compose for local development, and the production images.
Local Development
Build & Start
# Build all services
docker compose -f docker-compose.local.yml build
# Start all services
docker compose -f docker-compose.local.yml up
# Start in background
docker compose -f docker-compose.local.yml up -d
# Build without cache (after dependency changes)
docker compose -f docker-compose.local.yml build --no-cacheDjango Startup
The local Django container runs:
python manage.py migrate
python manage.py runserver_plus 0.0.0.0:8000Celery Worker (Hot Reload)
The local Celery worker uses watchfiles for automatic restart on code changes:
watchfiles --filter python celery.__main__.main \
--args '-A config.celery_app worker -l INFO'Management Commands
# Run any Django command
docker compose -f docker-compose.local.yml run --rm django python manage.py <command>
# Examples
docker compose -f docker-compose.local.yml run --rm django python manage.py createsuperuser
docker compose -f docker-compose.local.yml run --rm django python manage.py makemigrations
docker compose -f docker-compose.local.yml run --rm django python manage.py shell_plusDatabase Access
# PostgreSQL shell
docker compose -f docker-compose.local.yml exec postgres psql -U <POSTGRES_USER> -d <POSTGRES_DB>
# Check pgvector extension
SELECT * FROM pg_extension WHERE extname = 'vector';Service URLs
| Service | URL |
|---|---|
| Django API | http://localhost:8000 |
| Flower | http://localhost:5555 |
| Mailpit | http://localhost:8025 |
One server (Docker Compose)
docker-compose.production.yml runs everything on one server, with Traefik in front for HTTPS (Let's Encrypt), and builds the same images as AWS. It's an alternative these docs don't walk through. For production, AWS is the documented path.
The production images
compose/production/django/Dockerfile builds two images from one file, with Python 3.12:
| Target | Runs | Size | Why |
|---|---|---|---|
web | Django (gunicorn), Celery beat | about 1 GB | No Docling or PyTorch, so it starts fast and uses little memory |
worker | the Celery worker | about 4.3 GB | Docling, PyTorch (CPU) and the document models, for reading files |
docker build -f compose/production/django/Dockerfile --target web -t backend-web .
docker build -f compose/production/django/Dockerfile --target worker -t backend-worker .The tasks run on x86, so on Apple Silicon add --platform linux/amd64. On AWS, GitHub Actions builds and pushes both images; the worker's build cache lives in ECR.
- Static files are collected into the web image at build time and served by WhiteNoise, so nothing is uploaded at start-up.
- Build tools (
cmake,g++, for Docling) are only in the build stage. - The containers run as a non-root
djangouser, and translation messages are compiled during the build. - The entrypoint waits for PostgreSQL when
POSTGRES_HOSTis set. WithoutDATABASE_URL, the settings build it fromPOSTGRES_*.
Environment Files
Local (.envs/.local/)
.envs/.local/.django # Django, Redis, Celery settings
.envs/.local/.postgres # PostgreSQL credentialsOne server (.envs/.production/)
.envs/.production/.django.example # copy to .django: Django, AWS, Stripe, Sentry, email
.envs/.production/.postgres.example # copy to .postgres: PostgreSQL credentialsOn AWS there are no env files: Terraform sets the settings, and the keys live in Secrets Manager.
Health Checks
# Check all services are running
docker compose -f docker-compose.local.yml ps
# Check Django, its database and its cache
curl http://localhost:8000/healthz/ # {"status": "ok"}
# Check database connection
docker compose exec django python manage.py check --database default
# Check Celery worker
docker compose exec django celery -A config.celery_app inspect pingTroubleshooting
| Issue | Solution |
|---|---|
| Database connection refused | Wait for PostgreSQL to be ready, check credentials |
| Celery tasks not executing | Check Redis connection, verify worker is running |
| S3 upload failing | Verify AWS credentials and bucket permissions |
| Docling build failing | Ensure Python 3.12 (not 3.13), check cmake/g++ installed |
| Out of disk space | docker system prune -a to clean unused images |
| pgvector not available | Ensure using pgvector/pgvector:pg17 image |