Writing a Dockerfile
Turn an app into an image.
So far you have run images other people built; now you build your own. A Dockerfile is a plain-text recipe: a list of instructions Docker follows top to bottom to assemble an image. It starts FROM a base image, copies your app in, runs any build steps, and declares what to execute when a container starts. The whole thing is deliberately small and readable — most real Dockerfiles are under twenty lines.
FROM node:22-alpine # start from a small Node base imageWORKDIR /app # set the working directory inside the imageCOPY package*.json ./ # copy dependency manifests first (see the layers lesson)RUN npm ci --omit=dev # install dependencies at build timeCOPY . . # copy the rest of the sourceEXPOSE 3000 # document the port the app listens onCMD ["node", "server.js"] # the command that runs when the container starts
Build it, then run it
docker build turns the Dockerfile into an image. The -t flag tags it with a name, and the final argument (usually a dot) is the build context — the directory Docker sends to the daemon and can COPY from. Once built, you run it exactly like any other image.
$ docker build -t payments-api:1.0 .[+] Building 12.4s ... => naming to payments-api:1.0$ docker run -d -p 3000:3000 payments-api:1.0$ curl -s localhost:3000/healthzok
The instructions you will actually use
A handful cover most needs. FROM chooses the base. WORKDIR sets the directory for the instructions that follow. COPY brings files from the build context into the image (prefer it over ADD, which does extra magic you rarely want). RUN executes a command at build time and bakes the result into a layer. ENV sets defaults. EXPOSE documents a port. CMD and ENTRYPOINT — the next lesson — say what runs at start.