You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Running pre-built images is useful, but the real power of Docker comes from building your own images. This lesson covers Dockerfile syntax, key instructions, build context, layer caching, .dockerignore, and multi-stage builds.
A Dockerfile is a plain text file containing a series of instructions that Docker uses to assemble an image. Each instruction creates a layer in the image.
# Simple Dockerfile example
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Every Dockerfile must start with FROM. This sets the base image that your image builds upon.
# Official Node.js image based on Alpine Linux (small)
FROM node:20-alpine
# Official Python image
FROM python:3.12-slim
# Minimal base — just a shell
FROM alpine:3.19
# Empty base — for fully static binaries
FROM scratch
Tip: Always use specific tags (e.g.
node:20-alpine) rather thanlatestto ensure reproducible builds.
RUN executes a command during the build and commits the result as a new layer.
# Install system dependencies
RUN apt-get update && apt-get install -y \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# Install application dependencies
RUN npm ci --production
Best practice: Chain related commands with
&&in a singleRUNto reduce the number of layers.
# Copy specific files
COPY package.json package-lock.json ./
# Copy entire directory
COPY src/ ./src/
# ADD can also extract archives and fetch URLs (prefer COPY for simplicity)
ADD app.tar.gz /app/
WORKDIR /app
# All subsequent commands run in /app
Both define what runs when a container starts, but they behave differently:
| Instruction | Purpose | Override behaviour |
|---|---|---|
CMD | Default command and arguments | Completely replaced by docker run args |
ENTRYPOINT | Fixed executable | Args appended by docker run |
# CMD — user can override the entire command
CMD ["node", "server.js"]
# ENTRYPOINT — user can only append arguments
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8080"] # default args, overridable
# ARG — build-time variable (not available at runtime)
ARG NODE_ENV=production
# ENV — persists into the running container
ENV NODE_ENV=production
ENV PORT=3000
# Documents that the container listens on port 3000
# (does NOT actually publish the port — that requires -p at runtime)
EXPOSE 3000
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
When you run docker build, Docker sends the build context (the directory you specify) to the daemon. The daemon uses this context to resolve COPY and ADD instructions.
# Build from the current directory
docker build -t my-app .
# Build from a specific directory
docker build -t my-app ./path/to/context
# Build with a specific Dockerfile
docker build -t my-app -f Dockerfile.prod .
Important: A large build context slows down builds. Use
.dockerignoreto exclude unnecessary files.
Similar to .gitignore, a .dockerignore file tells Docker which files to exclude from the build context:
# .dockerignore
node_modules
.git
.env
*.md
.DS_Store
dist
coverage
.next
This reduces build context size and prevents sensitive files from being included in the image.
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.