Cloud Native 10 min read

How to Build Minimal Docker Images with Short‑Lived Containers and Multi‑Stage Builds

This guide explains how to create short‑lived Docker containers, manage build context efficiently, use .dockerignore, apply multi‑stage builds, avoid unnecessary packages, decouple applications, reduce image layers, sort parameters, and leverage build cache to produce lean, fast‑building images.

Efficient Ops
Efficient Ops
Efficient Ops
How to Build Minimal Docker Images with Short‑Lived Containers and Multi‑Stage Builds

Creating Short‑Lived Containers

Generate an image from a Dockerfile and run containers that are meant to be stopped or destroyed quickly, treating them like lightweight VMs that can be recreated when configuration changes.

Understanding Build Context

When you run

docker build

, the current directory becomes the build context. By default the Dockerfile resides there, but you can specify another location with the

-f

flag. All files in the current directory are sent to the Docker daemon.

Build Context Example

Create a directory, add a

hello

file and a Dockerfile, then build the image in the current context:

<code>mkdir myproject && cd myproject
 echo "hello" > hello
 echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
 docker build -t helloapp:v1 .
</code>

Move the Dockerfile and

hello

file to another directory and build without using cache, specifying the Dockerfile location and context explicitly:

<code>mkdir -p dockerfiles context
 mv Dockerfile dockerfiles && mv hello context
 docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context
</code>

Including unnecessary files in the build context inflates the image size and increases build, pull, and push times. Use

.dockerignore

(similar to

.gitignore

) to exclude files that are not needed in the image.

Using Multi‑Stage Builds

Multi‑stage builds dramatically reduce final image size by copying only the required artifacts from earlier stages, leveraging the build cache to minimize layers.

Example Dockerfile for a Go application:

<code># Install tools required for project
# Run `docker build --no-cache .` to update dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# List project dependencies with Gopkg.toml and Gopkg.lock
# These layers are only rebuilt when Gopkg files change
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy the entire project and build it
# This layer is rebuilt when any source file changes
COPY . /go/src/project/
RUN go build -o /bin/project

# Final minimal image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
</code>

Avoid Installing Unnecessary Packages

Do not install tools that are not required for the runtime, such as text editors in a database image, to keep the image simple and small.

Application Decoupling

Each container should run a single application instance. Decoupling services into separate containers (e.g., web, database, cache) enables horizontal scaling and reuse. While a single process per container is a good guideline, it is not mandatory; some containers may run multiple related processes.

Reducing Image Layers

Only the

FROM

,

COPY

,

ADD

commands create new layers; other commands generate temporary images without adding layers. Multi‑stage builds also help by copying only needed artifacts into the final image.

Sorting Multi‑Line Parameters

Alphabetically sorting parameters (e.g.,

apt-get install

packages) avoids duplicate installations and improves readability.

<code>RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
</code>

Leveraging Build Cache

Docker executes Dockerfile instructions sequentially, checking the cache for each step. Use

--no-cache

to disable caching. Cache hits occur when the command and its inputs (e.g., file contents for

ADD

/

COPY

) match previous builds; otherwise the cache is invalidated.

If the parent image is cached, subsequent commands can reuse layers derived from it.

For

ADD

and

COPY

, Docker compares file contents and metadata; changes invalidate the cache.

For commands like

RUN apt-get update

, Docker only compares the command string, not the state of the container.

DockerImage OptimizationDevOpscontainerMulti‑Stage Buildbuild context
Efficient Ops
Written by

Efficient Ops

This public account is maintained by Xiaotianguo and friends, regularly publishing widely-read original technical articles. We focus on operations transformation and accompany you throughout your operations career, growing together happily.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.