Build Django Images With Cloud Native Buildpacks
Build production Django container images without a Dockerfile using Cloud Native Buildpacks and the pack CLI: choose a builder, configure Python version and start command with a Procfile, run collectstatic, rebase images for OS patches, and use it in CI.
Problem statement
Writing and maintaining a Dockerfile for every Django service means keeping base images patched, layers ordered for caching, users non-root, and builds reproducible. Cloud Native Buildpacks (CNB) turn source code into OCI images automatically: they detect Python, install dependencies, set a start command, and produce images you can rebase onto a patched OS layer without rebuilding. Heroku, Google Cloud Run source deploys, and several platforms use them internally; with the pack CLI you can use them anywhere.
- Install the
packCLI and pick a builder, such asheroku/builder:24orpaketobuildpacks/builder-jammy-base. - Pin Python with
.python-version(or the method your builder documents) and keeprequirements.txt, Poetry, or uv lock files in the repo. - Add a
Procfilewithweb: gunicorn config.wsgi --bind 0.0.0.0:${PORT:-8000}. - Build:
pack build ghcr.io/acme/shop:$SHA --builder heroku/builder:24 --publish. - Apply OS security patches without rebuilding:
pack rebase ghcr.io/acme/shop:$SHA --publish.
Step-by-step solution
1. Install pack and choose a builder
brew install buildpacks/tap/pack # or download the release binary
pack builder suggest
A builder bundles buildpacks with a build image and a run image. Two common choices for Python:
- Heroku builder (
heroku/builder:24): same Python buildpack as Heroku, supports pip, Poetry, and uv. - Paketo builder (
paketobuildpacks/builder-jammy-base): CNCF community buildpacks, supports pip, Pipenv, Poetry, and Conda.
2. Prepare the project
.python-version # e.g. 3.12
requirements.txt # or poetry.lock / uv.lock
Procfile
# Procfile
web: gunicorn config.wsgi --bind 0.0.0.0:${PORT:-8000} --workers 3
worker: celery -A config worker -l info
The Procfile defines process types; the web type becomes the image's default command, and other types are available as alternative entrypoints (for example --entrypoint worker in Docker, or the launcher with the process name).
3. Build and run collectstatic
The Heroku Python buildpack runs collectstatic automatically when it finds Django, unless DISABLE_COLLECTSTATIC=1. Paketo does not by default; add a build step through a project.toml or run it at container start. Settings must import without runtime secrets during the build:
pack build ghcr.io/acme/shop:$(git rev-parse --short HEAD) \
--builder heroku/builder:24 \
--env DJANGO_SETTINGS_MODULE=config.settings.build \
--publish
--publish pushes straight to the registry, which is faster in CI and keeps the layer cache in the registry.
4. Run the image
docker run --rm -p 8000:8000 --env-file .env ghcr.io/acme/shop:3f2a1c9
docker run --rm --env-file .env --entrypoint worker ghcr.io/acme/shop:3f2a1c9
docker run --rm --env-file .env --entrypoint launcher ghcr.io/acme/shop:3f2a1c9 python manage.py migrate
Images run as a non-root user by default, which suits Kubernetes and most platforms.
5. Patch the OS with rebase
When the run image gets security updates, rebase swaps the OS layers under your app layers without running the build again:
pack rebase ghcr.io/acme/shop:3f2a1c9 --publish
This takes seconds because nothing is recompiled. Schedule it, or rebuild regularly from CI, so images do not age with known vulnerabilities.
Explanation
The CNB lifecycle runs in phases: detect chooses buildpacks based on files in the repo, build lets each buildpack contribute layers (Python, dependencies, app), and export writes an OCI image with those layers on top of a run image. Because layers are created by buildpacks with stable boundaries, the OS layer can be replaced later with rebase. The approach standardizes images across services and teams, at the cost of less control than a Dockerfile.
Edge cases / notes
- System packages. Default builders do not install apt packages. Libraries like GDAL require custom buildpacks, an apt buildpack, or a Dockerfile.
- Build-time settings. Use a dedicated build settings module so
collectstaticdoes not need database or secret variables. - Image size. Buildpack images are typically larger than a tuned slim Dockerfile; acceptable for most platforms.
Next steps
- Compare with a handwritten image in Dockerize Django for Production.
- Platforms that build with buildpacks: Deploy Django on Google Cloud Run.
- Automate builds with Deploy Django With GitHub Actions CI/CD.
FAQ
Do buildpacks work with Poetry or uv?
The Heroku Python buildpack supports pip, Poetry, and uv; Paketo supports pip, Pipenv, Poetry, and Conda. Check the builder's documentation.
How do I run migrations with a buildpack image?
Use the launcher entrypoint: --entrypoint launcher image python manage.py migrate, or a Procfile process type.
What does pack rebase do?
It replaces the OS base layers of an image with an updated run image, without rebuilding your app layers.
Can I add system packages?
Not with the default Python buildpacks. Use an apt buildpack, a custom buildpack, or a Dockerfile.