ADR 025: Container Strategy — Production, Development, and Orchestration¶
Status¶
Accepted
Context¶
Modern Python projects benefit from containerization for both development consistency and production deployment. However, "containers" can mean different things:
- Production containers — Minimal images that run your application
- Development containers — Full environments for writing code
- Container orchestration — Tools to manage multi-container setups
These serve different purposes and shouldn't be conflated. The project needed a clear strategy for when and how to use each approach.
Decision¶
We provide three container-related configurations, each serving a distinct purpose:
1. Containerfile (Production)¶
Located at the repository root, this builds a minimal OCI-compliant image containing only the installed application — no dev tools, no source code, no tests.
Use case: Deploying to production, CI/CD pipelines, distribution.
docker build -t simple-python-boilerplate -f Containerfile .
docker run --rm simple-python-boilerplate
2. Dev Container (Development)¶
Located in .devcontainer/, this configures VS Code to run inside a container with
all development tools pre-installed: Python, Node.js, pre-commit hooks, extensions.
Use case: Consistent development environment, onboarding new contributors, Codespaces.
3. Docker Compose (Orchestration)¶
Located at docker-compose.yml, this provides convenience commands for building and
running the production container locally. It can also define multi-service setups
(app + database, etc.).
Use case: Local testing of production build, multi-container development.
Alternatives Considered¶
Single Dockerfile for everything¶
Use one Dockerfile with build arguments to switch between dev and production modes.
Rejected because: Over-complicates the Dockerfile, makes each mode harder to understand, and dev containers have their own JSON format that integrates with VS Code features.
Skip Docker Compose¶
Just use docker build and docker run commands directly.
Rejected because: Compose provides a declarative, version-controlled way to specify build/run options. It's also the foundation for multi-service setups if the project grows.
Use Docker-in-Docker for dev container¶
Run Docker inside the dev container for full container workflow testing.
Rejected because: Adds complexity and security considerations. Users who need to test container builds can exit the dev container and run Docker on the host.
Consequences¶
Positive¶
- Clear separation of concerns — each file has one purpose
- Template users can delete what they don't need
- Zero-setup development via Codespaces or Dev Containers
- Production images stay minimal (~150MB vs ~1GB+ for dev)
- Docker Compose enables easy multi-service expansion
Negative¶
- Three files to maintain instead of one
- Users must understand which tool serves which purpose
- Dev container requires Docker Desktop (or Podman) installed
Mitigations¶
- Clear README files explain each component
- This ADR documents the rationale
- Files are optional — template users can remove any they don't use
Implementation¶
- Containerfile — Production container build
- .devcontainer/devcontainer.json — VS Code dev container config
- .devcontainer/README.md — Dev container documentation
- docker-compose.yml — Container orchestration
References¶
- ADR 019: Containerfile — Original production container decision
- Dev Containers specification
- Docker Compose documentation
- GitHub Codespaces