Skip to main content
  1. Blog/

Docker Best Practices I Wish I Knew Earlier

Ahmed Abdelmoneim
Author
Ahmed Abdelmoneim
Passionate software engineer building scalable systems and sharing knowledge through code.

Introduction
#

After years of working with Docker in production, I’ve accumulated a set of best practices that I wish someone had told me on day one. These tips will help you build smaller, faster, and more secure containers.

1. Use Multi-Stage Builds
#

One of the most impactful optimizations. Multi-stage builds let you separate your build environment from your runtime environment:

# Build stage
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server

# Runtime stage
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]

Result: image size drops from ~800MB to ~15MB.

2. Order Your Layers Wisely
#

Docker caches layers from top to bottom. Put things that change least frequently first:

FROM node:20-alpine

WORKDIR /app

# Dependencies change less often than source code
COPY package.json package-lock.json ./
RUN npm ci --production

# Source code changes frequently
COPY . .

CMD ["node", "server.js"]

3. Don’t Run as Root
#

Always create a non-root user in your containers:

FROM node:20-alpine

RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup -s /bin/sh -D appuser

WORKDIR /app
COPY --chown=appuser:appgroup . .

USER appuser
CMD ["node", "server.js"]

4. Use .dockerignore
#

Just like .gitignore, a .dockerignore file prevents unnecessary files from being copied into your image:

node_modules
.git
.env
*.md
docker-compose*.yml
.github

5. Pin Your Base Image Versions
#

Never use latest in production. Always pin to a specific version:

# Bad
FROM node:latest

# Good
FROM node:20.11-alpine3.19

6. Health Checks
#

Add health checks so orchestrators know when your container is truly ready:

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1

Conclusion
#

These practices might seem small individually, but together they make a massive difference in production. Your containers will be smaller, build faster, start quicker, and be more secure.

Start applying these one at a time to your existing projects — you’ll see the benefits immediately.