DevOps Peach

Docker & Kubernetes · Building Docker Images with Dockerfile · Lesson 2 of 4

The main Dockerfile instructions

FROM, RUN, WORKDIR, COPY, EXPOSE, ENV and ARG — what each instruction does and the usual traps.

Practice interactively →

ARG VERSION=1.36
FROM busybox:$VERSION
ARG VERSION
WORKDIR /app
COPY --chown=1000:1000 . /app
RUN echo "build $VERSION" > /app/build.txt
ENV APP_ENV=production
EXPOSE 8080
Instruction Meaning
FROM Starts a new build stage and picks the base image; each FROM drops the previous stage's state
RUN Runs a command at build time; the result becomes a layer
WORKDIR Sets the working directory for later instructions; created if missing
COPY Copies files from the build context into the image; --chown sets the owner
EXPOSE Documents the port the app listens on (TCP by default, 80/udp for UDP)
ENV An environment variable available at build and at container run time
ARG A variable that only exists at build time, passed with --build-arg
flowchart LR
  CTX["Build context<br/>Dockerfile + files"] --> FROM["FROM busybox<br/>base image"]
  FROM --> WD["WORKDIR /app"]
  WD --> CP["COPY . /app<br/>new layer"]
  CP --> RUN["RUN build step<br/>new layer"]
  RUN --> IMG["Image demo:1.36<br/>ENV and EXPOSE as metadata"]
  IMG -- "docker run -p 8080:8080" --> C["Container<br/>port published"]
  %% step CTX: docker build sends the build context; COPY can only read files inside it.
  %% step FROM: FROM picks the base image and starts a stage; ARG before FROM is usable only in this line.
  %% step WD: WORKDIR sets the directory for the instructions that follow.
  %% step CP: COPY adds files from the context as a new layer.
  %% step RUN: RUN executes at build time and its result becomes another layer.
  %% step IMG: ENV and EXPOSE are stored as image metadata; EXPOSE only documents the port.
  %% step C: The port is reachable from the host only when docker run publishes it with -p.

Easy to get wrong

docker build --build-arg VERSION=1.36 -t demo:1.36 .

More instructions

Instruction Meaning
ADD Like COPY, but also extracts local tar archives and can fetch URLs; prefer COPY unless you need those
USER Sets the user for later RUN, CMD, and ENTRYPOINT; use a non-root user for the final image
LABEL Metadata such as org.opencontainers.image.source or a maintainer
VOLUME Marks a path as a volume so its data lives outside the container layer
HEALTHCHECK Command Docker runs to judge whether the app is healthy
SHELL Changes the shell used by shell-form instructions, for example to bash or PowerShell
STOPSIGNAL Signal sent by docker stop

Running as non-root

RUN addgroup -S app && adduser -S app -G app
USER app

A process running as root inside a container is root on the host if it ever escapes, so dropping privileges is one of the cheapest security wins. Many official images ship a ready user, such as node in Node.js images.

Secrets during the build

Never COPY a credentials file or pass a token through ARG. With BuildKit, a secret can be mounted only for one RUN step and never stored in a layer:

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

Build it with docker build --secret id=npmrc,src=$HOME/.npmrc ..

.dockerignore

A .dockerignore file next to the Dockerfile keeps files out of the build context: .git, node_modules, local .env files, and test output. A smaller context uploads faster, invalidates the cache less often, and avoids leaking secrets into the image through COPY . ..

Heredocs

Recent Dockerfile syntax allows multi-line scripts in one instruction:

RUN <<EOF
set -e
apt-get update
apt-get install -y --no-install-recommends curl
rm -rf /var/lib/apt/lists/*
EOF

Practice interactively →