A Dockerfile is a text file of instructions. docker build runs them top to bottom and saves the result as an image. This chapter packages one app, the team's status page, a small Node.js/Express service in ~/status-page.
The Dockerfile
# status-page image
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]| Instruction | What it does | Takes effect |
|---|---|---|
FROM node:20-alpine | Start from an existing image | Build |
WORKDIR /app | Create /app and make it the current directory | Build and run |
COPY package.json ./ | Copy a file from the build context into the image | Build |
RUN npm install | Run a command inside the image being built, keep its changes | Build |
COPY . . | Copy everything else from the context | Build |
EXPOSE 3000 | Record that the app listens on 3000 | Metadata only |
CMD ["node", "server.js"] | Default command for containers | Run |
Lines starting with # are comments. Instructions are written in upper case by convention. Each later lesson covers one of these lines.
The build context
docker build -t status-page:0.1 ~/status-page
└── build context
~/status-page/
├── Dockerfile read by the builder
├── package.json ─┐
├── server.js ├─ files COPY can use
├── README.md │
└── public/ ─┘The last argument to docker build is the build context: a directory whose contents are sent to the builder.
- Docker reads
Dockerfilefrom the root of the context unless-fnames another file. COPYsources are paths relative to the context root. Nothing outside it is visible.- The whole context is sent on every build, so large or secret files in it matter (lesson 9).
Building from a directory without a Dockerfile fails before anything runs:
learner@pier:~$ docker build -t status-page:0.1 .
[+] Building 0.0s (1/1) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 2B 0.0s
ERROR: failed to solve: failed to read dockerfile: open Dockerfile: no such file or directoryPass the right directory, or cd into it and use ..
Reading the build output
learner@pier:~$ docker build -t status-page:0.1 ~/status-page
[+] Building 2.2s (10/10) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 137B 0.0s
=> [internal] load metadata for docker.io/library/node:20-alpine 0.8s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/node:20-alpine@sha256:487ed5beb25f… 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 1.56kB 0.0s
=> [2/5] WORKDIR /app 0.0s
=> [3/5] COPY package.json ./ 0.1s
=> [4/5] RUN npm install 0.5s
=> [5/5] COPY . . 0.1s
=> exporting to image 0.1s
=> => naming to docker.io/library/status-page:0.1 0.0s| Line | Meaning |
|---|---|
load build definition | The Dockerfile was read |
load metadata for … | The base image was resolved |
transferring context: 1.56kB | Size of the build context sent |
[2/5] WORKDIR /app | Step 2 of 5; only FROM, WORKDIR, COPY, ADD and RUN are numbered |
naming to … | The tag given with -t |
EXPOSE and CMD have no numbered step: they only change the image's configuration.