> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dockerfile Instructions One by One

> The semantics and examples of every Dockerfile instruction, plus the three commonly confused pairs: CMD vs ENTRYPOINT, COPY vs ADD, ARG vs ENV.

`Dockerfile` `Instructions`

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

## Instruction overview

| Instruction   | Purpose                                                                                    |
| ------------- | ------------------------------------------------------------------------------------------ |
| `FROM`        | Specify the base image; must be the first effective instruction. `AS <name>` names a stage |
| `RUN`         | Run a command at build time (install, compile). Each `RUN` is a layer                      |
| `CMD`         | Default command at container start; overridable by what you append to `docker run`         |
| `ENTRYPOINT`  | Fixed entry point at container start; `docker run` args do not override it                 |
| `COPY`        | Copy files from the context into the image (the common choice)                             |
| `ADD`         | Like COPY but also auto-extracts tar and downloads URLs (prefer COPY)                      |
| `ENV`         | Set an env var; stays in the image, visible at runtime                                     |
| `ARG`         | Build-time variable via `--build-arg`; does not stay in the image                          |
| `WORKDIR`     | Set the working dir (auto-created), replacing `RUN cd`                                     |
| `EXPOSE`      | Documentary port declaration; does not actually open a port                                |
| `VOLUME`      | Mark a path as an external volume mount point                                              |
| `USER`        | Set the identity for following instructions and the container (use non-root)               |
| `LABEL`       | Add metadata (replaces the deprecated `MAINTAINER`)                                        |
| `HEALTHCHECK` | Define a health-check command                                                              |
| `ONBUILD`     | Deferred trigger: runs when this image is used as a base                                   |
| `SHELL`       | Override the shell used by shell form                                                      |
| `STOPSIGNAL`  | Signal sent on `docker stop` (default SIGTERM)                                             |

## RUN shell form vs exec form

```dockerfile theme={null}
# 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

<Tabs>
  <Tab title="CMD vs ENTRYPOINT">
    * `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.

    ```dockerfile theme={null}
    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 ENTRYPOINT    | `ENTRYPOINT ["ep"]` (exec) | `ENTRYPOINT ep` (shell)       |
    | ------------- | ---------------- | -------------------------- | ----------------------------- |
    | No CMD        | error            | `ep`                       | `/bin/sh -c ep`               |
    | `CMD ["cmd"]` | `cmd`            | `ep cmd`                   | `/bin/sh -c ep` (CMD ignored) |
    | `CMD cmd`     | `/bin/sh -c cmd` | `ep /bin/sh -c cmd`        | `/bin/sh -c ep` (CMD ignored) |
  </Tab>

  <Tab title="COPY vs ADD">
    |                        | COPY | ADD |
    | ---------------------- | ---- | --- |
    | Copy local files       | Yes  | Yes |
    | Auto-extract local tar | No   | Yes |
    | Download remote URL    | No   | Yes |
    | `--from` across stages | Yes  | No  |

    Official advice is to **prefer COPY**: simple and predictable. Use ADD only when you actually need to "extract a tar" or "download a URL", and since ADD's URL caching and auth behavior is complex, a `RUN curl` is often clearer.

    ```dockerfile theme={null}
    COPY requirements.txt .
    COPY --from=builder /app/dist /usr/share/nginx/html   # across stages
    COPY --chown=appuser:appgroup . /app                  # set owner
    ```
  </Tab>

  <Tab title="ARG vs ENV">
    |                          | ARG           | ENV                |
    | ------------------------ | ------------- | ------------------ |
    | Declarable before FROM   | Yes           | No                 |
    | Stays in the final image | No            | Yes                |
    | Visible at runtime       | No            | Yes                |
    | Passed via CLI           | `--build-arg` | `docker run --env` |
    | In `docker history`      | No (default)  | Yes                |

    `ARG` is a temporary build-time variable; `ENV` is baked into the image. **ARG has a scope trap**: a global ARG declared before `FROM` is lost inside a stage, so re-declare `ARG` inside the stage to use it. When names collide, `ENV` overrides `ARG`.

    ```dockerfile theme={null}
    ARG PYTHON_VERSION=3.12-slim
    FROM python:${PYTHON_VERSION}
    ARG PYTHON_VERSION          # re-declare inside the stage to get a value
    RUN echo "built on ${PYTHON_VERSION}"
    ```

    Variables needed only at build time (like `DEBIAN_FRONTEND`) should not use ENV (it pollutes runtime); use inline or ARG:

    ```dockerfile theme={null}
    ARG DEBIAN_FRONTEND=noninteractive
    RUN apt-get install -y tzdata
    ```
  </Tab>
</Tabs>

## Other common instruction examples

```dockerfile theme={null}
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

* [A Python app Dockerfile](/en/notes/docker/dockerfile/python/): assembling these instructions into a working Dockerfile.
* [Layer cache and best practices](/en/notes/docker/dockerfile/caching/): instruction order and caching.

Reference: [docs.docker.com/reference/dockerfile](https://docs.docker.com/reference/dockerfile/)
