Skip to main content
Dockerfile Instructions What each instruction means, with copy-exact examples. Following the official Dockerfile reference (as of 2026-06).

Instruction overview

InstructionPurpose
FROMSpecify the base image; must be the first effective instruction. AS <name> names a stage
RUNRun a command at build time (install, compile). Each RUN is a layer
CMDDefault command at container start; overridable by what you append to docker run
ENTRYPOINTFixed entry point at container start; docker run args do not override it
COPYCopy files from the context into the image (the common choice)
ADDLike COPY but also auto-extracts tar and downloads URLs (prefer COPY)
ENVSet an env var; stays in the image, visible at runtime
ARGBuild-time variable via --build-arg; does not stay in the image
WORKDIRSet the working dir (auto-created), replacing RUN cd
EXPOSEDocumentary port declaration; does not actually open a port
VOLUMEMark a path as an external volume mount point
USERSet the identity for following instructions and the container (use non-root)
LABELAdd metadata (replaces the deprecated MAINTAINER)
HEALTHCHECKDefine a health-check command
ONBUILDDeferred trigger: runs when this image is used as a base
SHELLOverride the shell used by shell form
STOPSIGNALSignal sent on docker stop (default SIGTERM)

RUN shell form vs exec form

# Shell form (wrapped in /bin/sh -c, does variable expansion and pipes)
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl git \
    && rm -rf /var/lib/apt/lists/*

# Exec form (JSON array, no shell)
RUN ["/bin/bash", "-c", "echo $HOME"]
Merging multiple RUN into one (with && and \) reduces layers and cleans the apt cache in the same layer.

The three commonly confused pairs

  • CMD is the “default command”; docker run myimg other-command completely overrides CMD.
  • ENTRYPOINT is the “fixed entry point”; args appended to docker run are appended after ENTRYPOINT, not overriding it.
  • Common combo: ENTRYPOINT holds the fixed executable, CMD holds overridable default args.
  • Write both in exec form (JSON array) so PID 1 is the app itself and can receive SIGTERM for a graceful shutdown; shell form wraps a /bin/sh -c and the signal never reaches the app.
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8000"]
# docker run img             → python app.py --port 8000
# docker run img --port 9000 → python app.py --port 9000
Full interaction:
No ENTRYPOINTENTRYPOINT ["ep"] (exec)ENTRYPOINT ep (shell)
No CMDerrorep/bin/sh -c ep
CMD ["cmd"]cmdep cmd/bin/sh -c ep (CMD ignored)
CMD cmd/bin/sh -c cmdep /bin/sh -c cmd/bin/sh -c ep (CMD ignored)

Other common instruction examples

WORKDIR /app                    # working dir; relative paths are relative to the previous WORKDIR
EXPOSE 8000                     # documentary; actually publish with docker run -p
ENV APP_ENV=production PORT=8080
USER 1001:1001                  # switch to non-root (UID:GID, no dependence on a user database)
LABEL org.opencontainers.image.authors="dev@example.com"
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost/ || exit 1
STOPSIGNAL SIGTERM
EXPOSE is only a declaration, it does not open a port; a non-existent user for USER must be created first with RUN useradd.

Next

Reference: docs.docker.com/reference/dockerfile