Cloud Native 21 min read

How to Optimize Dockerfiles for Faster Builds and Smaller Images

This guide explains practical Dockerfile optimization techniques—including using .dockerignore, minimizing layers, choosing lightweight base images, consolidating RUN commands, setting proper WORKDIR/CMD, leveraging ENTRYPOINT with exec, preferring COPY over ADD, and applying multi‑stage builds—to dramatically speed up image builds and reduce final image size.

Efficient Ops
Efficient Ops
Efficient Ops
How to Optimize Dockerfiles for Faster Builds and Smaller Images

Introduction

Dockerfile syntax is simple, but speeding up image builds, reducing image size, and improving readability require practical experience. This article provides a comprehensive set of Dockerfile best‑practice tips.

Goals

Faster build speed

Smaller Docker images

Fewer image layers

Effective use of cache

Improved Dockerfile readability

Simpler container usage

Key Recommendations

1. Create a .dockerignore file

<code>.git/</code><code>node_modules/</code>

2. Run a single application per container

Running multiple processes in one container leads to long build times, large images, tangled logs, wasted resources, and zombie‑process issues. Use separate containers (e.g., via Docker Compose) for each service.

3. Combine RUN instructions

Each Dockerfile instruction creates a new layer. Merging related RUN commands reduces layers and improves cache reuse.

<code>FROM ubuntu</code><code>RUN apt-get update \</code><code>    && apt-get install -y nodejs \</code><code>    && cd /app \</code><code>    && npm install</code>

4. Avoid using the latest tag

Specify an immutable tag (e.g.,

ubuntu:16.04

) to prevent unexpected changes when the upstream image updates.

<code>FROM ubuntu:16.04</code>

5. Delete temporary files after each RUN

Remove package lists and other build‑time artifacts to keep the image lean.

<code>RUN apt-get update \</code><code>    && apt-get install -y nodejs \</code><code>    && rm -rf /var/lib/apt/lists/*</code>

6. Choose an appropriate base image

For Node.js apps, use the official

node

image or an Alpine variant (

node:7-alpine

) to minimize size.

<code>FROM node:7-alpine</code><code>ADD . /app</code><code>RUN cd /app && npm install</code><code>CMD npm start</code>

7. Set WORKDIR and CMD correctly

<code>FROM node:7-alpine</code><code>WORKDIR /app</code><code>ADD . /app</code><code>RUN npm install</code><code>CMD ["npm","start"]</code>

8. Use ENTRYPOINT (optional) with exec form

Exec form prevents an extra shell process and ensures proper signal handling.

<code>ENTRYPOINT ["./entrypoint.sh"]</code><code>CMD ["start"]</code>

9. Use exec in entrypoint scripts

Calling

exec

replaces the shell with the application process, allowing Docker to forward signals correctly.

<code>#!/bin/sh</code><code>trap "exit" TERM</code><code>while true; do date; sleep 1; done</code>

10. Prefer COPY over ADD

COPY is simpler and only copies files; use ADD only when you need remote URLs or automatic extraction.

<code>COPY . /app</code>

11. Order COPY and RUN wisely

Place the least‑changing instructions early to maximize cache hits. For example, copy

package.json

first, run

npm install

, then copy the rest of the source.

<code>FROM node:7-alpine</code><code>WORKDIR /app</code><code>COPY package.json /app</code><code>RUN npm install</code><code>COPY . /app</code><code>CMD ["npm","start"]</code>

12. Set default environment variables, ports, and volumes

<code>ENV PROJECT_DIR=/app</code><code>WORKDIR $PROJECT_DIR</code><code>EXPOSE 3000</code><code>VOLUME /data</code>

13. Use LABEL for image metadata

<code>LABEL maintainer="[email protected]"</code>

14. Add HEALTHCHECK

<code>HEALTHCHECK CMD curl --fail http://localhost:$APP_PORT || exit 1</code>

15. Multi‑stage builds

Multi‑stage builds let you compile in a heavyweight image and copy only the final artifact into a lightweight runtime image.

<code>FROM golang:1.7.3 AS builder</code><code>WORKDIR /src</code><code>RUN go build -o app .</code><code>FROM alpine:latest</code><code>COPY --from=builder /src/app /app</code><code>CMD ["/app"]</code>

By applying these practices you can achieve faster builds, smaller images, and more maintainable Dockerfiles.

DockerImage OptimizationDevOpscontainerDockerfile
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.