█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/DevOps and Infrastructure/Docker and Containerization/Writing Dockerfiles
50 minBeginner

Writing Dockerfiles

After this lesson, you will be able to: Write a Dockerfile from scratch: FROM, RUN, COPY, WORKDIR, ENV, EXPOSE, CMD vs ENTRYPOINT. Build and tag images.

A Dockerfile is a script that produces an image. Master the syntax and you can package any app.

Prerequisites:Docker Fundamentals

A minimal Dockerfile for a Node.js app

This is the shape; we'll iterate to production-grade in later lessons.

tsx
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
# Build: docker build -t myapp:1.0 .
# Run: docker run -p 3000:3000 myapp:1.0

The instructions in detail

FROM <image>: base image. Every Dockerfile starts here. WORKDIR <path>: cd into this directory for subsequent commands (creates it if missing). COPY <src> <dest>: copy from host build context into the image. RUN <cmd>: execute at build time; the result becomes a layer. ENV KEY=val: set environment variable in the resulting image. EXPOSE 3000: documents the port (does NOT actually publish it; `-p` does that). CMD ["node", "server.js"]: default command when the container starts (overridable). ENTRYPOINT ["node"]: fixed entry; arguments after `docker run image` become CMD.

CMD vs ENTRYPOINT (the gotcha)

CMD alone: the default command, overridable. `docker run myapp echo hi` runs echo, not your app. ENTRYPOINT alone: a fixed prefix. `docker run myapp arg1 arg2` runs ENTRYPOINT + arg1 arg2. Both together: ENTRYPOINT is the binary; CMD is the default args. The cleanest pattern: ENTRYPOINT ["node"], CMD ["server.js"]. Use the JSON-array form (exec form): CMD ["node", "server.js"]. The string form invokes a shell, which complicates signal handling.

Build, tag, run

The full build → tag → run loop.

tsx
# Build (read Dockerfile in current dir, build context = .)
docker build -t myapp:1.0 .
# Tag for a registry
docker tag myapp:1.0 ghcr.io/myorg/myapp:1.0
# List built images
docker images
# Run, override CMD
docker run --rm myapp:1.0 node --version
# Pass build args
FROM node:${NODE_VERSION:-20}-alpine # in Dockerfile
docker build --build-arg NODE_VERSION=22 -t myapp:1.0 .

💡 Layers and the build cache

Each RUN / COPY creates a layer. Docker caches layers; if no input has changed, it reuses the cache. Order matters: put the LEAST-changing instructions early (COPY package.json + npm ci) and the MOST-changing late (COPY . .). That way, changing a source file only invalidates the last few layers; the slow `npm ci` step stays cached. Misordering this turns a 5-second build into a 5-minute build.

Common mistakes only experienced engineers avoid

COPY . . at the top of the file. Invalidates the cache on every code change; rebuilds dependencies. Forgetting .dockerignore. Without it, your build context includes node_modules, .git, .env. Big, slow, leaks secrets. Using `:latest` tag in production. Reproducibility lost; rollbacks hard. Always pin: `node:20.11-alpine`. RUN with multiple lines (RUN cmd1 + RUN cmd2 + ...). Each RUN is a layer. Combine related steps: `RUN cmd1 && cmd2`. Running containers as root by default. Add `USER node` (or a non-root user) before CMD.

Quick Check

Why does the Dockerfile pattern `COPY package*.json ./ ; RUN npm ci ; COPY . .` exist?

Pick the production reason.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Docker Fundamentals
Back to Docker and Containerization
Docker Compose→