Stage 4 · Provision
Building Images
Dockerfile Basics
Use FROM, WORKDIR, COPY, RUN, CMD, ENTRYPOINT, EXPOSE, and .dockerignore correctly.
What Is a Dockerfile?
A Dockerfile is a text file containing instructions that Docker reads sequentially to build an image. Each instruction creates a layer in the image. Docker builds images by executing these instructions step by step, caching each layer for faster rebuilds.
Essential Instructions
| Instruction | Purpose | Example |
|---|---|---|
| FROM | Set the base image | FROM node:20-alpine |
| WORKDIR | Set the working directory | WORKDIR /app |
| COPY | Copy files from host to image | COPY . . |
| RUN | Execute a command during build | RUN npm ci |
| CMD | Default command when container starts | CMD ["node", "server.js"] |
| ENTRYPOINT | Fixed entrypoint for the container | ENTRYPOINT ["python"] |
| EXPOSE | Document the container's listening port | EXPOSE 3000 |
| ENV | Set environment variables | ENV NODE_ENV=production |
CMD vs ENTRYPOINT
CMD provides default arguments that can be overridden when running the container. ENTRYPOINT defines the fixed executable that always runs. The common pattern is to use ENTRYPOINT for the command and CMD for its default arguments.
# CMD — arguments can be overridden
CMD ["python", "app.py"]
# docker run myimage → python app.py
# docker run myimage bash → bash (CMD overridden)
# ENTRYPOINT — fixed, not overridden
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myimage → python app.py
# docker run myimage test.py → python test.py (only CMD overridden)ENTRYPOINT + CMD gives you a fixed command with configurable arguments. This is the recommended pattern for most containers.
Use the JSON array form CMD ["python", "app.py"] instead of the shell form CMD python app.py. The shell form wraps your command in /bin/sh -c, which prevents signal forwarding and PID 1 reaping.
COPY vs ADD
COPY is straightforward — it copies files from the build context to the image. ADD has extra features: it can auto-extract tar archives and download files from URLs. However, these hidden behaviors make builds less predictable. Use COPY unless you specifically need tar extraction.
.dockerignore
The .dockerignore file excludes files from the build context. Without it, Docker sends the entire directory tree to the daemon, including node_modules, .git, and other unnecessary files. A good .dockerignore makes builds faster and images smaller.
node_modules
.git
.env
.env.*
dist
coverage
*.md
Dockerfile
docker-compose.ymlDocker reads .dockerignore before sending the build context. Excluded files are never sent to the daemon, so they cannot be COPYed into the image.
Your First Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]This Dockerfile copies package files first (for layer caching), installs dependencies, copies the app, runs as a non-root user, and starts the server. Each instruction is intentional.
# Build the image
docker build -t myapp:1.0 .
# Run the container
docker run -d -p 3000:3000 --name app myapp:1.0
# Verify it works
curl http://localhost:3000The -t flag tags the image with a name and version. The . specifies the build context (current directory). Docker reads the Dockerfile and builds the image layer by layer.
Always use the exec form for CMD and ENTRYPOINT: CMD ["node", "server.js"]. The exec form runs the process directly as PID 1, receiving Unix signals properly. The shell form wraps in sh -c, breaking signal handling.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.