Mastering Docker Builds: Understanding --progress=plain and Essential Best Practices

Mastering Docker Builds: Understanding --progress=plain and Essential Best Practices
Photo by Pop & Zebra / Unsplash

When working with Docker, one of the most common commands you’ll use is docker build. This command creates a Docker image from a Dockerfile, and while it seems straightforward, there are several optimizations and configurations that can improve both efficiency and debugging. One such option is --progress=plain, which affects how build logs are displayed.

What Does --progress=plain Do?

By default, Docker uses a fancy, interactive progress bar when building images. While this looks nice, it can be difficult to debug, especially in CI/CD environments or when you need detailed logs.

Using --progress=plain ensures that the build output is shown in a raw, readable format, which is particularly useful when:

  • Debugging complex builds where step-by-step details matter.
  • Running builds in headless environments (like CI/CD pipelines) where interactive UI features are not helpful.
  • Capturing and analyzing logs easily since there is no dynamic UI interference.

Full Command Usage

To build an image with detailed logs, run:

docker build --progress=plain -t my-image .

Here’s what each part does:

  • docker build – Initiates the build process.
  • --progress=plain – Displays detailed logs without the progress UI.
  • -t my-image – Tags the built image as my-image.
  • . – Specifies the current directory as the build context.

Additional Considerations When Building Docker Images

To further optimize your Docker builds, consider these best practices:

1. Use a .dockerignore File

A .dockerignore file functions similarly to .gitignore, preventing unnecessary files from being included in the build context. This can significantly speed up builds and reduce image size. Example .dockerignore file:

node_modules/
.git/
temp/
.env

2. Optimize Layer Caching

Docker builds images in layers, and reusing cached layers can drastically speed up the build process. To take advantage of caching:

  • Place the most frequently changing instructions at the bottom of the Dockerfile.
  • Install dependencies early if they don’t change often.
  • Avoid running commands that change timestamps unnecessarily, as they invalidate cache layers.

3. Use Multi-Stage Builds

Multi-stage builds help keep final images small by discarding unnecessary build dependencies. Example:

# First stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# Final image
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]

This method ensures that only the final binary is included in the final image, reducing size significantly.

4. Minimize the Number of Layers

Each command in a Dockerfile creates a new layer. Merging commands with && can reduce the number of layers. Example:

RUN apt-get update && \
    apt-get install -y curl && \
    rm -rf /var/lib/apt/lists/*

5. Use Lighter Base Images

Instead of using large images like ubuntu:latest, consider using lightweight alternatives like alpine:

FROM alpine:latest

This can reduce image size significantly, improving build and deployment speed.

Finally

Using --progress=plain when building Docker images is a simple but powerful way to gain full visibility into your build process. Alongside this, adopting best practices like ignoring unnecessary files, leveraging caching, using multi-stage builds, reducing layers, and choosing the right base images will make your builds more efficient and production-ready.

Support Us