ARG Instruction Reference
Last reviewed on 2026-09-25
Define build-time variables that users can pass at build-time
Quick answer: declare ARG after FROM, use it in RUN like a shell variable, and set it with --build-arg:
FROM alpine:3.20
ARG APP_VERSION=1.0
RUN echo "Building version $APP_VERSION"
docker build --build-arg APP_VERSION=2.1 -t myapp .
docker build --build-arg APP_VERSION="$VERSION" -t myapp . # from a shell variable
Syntax
The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build command using the --build-arg <varname>=<value> flag.
The ARG instruction allows you to define variables that can be passed at build-time to customize your builds. Unlike ENV instructions, ARG values are not available to containers running from the final image.
ARG instructions are particularly useful for creating flexible Dockerfiles that can be customized without modification. They allow users to specify values like version numbers, repository URLs, or other build-specific configurations when they run docker build.
Description
The ARG instruction defines a variable that users can pass at build-time to the builder. This allows for dynamic configuration of the build process without changing the Dockerfile.
An ARG instruction can include a default value that will be used if no value is passed at build-time. For example:
ARG VERSION=latest
In this case, VERSION will default to "latest" if not specified by the user.
To pass a value at build-time, you would use the --build-arg flag:
docker build --build-arg VERSION=1.0 -t myimage .
ARG values can be referenced in subsequent Dockerfile instructions using the familiar shell syntax:
ARG VERSION=latest
FROM ubuntu:${VERSION}
Unlike ENV variables, ARG values do not persist in the built image. They are only available during the build process and not to containers created from the image. If you need a value to be available at runtime, you should use ENV instead, or combine ARG and ENV:
ARG VERSION=latest
ENV VERSION=${VERSION}
Using ARG values in RUN
While a RUN instruction executes, every ARG in scope is exported to it as an environment variable. The shell then expands $NAME or ${NAME} as usual. Two conditions must hold:
- The
ARGis declared inside the stage, after itsFROM, and above theRUN. EachFROMstarts a new scope. - A global
ARG(declared before the firstFROM) is redeclared without a value in each stage that needs it. The bareARG NAMEinherits the global default or the--build-argvalue.
ARG PYTHON_VERSION=3.12
FROM python:${PYTHON_VERSION}-slim
# Empty here: the global ARG is only visible in FROM lines
RUN echo "py=$PYTHON_VERSION"
# Redeclare to bring it into this stage
ARG PYTHON_VERSION
ARG PIP_VERSION=24.2
RUN echo "py=$PYTHON_VERSION" \
&& pip install --no-cache-dir "pip==${PIP_VERSION}"
Things that look like ARG bugs but are not:
- Exec form runs no shell.
RUN ["echo", "$PIP_VERSION"]prints the literal string. Use shell form, orRUN ["sh", "-c", "echo $PIP_VERSION"]. - An
ENVwith the same name wins. If the stage (or its base image) setsENV PIP_VERSION=..., that value overrides theARGfor later instructions. - Unused build args. Passing
--build-arg FOO=1without anARG FOOin the Dockerfile does nothing; the value is not exported toRUN.
Instructions other than RUN — FROM, COPY, ADD, ENV, LABEL, WORKDIR, USER, EXPOSE and others — substitute ${NAME} themselves, following the same scope rules.
Passing --build-arg from a shell variable
The value after = is expanded by your shell before Docker sees it, so any variable or command substitution works. Quote it to keep spaces intact:
VERSION=2.1.0
docker build --build-arg VERSION="$VERSION" -t myapp:"$VERSION" .
# Command substitution
docker build --build-arg GIT_SHA="$(git rev-parse --short HEAD)" -t myapp .
# Name only: copies the value of $VERSION from the current environment
export VERSION=2.1.0
docker build --build-arg VERSION -t myapp .
# Several args: repeat the flag
docker build --build-arg VERSION="$VERSION" --build-arg ENVIRONMENT=staging .
In Docker Compose, the same thing goes under build.args, where ${VERSION} is interpolated from the shell or .env file:
services:
app:
build:
context: .
args:
VERSION: ${VERSION:-dev}
In CI, pass the pipeline's variables the same way, for example --build-arg GIT_SHA="$GITHUB_SHA", or the build-args input of docker/build-push-action.
Scope and Precedence
ARG instructions have specific scoping rules in Dockerfiles:
An ARG instruction goes into effect from the line it is defined and lasts until the end of the build stage.
FROM ubuntu:20.04
# ARG VERSION is not available here
ARG VERSION=1.0
# ARG VERSION is available here and below
ARG instructions defined before the first FROM are called "global" and can be used in any FROM instruction.
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE}
# BASE_IMAGE is empty in RUN here unless redeclared with: ARG BASE_IMAGE
These pre-FROM ARG values are not available for use inside instructions after the FROM line unless they are redefined with a new ARG instruction in that build stage.
In multi-stage builds, each FROM instruction starts a new build stage with its own scope.
ARG VERSION=1.0 # Global
FROM ubuntu:20.04 AS builder
ARG VERSION # Redeclared in this stage
RUN echo ${VERSION}
FROM alpine:3.14 AS runner
ARG VERSION # Must be redeclared in this stage too
RUN echo ${VERSION}
Docker has two groups of predefined build args:
- Proxy args —
HTTP_PROXY,HTTPS_PROXY,FTP_PROXY,NO_PROXY,ALL_PROXY(and lowercase forms). Usable inRUNwithout anARGline when passed with--build-arg, and excluded fromdocker history. - Platform args (BuildKit) —
BUILDPLATFORM,BUILDOS,BUILDARCH,TARGETPLATFORM,TARGETOS,TARGETARCH,TARGETVARIANT. These are set automatically in the global scope, so they work inFROMdirectly but must be declared withARGinside a stage:
FROM ubuntu:24.04
ARG TARGETARCH
RUN echo "Building for architecture: ${TARGETARCH}"
Examples
Basic Usage
Defining a simple build argument with a default value:
FROM ubuntu:20.04
ARG VERSION=1.0
RUN echo "Building version: ${VERSION}"
The VERSION argument defaults to 1.0 but can be overridden at build time.
Dynamic Base Image
Using ARG to determine the base image:
ARG BASE_IMAGE=ubuntu:20.04
FROM ${BASE_IMAGE}
ARG BASE_IMAGE
RUN echo "Using base image: ${BASE_IMAGE}"
This allows building from different base images without modifying the Dockerfile.
Package Installation
Using ARG to control which packages to install:
FROM ubuntu:20.04
ARG PACKAGES="vim curl git"
RUN apt-get update && apt-get install -y ${PACKAGES}
This makes it easy to customize which packages are installed during the build.
Setting Environment Variables
Combining ARG and ENV to set runtime configuration:
FROM node:18-alpine
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
RUN echo "Building with NODE_ENV=${NODE_ENV}"
This allows customizing the NODE_ENV value at build time, and persisting it for runtime.
Conditional Logic
Using ARG for conditional build steps:
FROM ubuntu:20.04
ARG ENVIRONMENT=production
RUN if [ "$ENVIRONMENT" = "development" ]; then \
apt-get update && apt-get install -y vim git curl; \
else \
apt-get update && apt-get install -y curl; \
fi
This example installs different packages based on the build environment.
Multi-stage Build with ARG
Using ARG in a multi-stage build:
ARG VERSION=latest
FROM node:18 AS builder
ARG VERSION
RUN echo "Building version: ${VERSION}"
# Build commands here...
FROM nginx:alpine AS runtime
ARG VERSION
LABEL version="${VERSION}"
# Copy from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
Note how the ARG needs to be redeclared in each stage to be available.
Best Practices
- Provide default values: Always include sensible default values for ARG instructions to ensure the Dockerfile works without requiring build arguments.
- Document ARG instructions: Add comments explaining what each ARG is for and what values are acceptable.
- Use for build-time customization: Use ARG for values that should be customizable at build-time but don't need to be available at runtime.
- Combine with ENV for runtime variables: If a value is needed at both build-time and runtime, define it with ARG and then set it as an ENV variable.
- Be careful with secrets: ARG values are visible in the image history. Don't use ARG for sensitive information like passwords or API keys.
- Group related ARG instructions: Place related ARG instructions together to make the Dockerfile more readable.
- Consider multi-platform builds: Use BuildKit's predefined ARGs like TARGETARCH for multi-platform images (declare
ARG TARGETARCHinside the stage). - Redeclare in each stage: Remember to redeclare ARG instructions in each build stage where they are needed.
ARG and the build cache
An ARG affects the layer cache only from the point at which its value is actually referenced. Declaring one costs nothing; using one invalidates every instruction below the reference whenever the value changes. That makes placement, not declaration, the thing to get right.
The common mistake is threading a per-build value — a commit SHA, a build timestamp — through the top of the Dockerfile:
# Slow: every commit changes GIT_SHA, so npm ci re-runs on every build
FROM node:20-alpine
ARG GIT_SHA
ENV GIT_SHA=${GIT_SHA}
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Fast: the dependency layer never sees GIT_SHA
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG GIT_SHA
ENV GIT_SHA=${GIT_SHA}
The same property is occasionally useful in reverse. Because a changed ARG value busts everything below it, a deliberately-varied argument makes a reliable cache-buster for a step that must re-run:
docker build --build-arg CACHEBUST=$(date +%s) -t myapp .
Passing secrets: use a secret mount, not ARG
An ARG value is recorded in the image metadata and is visible to anyone who can pull the image:
docker build --build-arg NPM_TOKEN=npm_abc123 -t myapp .
docker history --no-trunc myapp # the token is right there
docker image inspect myapp | grep -A5 Args
Squashing layers does not remove it, and neither does unsetting the variable in a later instruction. The supported answer is BuildKit's secret mount, which exposes the value as a file inside a single RUN and never writes it to a layer:
# syntax=docker/dockerfile:1.7
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm ci --omit=dev
docker build --secret id=npmrc,src=$HOME/.npmrc -t myapp .
# Or take the secret's value from an environment variable
# (Dockerfile: RUN --mount=type=secret,id=npm_token,env=NPM_TOKEN npm ci)
docker build --secret id=npm_token,env=NPM_TOKEN -t myapp .
For Git access over SSH, the equivalent is --mount=type=ssh with docker build --ssh default. Both are covered in the securing Docker builds tutorial. Reserve ARG for values you would happily print in a log: versions, feature flags, target architectures, repository URLs.
ARG vs ENV
It's important to understand the difference between ARG and ENV instructions:
| ARG | ENV |
|---|---|
| Only available at build time | Available at both build time and runtime |
| Does not persist in the final image | Persists in the final image |
Can be set at build time with --build-arg |
Can be overridden at container runtime with -e |
| Used for build-time customization | Used for runtime configuration |
Scoped to one build stage; must be redeclared after each FROM |
Inherited by later stages built FROM that stage, and by child images |
Loses to an ENV of the same name |
Overrides an ARG of the same name |
If you need a value only during the build process, use ARG. If you need a value to be available to the running container, use ENV.
You can combine ARG and ENV to create build-time configurable runtime environments:
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
This allows you to set NODE_ENV at build time with --build-arg, while still making it available at runtime as an environment variable.
Notes and Limitations
- ARG values are not set in the final image's environment, unlike ENV values (they are still visible in the image history).
- ARG values are visible in the image history, so don't use them for secrets.
- ARG instructions defined before a FROM are only available for use within FROM lines unless they are redeclared after the FROM.
- Each build stage in a multi-stage build has its own scope for ARG values. You need to redeclare ARG in each stage where you want to use it.
- An ENV instruction with the same name overrides an ARG for all later instructions.
--build-arg NAMEwithout a value reads NAME from the environment of the shell runningdocker build. - Proxy args (HTTP_PROXY etc.) can be used without declaring them; BuildKit platform args (TARGETARCH etc.) work in FROM directly but need
ARG TARGETARCHinside a stage. - ARG values can be referenced in almost all Dockerfile instructions, but there are some limitations with ONBUILD instructions.
- Neither ARG nor ENV creates a filesystem layer; ENV is stored in the image config, while ARG is only recorded in the build history.
FAQ
How do I use an ARG value in a RUN instruction?
Declare the ARG after the FROM of the stage that uses it, then reference it like a shell variable: ARG VERSION=1.0 followed by RUN echo "$VERSION". During RUN the value is exposed as an environment variable, so the shell expands $VERSION or ${VERSION}. Override it with docker build --build-arg VERSION=2.0 .
Why is my ARG empty inside RUN?
Usually one of three reasons: the ARG is declared before FROM (a global ARG is only visible in FROM lines until you redeclare it with ARG NAME inside the stage); it is declared in a different stage (each FROM starts a new scope); or RUN uses exec form such as RUN ["echo", "$VERSION"], which runs no shell and prints the literal text.
How do I pass a shell variable to docker build --build-arg?
Let your shell expand it: docker build --build-arg VERSION="$VERSION" . or --build-arg GIT_SHA="$(git rev-parse --short HEAD)". If the variable already exists in your environment under the same name, --build-arg VERSION with no value copies it. The Dockerfile still needs a matching ARG VERSION.
What is the difference between ARG and ENV in a Dockerfile?
ARG is a build-time variable: set with --build-arg, visible to later instructions in its stage, and not present in the running container's environment. ENV is stored in the image config and set in every container started from it; override it with docker run -e. To carry a build argument into runtime, copy it: ARG VERSION then ENV VERSION=$VERSION. See ARG vs ENV.
Can I pass secrets with --build-arg?
No. Build argument values are recorded in the image history and build metadata and can be read by anyone with the image. Use a BuildKit secret mount instead: RUN --mount=type=secret,id=token ... with docker build --secret id=token,env=TOKEN. See passing secrets.