arg是唯一专为构建阶段设计的变量机制,不进镜像、不暴露给运行时,必须在使用前声明且作用域限于当前构建阶段;未设默认值时缺参将导致构建失败,多阶段需重复声明,不可自动跨阶段传递。

直接说结论:ARG 是唯一专为构建阶段设计的变量机制,它不进镜像、不暴露给容器运行时,但必须显式声明、按作用域使用,否则会静默失效或构建失败。
ARG 必须在使用前声明,且作用域只到当前构建阶段末尾
很多人写完 ARG VERSION=1.0 就直接在 FROM ubuntu:${VERSION} 里用,结果报错 “unknown instruction” 或版本没生效——因为 FROM 是阶段起点,ARG 若在它之后声明,对 FROM 不可见。
- 要在
FROM前声明才能用于基础镜像名:ARG BASE_IMAGE=alpine:3.18→FROM $BASE_IMAGE - 若想在
RUN中用,ARG必须出现在该RUN指令之前(同阶段内) - 多阶段构建中,每个
FROM后都是新阶段,ARG需重新声明:哪怕名字一样,前一阶段的ARG也不会自动带入下一阶段 - 未设默认值的
ARG,若构建时没传--build-arg,Docker 18.09+ 会直接拒绝构建(不是警告)
默认值不是摆设,但覆盖逻辑很严格
ARG BUILD_ENV=prod 看似简单,实际行为取决于你是否在构建命令里显式传参、有没有拼错名、以及是否被后续同名 ARG 覆盖。
- 默认值只在
--build-arg完全未提供该参数时生效;哪怕传了空值--build-arg BUILD_ENV=,也会覆盖默认值成空字符串 - 参数名区分大小写:
--build-arg build_env=dev对ARG BUILD_ENV=prod无效 - 同一 Dockerfile 中重复声明同名
ARG(如阶段开头和中间各写一次),后声明的会覆盖前一个,但仅影响其后的指令 - 构建时传多个参数,顺序无关,但每个
--build-arg必须是KEY=VALUE格式,不能漏等号
别把 ARG 当 ENV 用,也别指望它“自动传递”到运行时
ARG 和 ENV 是两套独立机制。常见误区是以为写了 ARG NODE_ENV=production,容器启动后 $NODE_ENV 就有值——其实完全没有。
- 想让构建时决定的值在容器里可用,必须显式转成
ENV:ARG NODE_ENV→ENV NODE_ENV=$NODE_ENV - 如果只想要构建时用(比如控制
RUN apt-get install装什么包),就别碰ENV,避免把本不该留下的逻辑固化进镜像 -
docker run -e NODE_ENV=staging可以覆盖上面ENV的值,但无法影响构建过程本身——运行时环境变量对ARG零作用 - 敏感信息(token、密码)即使通过
ARG传入,也会留在docker history的某一层里,inspect 就能看见;真要安全,得用docker buildx build --secret或挂载文件
CI/CD 中批量传参容易踩空格和换行坑
用 .buildargs 文件配合 $(cat .buildargs | xargs) 是常见做法,但 shell 展开对格式极其敏感。
-
.buildargs必须是 Unix 换行(LF),Windows 的 CRLF 会导致xargs把KEY=VALUE^M当成非法参数 - 值里含空格或特殊字符(如
API_URL=https://a b.com)必须用单引号包裹,且xargs要加-E ''或改用set -o allexport; source .buildargs; set +o allexport - GitHub Actions 中推荐直接展开:
--build-arg COMMIT_SHA=${{ github.sha }},比读文件更可靠 - GitLab CI 中注意
$CI_COMMIT_TAG可能为空,建议加判断或设默认值,避免传空参覆盖ARG默认值
最常被忽略的一点:ARG 的“不可见性”是假象——它不写进镜像配置,但所有用过它的 RUN 指令都会生成独立层,而 docker history --no-trunc 能清楚看到每条命令里展开的值。所谓“安全”,只针对镜像分发后 inspect 不可见,不针对构建过程审计。











