RUN executes a command inside the image while it is being built. Whatever the command writes to the filesystem becomes part of the image.
image so far ──▶ temporary container runs the command ──▶ changed files saved as a new layerTypical build steps
| Purpose | Alpine (node:20-alpine) | Debian (node:20) |
|---|---|---|
| App dependencies | RUN npm install | RUN npm install |
| OS package | RUN apk add --no-cache curl | RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* |
| Create a directory | RUN mkdir -p /app/data | RUN mkdir -p /app/data |
--no-cache(apk) andrm -rf /var/lib/apt/lists/*(apt) keep the package index out of the layer.-yanswers apt's prompt. A build cannot typeYfor you; without it the step aborts.- Chain commands that belong together with
&&in oneRUN, so a failure in the first stops the second.
RUN uses /bin/sh -c by default (shell form), so &&, pipes and $VARS work. The exec form RUN ["npm", "install"] runs the program directly, with no shell.
RUN versus CMD
RUN | CMD | |
|---|---|---|
| Runs | During docker build | When a container starts |
| Result | A filesystem layer | Nothing until a container runs it |
| Count | As many as you need | The last one wins |
RUN node server.js would try to start the server during the build and never finish. Start the app with CMD.
Order matters
A RUN sees only what earlier instructions put in the image. npm install reads package.json, so the COPY that brings package.json in must come first:
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN apk add --no-cache curl
RUN npm install
CMD ["node", "server.js"]Without RUN npm install, the image has the code but no node_modules, and the container stops with Error: Cannot find module 'express'.
When a step fails
A non-zero exit code stops the build. BuildKit prints the command's output and the Dockerfile lines around it:
learner@pier:~/status-page$ docker build -t status-page:0.4 .
...
=> [3/4] COPY . . 0.1s
=> ERROR [4/4] RUN apt-get update && apt-get install -y curl 0.4s
------
> [4/4] RUN apt-get update && apt-get install -y curl:
0.200 sh: apt-get: not found
------
Dockerfile:4
--------------------
2 | WORKDIR /app
3 | COPY . .
4 | >>> RUN apt-get update && apt-get install -y curl
5 | EXPOSE 3000
--------------------
ERROR: failed to solve: process "/bin/sh -c apt-get update && apt-get install -y curl" did not complete successfully: exit code: 127| Part | Tells you |
|---|---|
ERROR [4/4] RUN … | Which step failed |
Lines between ------ | The command's own output |
>>> | The Dockerfile line |
exit code: 127 | The exit status (127 = command not found) |
No image is tagged when a build fails; the previous image with that tag, if any, is unchanged.