Docker Compose 환경변수 변경이 적용되지 않을 때: restart와 재생성 구분
.env를 고친 뒤 컨테이너를 재시작했는데도 이전 설정이 남는다면, 먼저 같은 컨테이너를 다시 시작한 것인지, 새 설정으로 컨테이너를 다시 만든 것인지를 구분해야 한다.
docker compose restart는 Compose 설정 변경을 반영하는 명령이 아니다. Docker 공식 설명도 환경변수 변경이 재시작만으로 갱신되지 않는다고 명시한다. Docker restart 문서
하지만 곧바로 강제 재생성부터 하는 것도 정답은 아니다. Compose가 읽은 값 자체가 예전 값이면 새 컨테이너에도 잘못된 값이 들어갈 수 있다. 이 글은 설정 해석 → 컨테이너 반영 → 애플리케이션 사용을 나누어 확인하는 절차다.
검증 기준일은 2026-09-20이다. 공식 Docker Compose CLI 문서를 바탕으로 작성했으며, 아래 실습은 이번 작성 환경에서 Docker로 실행하지 못했다. 제시한 기대값은 독립 테스트 환경에서 확인해야 할 기준이지 운영 서버에서 얻은 성공 기록이 아니다.
1. 무엇을 바꿨는지부터 분리한다
| 바꾼 대상 | 먼저 확인할 것 |
|---|---|
Compose의 environment, 서비스 env_file |
최종 Compose 설정과 실행 컨테이너 환경 |
치환용 .env 또는 --env-file |
해당 값이 Compose 설정에 실제로 연결되는지 |
| 이미지 태그·Dockerfile | 이미지 준비와 컨테이너 교체를 별도로 확인 |
| 마운트한 애플리케이션 설정 파일 | 파일 내용과 프로그램의 재로딩 방식 |
.env에 변수를 적었다는 사실만으로 그 변수가 자동으로 컨테이너에 전달되지는 않는다. 예를 들어 environment에서 참조하거나 서비스의 env_file로 지정하는 연결이 있어야 한다. 이미지의 ENV, 서비스 환경 설정 등 여러 출처도 구분해야 한다. 환경변수 우선순위
이 글의 대상은 컨테이너 생성 시 전달하는 환경변수다. 마운트된 파일을 프로그램이 매 요청마다 읽는지, 시작할 때만 읽는지는 해당 프로그램의 규칙이다. 파일 수정 문제에 환경변수 진단을 그대로 대입하지 않는다.
2. 운영에서는 실행 대상을 먼저 고정한다
프로젝트 디렉터리, Compose 파일, 프로젝트 이름, 추가 override 파일이 달라지면 다른 구성에 명령을 내릴 수 있다. 기존 배포가 사용하는 -f, -p, --env-file, Docker context를 확인한다. 아래 경로와 서비스명은 예시이며 실서버 값으로 무작정 복사하지 않는다.
docker context show
docker compose version
docker compose -f /path/to/compose.yaml -p your-project config --services
docker compose -f /path/to/compose.yaml -p your-project ps -a
기존 배포가 여러 Compose 파일을 합친다면 같은 파일과 순서를 유지해야 한다. 이 글의 이후 운영 예시는 해당 프로젝트의 올바른 디렉터리와 설정 인자를 이미 확인했다는 전제다.
다음 명령은 설정을 출력하지 않고 검증한다. 성공해도 DB 연결이나 앱 설정의 의미까지 확인한 것은 아니다.
docker compose config -q
필요하면 docker compose config로 최종 구성을, docker compose config --environment로 치환 입력을 확인할 수 있다. 두 출력에는 비밀번호·토큰·접속 문자열이 포함될 수 있으므로 공개 게시판이나 채팅에 전체를 붙이지 않는다. config는 적용할 모델을 해석하는 명령이지 현재 컨테이너의 설정을 읽는 명령이 아니다. Docker config 문서
3. .env를 바꿨는데 최종 설정부터 옛 값인 경우
다음과 같이 연결된 구성을 생각해 보자.
services:
app:
image: your-approved-image:fixed-tag
environment:
APP_MODE: ${APP_MODE:?APP_MODE must be set}
이것은 구조 설명용이며 실제 이미지 이름은 아니다. ${...}는 Compose가 해석한다. 같은 이름을 셸에서 export했다면 .env를 고쳐도 셸 값이 우선할 수 있다. 이 경우 컨테이너를 몇 번 교체하느냐보다 Compose에 어떤 값이 들어갔는가가 먼저다. --env-file도 셸 환경보다 우선하는 만능 강제 옵션으로 취급하지 않는다. Compose 변수 치환
비밀이 아닌 APP_MODE 같은 표식으로 먼저 확인한다. 비밀번호를 확인하려고 전체 env를 출력하는 습관은 피한다.
서비스 env_file과 environment가 같은 변수를 정의할 때는 environment가 우선한다. --env-file은 CLI의 치환 입력이고, 서비스 안의 env_file은 컨테이너 환경을 구성하는 속성이다. 둘의 이름이 비슷하다고 같은 역할은 아니다. 환경변수 우선순위
4. restart와 up을 구분하는 작은 실습
아래 실습은 운영 Compose와 분리된 새 디렉터리·테스트 Docker 환경에서만 진행한다. DB·볼륨·호스트 포트·실제 비밀값은 사용하지 않는다. 이미지 다운로드와 작은 테스트 컨테이너 생성은 발생할 수 있다.
compose.yaml을 다음 내용으로 만든다. busybox:1.37.0은 실습용 지정 태그이며 최신·보안 권장 버전이라는 뜻이 아니다. Docker Official Images의 BusyBox 정의에서 해당 태그와 amd64·arm64v8 항목을 확인했다. 실제 이미지 다운로드·실행은 미검증이며, 엄격한 재현이 필요하면 자신의 플랫폼에서 검증한 digest를 사용한다.
services:
probe:
image: busybox:1.37.0
command: ["sleep", "3600"]
environment:
MLOG_DEMO_LABEL: ${MLOG_DEMO_LABEL:?set MLOG_DEMO_LABEL}
network_mode: none
read_only: true
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
같은 디렉터리에 demo.env를 만들고 다음 한 줄을 넣는다.
MLOG_DEMO_LABEL=before
다음 Bash 함수는 매번 같은 파일과 별도 프로젝트를 가리키며, 실습 변수에 한해 셸 우선순위 간섭을 제거한다. 이 프로젝트 이름을 이미 쓰고 있지 않은지 먼저 확인한다. 운영 셸 설정을 일괄 삭제하는 명령이 아니다.
dc() {
env -u MLOG_DEMO_LABEL docker compose \
-f ./compose.yaml -p mlog-env-demo \
--env-file ./demo.env "$@"
}
dc config -q
dc up -d probe
dc ps -q probe
dc exec -T probe printenv MLOG_DEMO_LABEL
첫 관찰 기준은 before다. 컨테이너 ID도 별도로 메모한다. 이어 편집기로 demo.env의 값만 after로 바꾸고 다음 명령을 실행한다.
dc config
dc restart probe
dc ps -q probe
dc exec -T probe printenv MLOG_DEMO_LABEL
이 샘플에는 비밀값이 없으므로 config를 비교할 수 있다. 기대하는 차이는 최종 구성에는 after, 실행 컨테이너에는 여전히 before가 보이는 것이다. 일반적인 재시작에서는 컨테이너 ID도 같다. 이는 여기서 실행해 관측한 출력이 아니라 확인할 기대값이다.
이번에는 변경된 구성을 반영한다.
dc up -d probe
dc ps -q probe
dc exec -T probe printenv MLOG_DEMO_LABEL
Docker는 기존 서비스의 구성이나 이미지 변경을 감지하면 up에서 컨테이너를 재생성한다. 이 예제에서는 새 ID와 after를 확인하는 것이 목표다. 변화가 없다면 먼저 파일·프로젝트·최종 설정을 대조한다. --no-recreate는 기존 컨테이너의 교체를 막으므로 이 문제 해결용 옵션이 아니다. Docker up 문서
실습을 끝낸 뒤 이 샘플의 컨테이너만 정리한다. 종료 전에 현재 디렉터리와 dc 함수의 대상이 실습 프로젝트인지 다시 확인한다.
dc down
여기에는 볼륨이 없으므로 -v가 필요 없다. 이 명령을 운영 프로젝트에서 대신 실행하면 안 된다.
5. 운영 반영은 재생성 범위와 데이터 위치를 확인한 뒤
현재 실행 서비스명이 app이고 환경변수만 바꾼 상황이라면, 검토 후 docker compose up -d app처럼 대상을 제한한다. 재생성에는 서비스 중단이 생길 수 있으므로 무중단을 보장하는 명령으로 소개하지 않는다.
--force-recreate는 구성 변경 감지 여부와 무관하게 교체를 강제한다. 올바른 설정을 확인한 뒤 필요할 때만 사용한다. --no-deps는 연결 서비스 시작을 생략하는 선택이므로, DB가 실제로 실행 중이고 의존 구성이 바뀌지 않았는지 확인하지 않은 상태에서 습관적으로 붙이지 않는다. Docker up 문서
재생성 전에 앱이 컨테이너 쓰기 계층에만 데이터를 저장하고 있지 않은지도 본다. 컨테이너 삭제 시 그 계층의 데이터는 유지되지 않는다. 영속 데이터가 volume 또는 bind mount에 있다는 사실과 실제 백업 복원 가능성을 구분해야 한다. Docker Storage
Ghost·DB처럼 상태가 있는 서비스라면 백업 복원 검증 글을 먼저 참고한다. 환경변수 복원과 데이터베이스 마이그레이션 되돌리기는 같은 작업이 아니다. 이미지 버전까지 함께 바꿨다면 이전 설정만 복사해도 복구된다고 보장할 수 없다.
이 글의 문제를 해결하려고 down -v, 볼륨 삭제, system prune --volumes를 실행하지 않는다. 특히 down -v는 Compose에 선언된 named volume과 연결된 anonymous volume의 삭제 범위를 포함한다. external로 선언된 볼륨은 제거 대상에서 제외된다. 일반 down도 컨테이너·네트워크를 제거하므로 단순 설정 적용보다 영향이 크다. Docker down 문서
6. 새 환경이 보인다고 앱까지 정상인 것은 아니다
실행 중인 서비스에 printenv가 있는 경우, 비밀이 아닌 특정 키만 조회할 수 있다.
docker compose exec -T app printenv APP_MODE
이는 컨테이너에서 새로 실행한 프로세스가 받은 환경을 보는 방법이다. 주 애플리케이션이 그 설정을 사용했는지, 별도 파일·DB 설정이 덮어쓰는지는 따로 확인한다. printenv 없는 최소 이미지에 이 점검만을 위해 도구를 즉석 설치할 필요는 없다. Docker exec 문서
검증은 다음 세 가지로 나눈다.
- 입력 확인: 올바른 파일을 읽었고 최종 구성에 의도한 값이 있는가.
- 컨테이너 확인: 해당 서비스가 새 설정으로 만들어졌고 실행 중인가.
- 기능 확인: 외부 응답, DB 연결, 실제 대상 기능이 정상인가.
세 번째에서 실패하면 환경변수 문제만 반복해서 고치지 않는다. DB 서비스명을 찾지 못한다면 Ghost ENOTFOUND 진단으로, 프록시와 DB의 연결 범위를 바꿨다면 Compose 네트워크 분리로 점검 범위를 옮긴다.
이미지까지 변경할 때도 단계를 분리한다. docker compose pull app은 이미지를 받는 작업이지 실행 컨테이너를 교체하는 작업이 아니다. 이미지 준비 뒤 배포 적용과 기능 검증이 따로 필요하다. 환경변수만 바꾸는 날에 의도치 않은 이미지 업그레이드까지 묶지 않는 편이 원인 분리에 유리하다. Docker pull 문서
결론
설정이 적용되지 않을 때의 질문은 “재시작했나?”보다 구체적이어야 한다. 어떤 입력을 읽었고, 어떤 설정으로 컨테이너를 만들었으며, 앱은 실제로 무엇을 사용했는가?
이 세 단계를 확인하면 셸 변수가 .env를 덮어쓴 경우, 기존 컨테이너를 단순 재시작한 경우, 애플리케이션의 다른 설정이 우선한 경우를 서로 구분할 수 있다. 먼저 최종 설정을 확인하고, 필요한 서비스에 변경을 적용한 뒤 기능으로 검증한다. 데이터 삭제는 그 절차에 포함되지 않는다.