Back
3/5

Writing Dockerfiles

+20 XP on completion

#Writing Dockerfiles

After this lesson you'll know:

  • how to write a Dockerfile from scratch
  • the layer principle and why order matters
  • how to build and optimize images

#Your first Dockerfile

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

#The individual commands

CommandEffect
FROMBase image (choose alpine — ~5MB instead of 300MB)
WORKDIRSets the working directory
COPYCopies files from host into the image
RUNExecutes commands during build
EXPOSEDocuments the port (doesn't open it!)
CMDDefault command when container starts

#Build & Run

# Build
docker build -t my-app .

# Run
docker run -p 3000:3000 my-app

#Layer Caching — the superpower

Each Dockerfile instruction creates a layer. Docker caches layers — as long as the content doesn't change.

# WRONG — npm install runs on every change
COPY . .
RUN npm install

# RIGHT — npm install only when package.json changes
COPY package*.json ./
RUN npm install
COPY . .

This saves ~30 seconds on the tenth build. On large projects, minutes.

#.dockerignore — keep junk out of your image

Without .dockerignore, you send your entire project folder as build context to the Docker daemon. node_modules (hundreds of MB), .git (the entire history), .env (with secrets) — all of it gets sent.

# .dockerignore — in project root next to Dockerfile
node_modules
.git
.env
*.log
dist
.DS_Store

Effects:

  • Faster builds — less data to send
  • More secure — no secrets in build context
  • Slimmer images — no junk in the final layer

Tip: After building, check docker build -t app . 2>&1 | head -20 — if the build context size is more than 10 MB, you forgot your ignore file.


#✋ Try it out

  • Create a new directory, write a Dockerfile with FROM alpine, CMD ["echo", "Hello Docker"], build with docker build -t hello . and run it
  • Extend the Dockerfile: install curl with RUN apk add curl, rebuild and start a shell (docker run -it hello sh)
  • Create a .dockerignore with node_modules and .git. Rebuild the image — is the build faster?

#📌 Summary

  • FROM selects the base image, RUN executes build commands, CMD starts the container
  • COPY order affects caching: first package.json, then the rest
  • Alpine-based images are ~50x smaller than full Linux distributions
  • Without .dockerignore, node_modules and .git end up in your image — unnecessary megabytes
← → to navigate