Multi-stage Dockerfile
Design a Dockerfile that separates build-time work from runtime execution, minimizes the final image, preserves reproducibility, and avoids leaking tools or secrets into production layers.
When to invoke
- "Create a multi-stage Dockerfile for this app."
- "Make this Docker image smaller and more secure."
- "Split the Dockerfile into builder and runtime stages."
- "Add .dockerignore, non-root USER, and HEALTHCHECK best practices."
Stage design
| Stage |
Purpose |
Common contents |
Must not contain |
deps |
Restore dependency manifests before source changes. |
package-lock.json, requirements.txt, .csproj, lockfiles. |
Full source when not needed for restore. |
builder |
Compile, bundle, transpile, or publish artifacts. |
SDK image, compiler, build tools, source code. |
Runtime-only secrets. |
test |
Optional validation when tests should run during image build. |
Test runner and test dependencies. |
Production entrypoint assumptions. |
runtime |
Run the app with the minimum required files. |
Published artifacts, runtime libraries, config defaults, non-root user. |
Compilers, package caches, source not needed at runtime. |
Use meaningful names with AS, for example FROM node:18 AS builder, and order stages as dependencies → build → test → runtime.
Base image and reproducibility rules
| Decision |
Rule |
| Image source |
Prefer official base images or trusted distroless runtime images. |
| Tags |
Pin exact version tags such as python:3.11-slim; do not use floating tags like latest or bare python. |
| Alpine |
Use Alpine only when native dependencies, libc assumptions, and debugging needs are compatible. |
| Distroless |
Use distroless when the app does not need a shell or package manager at runtime. |
| Runtime dependencies |
Install only libraries required to launch the application. |
Layer, cache, and context patterns
| Pattern |
Why it matters |
| Copy dependency manifests before source files. |
Dependency restore layers remain cached when application code changes. |
Put frequently changing COPY . . late. |
Avoid invalidating expensive install/build layers. |
Add .dockerignore. |
Exclude .git, local build output, caches, secrets, test reports, and dependency directories that should be restored inside the image. |
Combine related RUN commands with &&. |
Reduce layer count and allow cleanup in the same layer. |
Use COPY --chown=<user>:<group>. |
Set ownership without an extra RUN chown layer. |
| Use build arguments intentionally. |
Keep environment-specific values configurable without baking secrets into layers. |
Security and runtime practices
- Run as a non-root user with
USER; create only the required UID/GID and directories.
- Remove build tools, package manager caches, and unnecessary packages from the final image.
- Do not use build secrets through
ARG or ENV; use BuildKit secrets when secrets are unavoidable during build.
- Set restrictive file permissions for copied artifacts.
- Set runtime optimization variables such as
NODE_ENV=production when they are correct for the framework.
- Add a
HEALTHCHECK that validates the application process or HTTP endpoint without requiring privileged tools.
- Scan the final image for vulnerabilities with the repository's existing scanner or platform.
Output template
## Multi-stage Dockerfile result - <app>
**Status:** created | updated | recommendation only | blocked
**Runtime image:** `<image:tag>`
### Dockerfile
```dockerfile
FROM <base>:<version> AS deps
<dependency restore commands>
FROM <base>:<version> AS builder
<build commands>
FROM <runtime>:<version> AS runtime
<copy artifacts, configure USER, EXPOSE, HEALTHCHECK, ENTRYPOINT/CMD>
```
### Companion files
```dockerignore
<entries such as .git, build output, caches, secrets, dependency directories>
```
### Validation
- Build command: `docker build -t <image> .` - <pass/fail/not run>
- Runtime check: `<docker run or healthcheck evidence>` - <pass/fail/not run>
Quality gate
1---2name: multi-stage-dockerfile-23description: Create or improve optimized multi-stage Dockerfiles with builder, dependency, test, and runtime stages. Use when the user asks for a multi-stage structure, smaller image, secure runtime image, Docker layer cache improvements, non-root container, .dockerignore, HEALTHCHECK, or Dockerfile best practices.4---56# Multi-stage Dockerfile78Design a Dockerfile that separates build-time work from runtime execution, minimizes the final image, preserves reproducibility, and avoids leaking tools or secrets into production layers.910## When to invoke1112- "Create a multi-stage Dockerfile for this app."13- "Make this Docker image smaller and more secure."14- "Split the Dockerfile into builder and runtime stages."15- "Add .dockerignore, non-root USER, and HEALTHCHECK best practices."1617## Stage design1819| Stage | Purpose | Common contents | Must not contain |20| --- | --- | --- | --- |21| `deps` | Restore dependency manifests before source changes. | `package-lock.json`, `requirements.txt`, `.csproj`, lockfiles. | Full source when not needed for restore. |22| `builder` | Compile, bundle, transpile, or publish artifacts. | SDK image, compiler, build tools, source code. | Runtime-only secrets. |23| `test` | Optional validation when tests should run during image build. | Test runner and test dependencies. | Production entrypoint assumptions. |24| `runtime` | Run the app with the minimum required files. | Published artifacts, runtime libraries, config defaults, non-root user. | Compilers, package caches, source not needed at runtime. |2526Use meaningful names with `AS`, for example `FROM node:18 AS builder`, and order stages as dependencies → build → test → runtime.2728## Base image and reproducibility rules2930| Decision | Rule |31| --- | --- |32| Image source | Prefer official base images or trusted distroless runtime images. |33| Tags | Pin exact version tags such as `python:3.11-slim`; do not use floating tags like `latest` or bare `python`. |34| Alpine | Use Alpine only when native dependencies, libc assumptions, and debugging needs are compatible. |35| Distroless | Use distroless when the app does not need a shell or package manager at runtime. |36| Runtime dependencies | Install only libraries required to launch the application. |3738## Layer, cache, and context patterns3940| Pattern | Why it matters |41| --- | --- |42| Copy dependency manifests before source files. | Dependency restore layers remain cached when application code changes. |43| Put frequently changing `COPY . .` late. | Avoid invalidating expensive install/build layers. |44| Add `.dockerignore`. | Exclude `.git`, local build output, caches, secrets, test reports, and dependency directories that should be restored inside the image. |45| Combine related `RUN` commands with `&&`. | Reduce layer count and allow cleanup in the same layer. |46| Use `COPY --chown=<user>:<group>`. | Set ownership without an extra `RUN chown` layer. |47| Use build arguments intentionally. | Keep environment-specific values configurable without baking secrets into layers. |4849## Security and runtime practices5051- Run as a non-root user with `USER`; create only the required UID/GID and directories.52- Remove build tools, package manager caches, and unnecessary packages from the final image.53- Do not use build secrets through `ARG` or `ENV`; use BuildKit secrets when secrets are unavoidable during build.54- Set restrictive file permissions for copied artifacts.55- Set runtime optimization variables such as `NODE_ENV=production` when they are correct for the framework.56- Add a `HEALTHCHECK` that validates the application process or HTTP endpoint without requiring privileged tools.57- Scan the final image for vulnerabilities with the repository's existing scanner or platform.5859## Output template6061````markdown62## Multi-stage Dockerfile result - <app>6364**Status:** created | updated | recommendation only | blocked65**Runtime image:** `<image:tag>`6667### Dockerfile68```dockerfile69FROM <base>:<version> AS deps70<dependency restore commands>7172FROM <base>:<version> AS builder73<build commands>7475FROM <runtime>:<version> AS runtime76<copy artifacts, configure USER, EXPOSE, HEALTHCHECK, ENTRYPOINT/CMD>77```7879### Companion files80```dockerignore81<entries such as .git, build output, caches, secrets, dependency directories>82```8384### Validation85- Build command: `docker build -t <image> .` - <pass/fail/not run>86- Runtime check: `<docker run or healthcheck evidence>` - <pass/fail/not run>87````8889## Quality gate9091- [ ] Build-time tools are absent from the runtime stage.92- [ ] Base images use explicit version tags and the runtime image is minimal for the app.93- [ ] Dependency restore layers are ordered before frequently changing source layers.94- [ ] `.dockerignore` excludes local caches, VCS data, secrets, and generated output.95- [ ] The final stage uses a non-root `USER` unless a documented platform constraint prevents it.96- [ ] Secrets are not stored in `ARG`, `ENV`, image layers, or copied files.97- [ ] `NODE_ENV=production` or equivalent runtime optimization is used only when appropriate.98- [ ] A meaningful `HEALTHCHECK` is included or a reason for omission is documented.