You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Building and running containers in production requires more than knowing the commands. Following established best practices results in images that are smaller, faster, more secure, and easier to maintain.
Every byte in a base image adds to pull time, storage, and attack surface. Prefer minimal bases:
# Prefer this
FROM node:20-alpine
# Over this
FROM node:20
By default, processes in a container run as root. This is a security risk — if an attacker exploits your application, they have root privileges inside the container. Always create and switch to a non-root user:
FROM node:20-alpine
WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --omit=dev
# Switch to the built-in non-root user
USER node
CMD ["node", "src/index.js"]
Never use latest in production Dockerfiles. Use specific version tags so builds are reproducible and upgrades are intentional:
# Bad: unpredictable
FROM postgres:latest
# Good: explicit and reproducible
FROM postgres:16.2-alpine3.19
Each RUN instruction creates a layer. Chain related commands with && and clean up in the same layer so the intermediate files are never committed:
# Bad: three layers, apt cache left in the image
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# Good: one layer, cache cleaned in the same step
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
A .dockerignore file prevents unnecessary files from being sent to the Docker daemon as the build context, speeding up builds and keeping secrets out of images:
# .dockerignore
.git
node_modules
.env
*.log
dist
coverage
.DS_Store
Define a HEALTHCHECK so Docker (and orchestrators like Kubernetes) know whether your container is actually healthy, not just running:
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD curl -f http://localhost:3000/health || exit 1
Containers should be stateless and treat any writable data as temporary. Persist all state in volumes or external services (databases, object storage). This enables containers to be stopped, replaced, and scaled without data loss.
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.