This Docker build error occurs when you reference an ARG variable that hasn't been declared, or when the ARG declaration loses scope due to multi-stage builds. Fix it by declaring ARG before use and redeclaring after each FROM instruction.
The "ARG variable is undefined" error in Docker indicates that you're trying to use a build argument that hasn't been properly declared or has gone out of scope. Docker's ARG instruction defines variables that users can pass at build-time using the `--build-arg` flag. This error commonly occurs in two scenarios: when you reference a variable before declaring it with ARG, or when you use multi-stage builds where each FROM instruction resets the scope of previously declared ARGs. Unlike ENV variables which persist throughout the build and into the running container, ARG variables are only available from the point they're declared until the next FROM instruction. Docker BuildKit performs stricter validation of undefined variables than the legacy builder. When BuildKit detects usage of an undeclared variable, it warns you with messages like "Usage of undefined variable '$foo'" to help catch typos and missing declarations early in the build process.
Every ARG variable must be declared before it's used. Add an ARG instruction before any reference to the variable.
Incorrect:
FROM alpine AS base
COPY $foo .Correct:
FROM alpine AS base
ARG foo
COPY $foo .You can also provide a default value:
FROM alpine AS base
ARG foo=default_value
COPY $foo .In multi-stage builds, each FROM instruction starts a new build stage and resets the scope of ARG variables. You must redeclare the ARG in each stage where you need it.
Problem - ARG loses scope after FROM:
ARG VERSION=latest
FROM ubuntu:${VERSION}
# VERSION is still available here for FROM
ARG FILENAME
COPY ${FILENAME} /app/
# FILENAME works in this stage
FROM alpine
# FILENAME is now undefined here!
COPY ${FILENAME} /app/ # This failsSolution - Redeclare ARG in each stage:
ARG VERSION=latest
FROM ubuntu:${VERSION} AS builder
ARG FILENAME
COPY ${FILENAME} /app/
RUN build-something
FROM alpine
ARG FILENAME
COPY ${FILENAME} /app/The redeclared ARG inherits the value from --build-arg automatically.
If you need to use an ARG in a FROM instruction (like for dynamic base images), declare it before the FROM:
# Declare ARG before FROM
ARG ALPINE_VERSION=3.20
FROM alpine:${ALPINE_VERSION}
# If you need the value after FROM, redeclare it
ARG ALPINE_VERSION
RUN echo "Using Alpine ${ALPINE_VERSION}"Note that the ARG declared before FROM is only available in the FROM instruction itself. To use it in subsequent instructions, you must redeclare it after FROM.
Make sure to pass the required build arguments using the --build-arg flag:
# Single argument
docker build --build-arg FILENAME=app.tar.gz -t myapp .
# Multiple arguments
docker build \
--build-arg VERSION=1.0.0 \
--build-arg ENV=production \
-t myapp .If you don't pass a value and there's no default, the ARG will be empty (not undefined, but empty string).
Using docker-compose:
services:
app:
build:
context: .
args:
- VERSION=1.0.0
- ENV=productionDocker BuildKit can detect typos and suggest corrections. If you see a message like:
Usage of undefined variable '$PAHT' (did you mean $PATH?)Review your Dockerfile for spelling mistakes:
# Wrong
ARG MY_VARIABLE
RUN echo ${MY_VARAIBLE} # Typo!
# Correct
ARG MY_VARIABLE
RUN echo ${MY_VARIABLE}Use consistent naming conventions (e.g., all uppercase for ARG names) to reduce typo risk.
Environment variables defined with ENV always override ARG instructions with the same name. This can cause unexpected behavior:
FROM alpine
ARG MY_VAR=from_arg
ENV MY_VAR=from_env
# MY_VAR will always be "from_env", not what you pass via --build-arg
RUN echo ${MY_VAR}Solution: Use different names for ARG and ENV, or explicitly set ENV from ARG:
FROM alpine
ARG BUILD_VERSION=1.0.0
ENV APP_VERSION=${BUILD_VERSION}
RUN echo "Build version: ${BUILD_VERSION}"
RUN echo "App version: ${APP_VERSION}"Always provide sensible default values for ARG instructions that should work without --build-arg:
FROM node:18
# Required arg - will be empty if not provided
ARG APP_ENV
# Optional args with defaults
ARG NODE_ENV=production
ARG PORT=3000
ARG LOG_LEVEL=info
ENV NODE_ENV=${NODE_ENV}
ENV PORT=${PORT}
ENV LOG_LEVEL=${LOG_LEVEL}For truly required arguments, add validation:
ARG REQUIRED_VAR
RUN test -n "${REQUIRED_VAR}" || (echo "REQUIRED_VAR must be set" && exit 1)ARG vs ENV Scope:
- ARG: Available only during build, from declaration point until next FROM (or end of Dockerfile)
- ENV: Available during build (after declaration) AND at container runtime
- ARG values are NOT accessible in running containers unless explicitly copied to ENV
BuildKit UndefinedVar Check:
Docker BuildKit includes a build check called UndefinedVar that validates variable declarations. It doesn't evaluate undefined variables in RUN, CMD, and ENTRYPOINT instructions using shell form, since the shell resolves those. The check helps catch typos early by suggesting similar variable names.
Predefined ARGs:
Docker has predefined ARGs that don't require declaration: HTTP_PROXY, HTTPS_PROXY, FTP_PROXY, NO_PROXY, and their lowercase variants. These can be used without declaring them first:
FROM alpine
RUN wget --proxy=${HTTP_PROXY} http://example.com/fileSecurity Considerations:
Never pass secrets via ARG - they're visible in image history and build logs. Use Docker BuildKit's secret mounts instead:
# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecretdocker build --secret id=mysecret,src=./secret.txt .dockerfile parse error line 5: unknown instruction: RRUN
How to fix 'unknown instruction' Dockerfile parse error in Docker
Error response from daemon: manifest for nginx:nonexistent not found: manifest unknown: manifest unknown
How to fix 'manifest for image:tag not found' in Docker
Error response from daemon: invalid reference format: repository name must be lowercase
How to fix 'repository name must be lowercase' in Docker
Error response from daemon: No such image
How to fix 'No such image' in Docker
Error response from daemon: Container is not running
How to fix 'Container is not running' when using docker exec