The Core Instructions

A Dockerfile Is a Recipe

FROM node:20-alpine              # start from a base image
WORKDIR /app                     # cd (creates the dir)
COPY package*.json ./            # copy files from build context
RUN npm ci                       # run a command, bake result into a layer
COPY . .
ENV NODE_ENV=production          # default env var
EXPOSE 3000                      # documentation: app listens here
USER node                        # drop root before running
CMD ["node", "server.js"]    # default command when container starts
$ docker build -t myapp:v1 .     # . = build context (what COPY can see)
InstructionRuns whenCommon mistake
RUNat build timeusing it to start servers (they die when the step ends)
CMDat container startshell form breaks signal handling - prefer the JSON array form
COPYat build timewrong source path → "not found" build errors
EXPOSEnever (metadata only)thinking it publishes the port - you still need -p
Tip: Read build errors from the first failure. COPY site/ /x failing with "not found" means the path doesn't exist in the build context - check what's actually in the directory you passed to docker build.