Cloud Native 6 min read

Using Docker Multistage Builds to Create Small, Efficient Images

This article explains how Docker's multistage build feature, introduced in version 17.05, enables faster image creation and significantly smaller container sizes by separating build steps into distinct stages within a single Dockerfile, and shows how to target specific stages during the build process.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
Using Docker Multistage Builds to Create Small, Efficient Images

Docker added support for multistage builds starting with version 17.05, allowing developers to define multiple build stages in a single Dockerfile and produce smaller, more efficient images.

In a basic example, a Go application is compiled in a builder stage based on golang:1.9-alpine , and the resulting binary is copied into a lightweight alpine:latest runtime stage, resulting in a minimal final image.

FROM golang:1.9-alpine as builder

RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]

The image can be built with a simple command:

$ docker build -t go/helloworld:3 .

Beyond basic usage, multistage builds can define many targets and let you build only a specific one using the --target flag. A more complex example defines a build environment that compiles several Go binaries, compresses them with upx , and then copies each binary into its own distroless image.

# BUILD ENVIRONMENT
ARG GO_VERSION
FROM golang:${GO_VERSION} as builder
RUN apt-get -y update && apt-get -y install upx
WORKDIR /workspace
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download
# Copy source files
COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/
COPY internal/ internal/
COPY webhooks/ webhooks/
COPY version/ version/
COPY cmd/ cmd/
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on
RUN go build -mod=readonly "-ldflags=-s -w" ./...
ARG VERSION
RUN go build -mod=readonly -o manager main.go
RUN go build -mod=readonly -o proxy cmd/proxy/main.go
RUN go build -mod=readonly -o backup-agent cmd/backup-agent/main.go
RUN go build -mod=readonly -o restore-agent cmd/restore-agent/main.go
RUN upx manager proxy backup-agent restore-agent
# IMAGE TARGETS
FROM gcr.io/distroless/static:nonroot as controller
WORKDIR /
COPY --from=builder /workspace/manager .
USER nonroot:nonroot
ENTRYPOINT ["/manager"]

FROM gcr.io/distroless/static:nonroot as proxy
WORKDIR /
COPY --from=builder /workspace/proxy .
USER nonroot:nonroot
ENTRYPOINT ["/proxy"]

FROM gcr.io/distroless/static:nonroot as backup-agent
WORKDIR /
COPY --from=builder /workspace/backup-agent .
USER nonroot:nonroot
ENTRYPOINT ["/backup-agent"]

FROM gcr.io/distroless/static as restore-agent
WORKDIR /
COPY --from=builder /workspace/restore-agent .
USER root:root
ENTRYPOINT ["/restore-agent"]

To build a specific target, such as the controller image, use the --target option together with any required build arguments:

$ docker build --target controller \
  --build-arg GO_VERSION=${GO_VERSION} \
  --build-arg VERSION=$(VERSION) \
  --tag ${DOCKER_REPO}/${DOCKER_IMAGE_NAME_PREFIX}controller:${DOCKER_TAG} \
  --file Dockerfile .

By defining multiple stages and targets in a single Dockerfile, developers can maintain a concise build configuration while producing a variety of optimized container images.

cloud-nativedockerGoContainerDockerfilemultistage-build
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

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.