Four instructions decide how configurable, observable and safe an image is. ARG and ENV both look like variables but live in different phases of the container's life. HEALTHCHECK tells Docker whether the application is actually serving, not merely running. USER removes the most common security weakness in Dockerfiles: running as root. After this lesson you will use all four correctly.
ARG declares a variable that exists only while the image is being built. Supply a value with --build-arg, or rely on the default:
ARG NODE_VERSION=22 # before FROM: usable in the FROM line only
FROM node:${NODE_VERSION}-alpine
ARG NODE_VERSION # redeclare to use it inside the stage
ARG APP_VERSION=dev
RUN echo "Building $APP_VERSION on Node $NODE_VERSION"docker build --build-arg APP_VERSION=1.4.2 -t my-app:1.4.2 .Key rules:
ARG before the first FROM is global to FROM lines but must be redeclared inside a stage to be used there.docker run my-app env will not show APP_VERSION.docker history can reveal them. Never pass secrets through ARG; the secrets lesson shows the right mechanism.TARGETARCH, TARGETPLATFORM and BUILDPLATFORM, useful for downloading the right binary in multi-platform builds.ENV sets an environment variable in the image. It is available to every later build instruction and to the running container, where docker run -e can override it:
ENV NODE_ENV=production \
PORT=3000 \
PATH="/app/node_modules/.bin:${PATH}"docker run --rm my-app env | grep PORT # PORT=3000
docker run --rm -e PORT=8080 my-app env | grep PORT # PORT=8080| | ARG | ENV |
|---|---|---|
| Set with | --build-arg or default | Dockerfile, -e, --env-file, Compose environment: |
| Available during build | Yes | Yes (after the instruction) |
| Available in the container | No | Yes |
| Overridable at run time | No | Yes |
| Shows in docker history | Yes | Yes |
A common pattern combines them: ARG receives a value at build time and ENV persists it, as in ARG APP_VERSION followed by ENV APP_VERSION=$APP_VERSION. Use the KEY=value form; the older ENV KEY value syntax without = is deprecated.
A container's process can be running while the application inside is deadlocked, still starting, or unable to reach its database. HEALTHCHECK defines a command Docker runs periodically; exit code 0 means healthy, 1 means unhealthy.
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1| Option | Default | Meaning |
|--------|---------|---------|
| --interval | 30s | Time between checks |
| --timeout | 30s | Fail the check if it runs longer |
| --start-period | 0s | Grace period during startup; failures do not count |
| --retries | 3 | Consecutive failures before unhealthy |
The status appears in docker ps as (healthy) or (unhealthy) and in docker inspect --format '{{.State.Health.Status}}'. Compose uses it for depends_on: condition: service_healthy, and Swarm replaces unhealthy tasks. The plain Engine does not restart an unhealthy container by itself.
The check command must exist in the image: curl is absent from many slim images, so use wget -qO- or a one-liner in the application's own runtime (node -e, python -c). HEALTHCHECK NONE disables a check inherited from the base image.
By default every instruction and the container's process run as root. If an attacker exploits the application, they are root inside the container, and any kernel or misconfiguration issue then puts the host at risk. Create a dedicated user and switch to it before CMD:
FROM python:3.13-slim
RUN groupadd --gid 1001 app && useradd --uid 1001 --gid app --create-home app
WORKDIR /app
COPY --chown=app:app . .
RUN pip install --no-cache-dir -r requirements.txt
USER app
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]Practical notes:
USER applies to everything after it, including RUN.COPY --chown so the application can read (and, where needed, write) its own files.-p 80:8080 on the host.node in the Node images, nonroot in distroless images; docker run --user 1001:1001 overrides USER at run time.Which variable is available inside the running container?
ARG is build-time only and set with --build-arg; ENV persists into the image and can be overridden with -e.docker history.HEALTHCHECK reports application readiness; Compose and orchestrators rely on it, and the check tool must exist in the image.--start-period so slow-starting apps are not marked unhealthy during boot.USER app; combine with COPY --chown and ports above 1024.Next lesson: Building and Tagging Images — turn the Dockerfile into versioned images with docker build and docker tag.