Docker
Intermediate Containers
Containerization
Build Process
DevOps

How are intermediate containers formed?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Intermediate containers are temporary build-time execution environments created while an image is being assembled from a Dockerfile. Historically Docker showed them very explicitly during the classic builder flow, while modern BuildKit hides more of the mechanics behind snapshots and cached build steps, but the idea is the same: each build step needs a temporary filesystem state to run against.

The Classic Builder Mental Model

In the classic Docker build flow, each Dockerfile instruction conceptually did this:

  1. start from the current image layer state
  2. create a temporary container
  3. execute the instruction
  4. commit the filesystem changes as a new image layer
  5. remove the temporary container

That is why older builds often printed lines like:

text
Running in 3f2d8c1a4b5c
Removing intermediate container 3f2d8c1a4b5c

Those messages were showing the temporary container created for that step.

Which Instructions Create Intermediate Execution States

The instructions that modify filesystem state or otherwise need a build context often create an intermediate execution environment:

  • 'RUN'
  • 'COPY'
  • 'ADD'

For example:

dockerfile
1FROM python:3.12-slim
2WORKDIR /app
3COPY requirements.txt .
4RUN pip install -r requirements.txt
5COPY . .

The builder needs a temporary container-like context to apply COPY and RUN against the current filesystem state. After each step, the result becomes the new parent state for the next step.

Why Docker Did This in the First Place

The approach provides three useful properties:

  • isolation for each build step
  • layer caching
  • reproducible step boundaries

If the COPY requirements.txt . step has not changed, Docker can reuse its cached result. Then the following RUN pip install ... step can also be reused if its inputs match.

This is one reason Dockerfiles are often structured to maximize cache reuse.

Intermediate Containers vs. Layers

People often confuse intermediate containers with image layers. They are related, but not identical.

  • the intermediate container is the temporary runtime context for a build step
  • the committed layer is the filesystem result that survives after the step finishes

So the temporary container goes away, but the resulting layer can remain cached and reused later.

That distinction explains why you may not see intermediate containers anymore but still benefit from cached layers from earlier builds.

What BuildKit Changed

Modern Docker builds often use BuildKit, which changes how much of this process is visible. BuildKit still creates intermediate build states, but it optimizes and abstracts them more aggressively:

  • better cache management
  • parallelized build graph handling
  • less user-facing noise about temporary containers

So in current tooling, you may not literally watch visible "intermediate containers" appear and disappear the way older Docker tutorials show. The concept still exists, but the implementation is more optimized and less user-exposed.

That is why older explanations are still conceptually right while looking visually outdated.

Why This Matters for Dockerfile Design

Understanding intermediate build states helps explain common best practices:

  • place stable steps early for cache reuse
  • combine related commands thoughtfully
  • avoid invalidating expensive layers unnecessarily

For example, putting COPY . . before dependency installation often causes more rebuild work than needed, because any source-code change invalidates the dependency-install layer too.

The better pattern is usually:

dockerfile
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

That works because intermediate build states and layer caching are step-oriented.

Multi-Stage Builds Still Use Temporary Build States

Multi-stage builds do not remove intermediate build states. They just make them more useful:

dockerfile
1FROM golang:1.22 AS builder
2WORKDIR /src
3COPY . .
4RUN go build -o app .
5
6FROM debian:bookworm-slim
7COPY --from=builder /src/app /usr/local/bin/app
8CMD ["app"]

The builder stage still has intermediate execution states. The difference is that only the final artifacts are copied into the runtime image, which keeps the final image smaller.

Common Pitfalls

  • Confusing temporary intermediate containers with the image layers that remain cached afterward.
  • Assuming modern BuildKit removed the concept entirely just because it hides more of it.
  • Writing Dockerfiles without considering how step order affects cache reuse.
  • Treating every instruction as equally expensive when some steps dominate rebuild time.
  • Thinking intermediate containers are runtime containers for your app rather than build-time artifacts.

Summary

  • Intermediate containers are temporary build-time execution environments created while processing Dockerfile steps.
  • In the classic builder, Docker showed them explicitly with Running in ... messages.
  • The temporary container is not the same thing as the cached image layer produced by the step.
  • BuildKit still uses intermediate build states, but it hides and optimizes them more aggressively.
  • Understanding this model helps you write Dockerfiles that build faster and cache better.

Course illustration
Course illustration

All Rights Reserved.