Docker3 min read

Docker ARG와 ENV가 다르게 동작할 때: 빌드 시점과 실행 시점을 나눠 확인하기

2026년 10월 21일3 min read작성자 Flature

ARG는 빌드 중에 사용하는 값이고, ENV는 이미지에 저장되어 컨테이너 실행 시 기본 환경변수가 됩니다. 같은 Dockerfile을 빌드하고 실행 값을 바꾸는 예제로 적용 시점, 변수 범위, 셸 확장 여부를 구분합니다.

Docker ARG와 ENV가 다르게 동작할 때: 빌드 시점과 실행 시점을 나눠 확인하기

Docker 빌드 인자와 실행 환경변수가 예상과 다르게 동작하면 값을 사용하는 시점, ARG의 선언 범위, 변수를 확장하는 주체를 순서대로 확인하세요. --build-arg는 빌드에 값을 전달하고, docker run -e는 실행할 컨테이너의 환경변수를 설정합니다. 실행 환경변수를 바꿔도 빌드 중에 만들어 둔 파일이나 바이너리가 다시 생성되지는 않습니다.

먼저 값이 필요한 단계를 구분하기

설정적용 단계컨테이너 실행 시 의미
ARG NAME=value선언된 범위의 빌드 단계ARG 자체는 실행 환경변수로 자동 전달되지 않음
ENV NAME=value선언 이후 빌드 단계와 이미지 설정컨테이너 환경변수의 기본값이 됨
docker build --build-arg NAME=valueDockerfile에 선언한 ARG에 값 전달이미지에 반영되는 방식은 Dockerfile의 사용처에 달림
docker run -e NAME=value새 컨테이너 실행같은 이름의 이미지 ENV 기본값을 덮어씀

예를 들어 프런트엔드 번들 생성 명령이 빌드 중에 API 주소를 읽어 파일에 넣었다면, 실행할 때 같은 이름의 환경변수를 바꿔도 그 파일 내용은 바뀌지 않습니다. 실행 시 설정을 바꾸려면 애플리케이션이나 시작 스크립트가 실행 환경변수를 읽도록 설계되어 있어야 합니다.

진단 순서: 전달한 값부터 실제 사용처까지 따라가기

  1. 실제로 사용한 빌드·실행 명령을 확인합니다. 빌드 값은 --build-arg NAME=value, 실행 값은 -e NAME=value로 명시해 비교합니다. 호스트 셸에 변수가 있다는 사실만으로 Dockerfile에 자동 전달되지는 않습니다.
  2. ARG 선언 위치를 확인합니다. 값을 사용하는 빌드 단계에 ARG가 선언되어 있어야 합니다. 특히 FROM 이전 ARG와 단계 내부 ARG를 구분합니다.
  3. ARG가 어디에 반영되는지 찾습니다. RUN 명령의 입력인지, ENV에 복사되는지, 생성 파일에 들어가는지에 따라 실행 시 바꿀 수 있는 범위가 달라집니다.
  4. 동일한 이름의 ENV가 있는지 확인합니다. 같은 이름의 ARG와 ENV가 겹치면 이후 RUN에서 ENV 값이 사용될 수 있습니다. 두 설정을 같은 것으로 취급하지 마세요.
  5. 프로세스가 값을 읽는 방법을 확인합니다. 셸이 변수를 확장하는지, 애플리케이션이 환경변수를 직접 읽는지, 설정 파일이 우선하는지 확인합니다.

재현 예제: 빌드 결과와 실행 환경을 따로 출력하기

Docker CLI와 실행 중인 Docker 데몬이 필요합니다. 아래 예제는 Alpine 이미지를 사용하므로 로컬에 이미지가 없다면 가져올 수 있는 네트워크 환경도 필요합니다. 빈 디렉터리에 다음 내용을 Dockerfile로 저장합니다.

