The docker build command turns a Dockerfile into a runnable image.
It looks simple — docker build . — but the full command has dozens of
options that matter when you're building production images. Here's a practical
reference you can actually use.
The basic build command
docker build -t myapp:latest . Three things happen here:
-t myapp:latest— tags the image asmyappwith versionlatest.— the build context (current directory, containing the Dockerfile)- Docker looks for a file named
Dockerfilein that directory by default
Using a custom Dockerfile name or path
If your Dockerfile has a different name or lives in a subdirectory, use -f:
docker build -f ./docker/Dockerfile.prod -t myapp:prod .
Common pattern: Dockerfile.dev for development with hot reload, and
Dockerfile.prod for production with multi-stage optimization.
Multiple tags at once
You can tag the same image with multiple labels in a single build command:
docker build \
-t myapp:latest \
-t myapp:v1.2.3 \
-t myapp:stable \
-t registry.example.com/myapp:v1.2.3 \
.
This is useful in CI pipelines where you want both a version tag (for rollbacks)
and a latest tag (for convenience).
Build arguments (--build-arg)
Build args let you pass variables into the Dockerfile at build time:
docker build \
--build-arg NODE_VERSION=20 \
--build-arg BUILD_ENV=production \
-t myapp:prod . Your Dockerfile declares them with ARG:
ARG NODE_VERSION=18
FROM node:$NODE_VERSION
ARG BUILD_ENV
ENV NODE_ENV=$BUILD_ENV
Never pass secrets through --build-arg — they're baked into image
history and visible with docker history. Use --secret
instead (BuildKit feature):
DOCKER_BUILDKIT=1 docker build \
--secret id=npmrc,src=$HOME/.npmrc \
-t myapp . Disabling the cache
By default, Docker caches each layer. To force a full rebuild (useful when debugging cache issues or upstream image changes):
docker build --no-cache -t myapp . To pull the latest base image even if a cached version exists:
docker build --pull -t myapp . Target a specific stage (multi-stage builds)
For multi-stage Dockerfiles, you can build just a specific stage with --target:
docker build --target builder -t myapp:debug . Example multi-stage Dockerfile:
# Stage 1: build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: runtime
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"] docker build --target builder builds only the first stage, useful
for debugging or running tests against the full build environment.
Build for a different platform (ARM/AMD64)
If you're on an M1/M2 Mac (ARM) but deploying to x86 servers:
docker build --platform linux/amd64 -t myapp:prod . Or build for multiple platforms at once using BuildKit:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myapp:latest \
--push . Common flags cheat sheet
-t— tag the image-f— specify Dockerfile path--build-arg— pass build arguments--no-cache— disable build cache--pull— always pull base image--target— build specific multi-stage target--platform— target platform (amd64, arm64, etc.)--progress=plain— verbose output (useful for CI)--rm— remove intermediate containers (default: true)--squash— squash layers into one (experimental)
Production best practices
- Always use multi-stage builds — smaller final images, fewer attack surfaces
- Pin base image versions —
FROM node:20.11-alpine, notFROM node:latest - Order layers by change frequency — put
COPY package.jsonbeforeCOPY .for better caching - Use .dockerignore — prevent
node_modules,.git, and secrets from leaking into the image - Run as non-root —
USER nodeat the end of your Dockerfile - Use BuildKit — set
DOCKER_BUILDKIT=1for faster builds and secrets support
Building Docker images on bare metal is significantly faster than on virtualized cloud instances. Multi-stage builds with large dependencies (like ML models or full Node.js builds) can complete in minutes on dedicated hardware versus 15-30 minutes on shared cloud VMs.