Docker & deployment
Image structure
The Prefab Dockerfile uses a two-stage build:
build stage — based on python:<version>, compiles all native dependencies:
- Mozjpeg (compiled from source for high-quality JPEG encoding)
- Pillow (compiled against Mozjpeg, no pre-built binary)
- lxml
- All Python dependencies from
requirements.txtandrequirements-dev.txt
runtime stage — based on python:<version>-slim, contains only runtime libraries. Prefab is installed as a Python package from source. Python packages and compiled binaries are copied from the build stage.
The runtime stage sets DJANGO_SETTINGS_MODULE=prefab.settings.autoconfigure by default and runs Gunicorn on port 8080 with 2 workers × 4 threads.
ONBUILD pattern
The Prefab image uses Docker ONBUILD instructions so that downstream project images inherit the build behaviour automatically.
When a project does FROM prefab:latest, the following happens at the project's build time:
Build stage ONBUILD:
COPY requirements.txt .
RUN pip install -r requirements.txtRuntime stage ONBUILD:
COPY --from=build /usr/local/lib/python*/site-packages/ ...
COPY --from=build /usr/local/lib/lib*.so* ...
COPY --from=build /usr/local/bin/ ...
COPY --from=build /usr/local/include/ ...
COPY . /app/This means a minimal project Dockerfile is just:
FROM registry.gitlab.com/baksteen/prefab:latestThe project's requirements.txt is installed automatically during build, and all project files are copied into /app/.
Environment variables
| Variable | Default | Description |
|---|---|---|
APP | prefab | Python package name, used by autoconfigure to find settings |
ENVIRONMENT | (auto-detected) | Settings environment: development, test, production, review |
DJANGO_SETTINGS_MODULE | prefab.settings.autoconfigure | Can override autoconfigure entirely |
DJANGO_SECRET_KEY / SECRET_KEY | (insecure default) | Always set in production |
POSTGRES_DB | postgres | Database name |
POSTGRES_USER | postgres | Database user |
POSTGRES_PASSWORD | postgres | Database password |
POSTGRES_HOST | postgres | Database host |
POSTGRES_PORT | 5432 | Database port |
POSTGRES_CONN_MAX_AGE | 600 | Connection max age in seconds |
CACHE_LOCATION | redis://redis:6379/0 | Redis URL for Django cache |
CELERY_BROKER_URL | redis://redis:6379/1 | Redis URL for Celery broker |
AWS_ACCESS_KEY_ID | — | AWS credentials (production) |
AWS_SECRET_ACCESS_KEY | — | AWS credentials (production) |
AWS_STORAGE_BUCKET_NAME | — | S3 bucket name |
GCP_MEDIA_BUCKET_NAME | — | GCS media bucket |
GCP_STATIC_BUCKET_NAME | — | GCS static bucket |
Local development
make start # docker compose up
make stop # docker compose down
make bash # shell into the web container
make shell # Django shell
make superuser # create a superuser
make migrations # show, migrate, and make migrations
make test # run pytest
make lint # run flake8, isort, black
make lint-fix # auto-fix lint issuesThe docker-compose.yml mounts the local directory into /app/ and the local prefab/ package into the container's site-packages, so code changes take effect without rebuilding.
CI/CD (GitLab)
The pipeline is defined in .gitlab-ci.yml and includes shared pipeline templates from baksteen/gitlab-pipelines:
| Stage | Jobs |
|---|---|
quality | flake8 (allowed to fail), black |
build | Docker image build |
tests | pytest (runs on the build image) |
publish | Push image to registry |
deploy | Deploy to environment |
The pytest job uses $DOCKER_IMAGE_BRANCH_BUILD (the build stage image) since the runtime image's ONBUILD instructions require a project to be present.
Gunicorn configuration
The runtime image starts Gunicorn with:
--bind=:8080
--workers=2
--threads=4
--worker-class=gthread
--timeout=60
--worker-tmp-dir=/dev/shm/dev/shm is used as the worker temp directory to avoid issues with read-only filesystems in container environments.
Override the command in your project's docker-compose.yml or Kubernetes deployment to tune workers/threads for your workload.