FROM alpine:3
ARG BUILD_LABEL=dev
ENV APP_MODE=${BUILD_LABEL}
RUN printf '%s\n' "$BUILD_LABEL" > /build-label.txt
CMD ["sh", "-c", "printf 'built=%s runtime=%s arg=%s\\n' \"$(cat /build-label.txt)\" \"$APP_MODE\" \"${BUILD_LABEL:-unset}\""]

BUILD_LABEL은 빌드 인자입니다. 이 값을 파일에 기록하고, ENV를 통해 APP_MODE의 기본값으로도 저장합니다. CMD는 컨테이너가 시작될 때 파일 내용과 환경변수를 출력합니다.

docker build --build-arg BUILD_LABEL=staging -t arg-env-demo .
docker run --rm arg-env-demo
docker run --rm -e APP_MODE=production arg-env-demo

위 Dockerfile과 명령에서 기대하는 출력은 각각 다음과 같습니다.

built=staging runtime=staging arg=unset
built=staging runtime=production arg=unset
  • built=staging: 빌드 중 생성한 파일의 내용입니다. 실행 환경변수를 바꿔도 유지됩니다.
  • runtime=production: 두 번째 실행에서는 -e가 이미지의 APP_MODE 기본값을 덮어씁니다.
  • arg=unset: ARG 선언만으로 BUILD_LABEL이 컨테이너 환경에 남지는 않습니다.

실행 시 BUILD_LABEL도 직접 지정하면 차이가 더 분명해집니다.

docker run --rm -e BUILD_LABEL=runtime-only arg-env-demo

기대 출력은 built=staging runtime=staging arg=runtime-only입니다. 실행 환경에 같은 이름의 변수를 추가했지만, 파일과 APP_MODE 기본값은 그대로입니다. ENV APP_MODE=${BUILD_LABEL}은 빌드 시 값을 저장하는 동작이며 두 변수 사이의 지속적인 연결을 만들지 않습니다.

ARG 범위와 CMD 확장에서 자주 놓치는 부분

FROM 이전 ARG는 단계 내부에서 다시 선언하기

ARG BASE_TAG=3
FROM alpine:${BASE_TAG}
ARG BASE_TAG
RUN printf 'base=%s\n' "$BASE_TAG"

첫 ARG는 FROM의 이미지 태그를 정하는 데 사용합니다. 단계 내부 RUN에서도 그 값을 쓰려면 FROM 뒤에서 ARG를 다시 선언해야 합니다. 다단계 빌드에서도 별도의 FROM으로 시작한 독립 단계에 ARG가 자동으로 공유된다고 가정하면 안 됩니다. 값을 쓰는 각 단계의 선언과 상속 관계를 확인하세요.

exec 형식의 CMD는 셸 변수 확장을 수행하지 않음

CMD ["echo", "$APP_MODE"]

이 명령은 셸을 거치지 않으므로 $APP_MODE라는 문자열을 그대로 출력합니다. 앞의 재현 예제는 sh -c를 명시했기 때문에 실행 시 환경변수가 확장됩니다. 실제 서비스에서는 애플리케이션이 환경변수를 직접 읽게 하면 별도의 셸 확장이 필요하지 않습니다.

설정을 바꾸기 전에 남겨야 할 확인 기준

먼저 예제처럼 빌드로 고정된 결과와 실행 시 읽는 값을 따로 관찰하세요. 빌드 결과가 달라져야 한다면 이미지를 다시 빌드하고, 실행 환경만 달라져야 한다면 새 컨테이너를 생성해 확인합니다. 출력이 예상과 다르면 애플리케이션의 설정 우선순위와 시작 스크립트까지 이어서 확인하면 됩니다.

단, 비밀번호나 토큰으로 이 진단을 수행하지 마세요. ARG는 비밀 저장소가 아니며 사용 방식에 따라 빌드 기록이나 결과물 등에 값이 남을 수 있습니다. ENV도 이미지 설정에 저장되므로 비밀값을 이미지에 넣는 용도로 사용하지 않아야 합니다.