Create a Docker Multi-Stage Build for Optimized Production Images
Generate an optimized multi-stage Dockerfile with cached dependencies, minimal runtime image, security hardening, and health checks.
๐ The Prompt
Write an optimized multi-stage Dockerfile for a [APPLICATION_TYPE] application built with [LANGUAGE_AND_FRAMEWORK]. The application requires [BUILD_DEPENDENCIES] for compilation and [RUNTIME_DEPENDENCIES] at runtime. The source code entry point is [ENTRY_POINT_FILE].
**Stage Requirements:**
1. **Stage 1 โ Dependencies (`deps`)**:
- Use `[BUILD_BASE_IMAGE]` as the base (e.g., node:20-alpine, golang:1.22-bookworm, python:3.12-slim)
- Set `WORKDIR /app`
- Copy only dependency manifests ([MANIFEST_FILES] e.g., package.json + lock file, go.mod, requirements.txt)
- Install dependencies with [INSTALL_COMMAND] using cache mount (`--mount=type=cache`) for faster rebuilds
2. **Stage 2 โ Build (`builder`)**:
- Copy from `deps` stage
- Copy full source code
- Run [BUILD_COMMAND] to produce optimized output in [OUTPUT_DIRECTORY]
- Run tests with `RUN [TEST_COMMAND]` (optional, controlled by build arg `RUN_TESTS=true`)
3. **Stage 3 โ Production (`runtime`)**:
- Use a minimal base image: `[RUNTIME_BASE_IMAGE]` (e.g., alpine:3.19, distroless, scratch)
- Create a non-root user: `appuser` with UID [UID]
- Copy ONLY the built artifacts from `builder` stage using `COPY --from=builder`
- Copy runtime configs: [CONFIG_FILES]
- Set environment variables: [ENV_VARS]
- Add `HEALTHCHECK` instruction: `CMD [HEALTH_COMMAND]` with interval=[INTERVAL] timeout=[TIMEOUT]
- `EXPOSE [PORT]`
- Use `USER appuser`
- Set `ENTRYPOINT` and `CMD` with exec form
4. **Security & Best Practices**:
- Add `.dockerignore` file content excluding [IGNORE_PATTERNS]
- Use `LABEL` for OCI image metadata (maintainer, version, description)
- Pin all base image versions with SHA digests for reproducibility
- Minimize layers by combining related RUN commands
Include comments explaining why each optimization matters. Provide the expected final image size estimate compared to a naive single-stage build.
๐ก Tips for Better Results
Always copy dependency manifests before source code to maximize Docker layer caching โ this prevents reinstalling dependencies on every code change. Use distroless or scratch images for compiled languages like Go or Rust to achieve the smallest possible image. Include a .dockerignore file to prevent secrets, git history, and node_modules from entering the build context.
๐ฏ Use Cases
Backend and DevOps engineers use this when containerizing applications for production, aiming to reduce image size, improve build speed through layer caching, and follow container security best practices.