> ## Content Index
> Fetch the complete content index at: https://mlog.me/llms.txt
> Use this file to discover other available public pages before exploring further.

# Docker Compose 환경변수 변경이 적용되지 않을 때: restart와 재생성 구분
- URL: https://mlog.me/docker-compose-env-change-restart-vs-up/
- Published: 2026-09-20T08:53:46.000Z
- Updated: 2026-09-20T08:53:46.000Z
- Description: Docker Compose에서 .env를 바꿔도 설정이 그대로라면 restart와 재생성을 구분해야 합니다. 변수 우선순위, config 확인, 대상 서비스 적용과 안전한 검증 절차를 정리합니다.
- Author: mLog
- Tags: 서버·인프라, Docker

`.env`를 고친 뒤 컨테이너를 재시작했는데도 이전 설정이 남는다면, 먼저 **같은 컨테이너를 다시 시작한 것인지, 새 설정으로 컨테이너를 다시 만든 것인지**를 구분해야 한다.

`docker compose restart`는 Compose 설정 변경을 반영하는 명령이 아니다. Docker 공식 설명도 환경변수 변경이 재시작만으로 갱신되지 않는다고 명시한다. [Docker restart 문서](https://docs.docker.com/reference/cli/docker/compose/restart/?ref=mlog.me)

하지만 곧바로 강제 재생성부터 하는 것도 정답은 아니다. Compose가 읽은 값 자체가 예전 값이면 새 컨테이너에도 잘못된 값이 들어갈 수 있다. 이 글은 **설정 해석 → 컨테이너 반영 → 애플리케이션 사용**을 나누어 확인하는 절차다.

검증 기준일은 2026-09-20이다. 공식 Docker Compose CLI 문서를 바탕으로 작성했으며, 아래 실습은 이번 작성 환경에서 Docker로 실행하지 못했다. 제시한 기대값은 독립 테스트 환경에서 확인해야 할 기준이지 운영 서버에서 얻은 성공 기록이 아니다.

## 1\. 무엇을 바꿨는지부터 분리한다

| 바꾼 대상                               | 먼저 확인할 것                    |
| ----------------------------------- | --------------------------- |
| Compose의 environment, 서비스 env\_file | 최종 Compose 설정과 실행 컨테이너 환경   |
| 치환용 .env 또는 \--env-file             | 해당 값이 Compose 설정에 실제로 연결되는지 |
| 이미지 태그·Dockerfile                   | 이미지 준비와 컨테이너 교체를 별도로 확인     |
| 마운트한 애플리케이션 설정 파일                   | 파일 내용과 프로그램의 재로딩 방식         |

`.env`에 변수를 적었다는 사실만으로 그 변수가 자동으로 컨테이너에 전달되지는 않는다. 예를 들어 `environment`에서 참조하거나 서비스의 `env_file`로 지정하는 연결이 있어야 한다. 이미지의 `ENV`, 서비스 환경 설정 등 여러 출처도 구분해야 한다. [환경변수 우선순위](https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/?ref=mlog.me)

이 글의 대상은 컨테이너 생성 시 전달하는 환경변수다. 마운트된 파일을 프로그램이 매 요청마다 읽는지, 시작할 때만 읽는지는 해당 프로그램의 규칙이다. 파일 수정 문제에 환경변수 진단을 그대로 대입하지 않는다.

## 2\. 운영에서는 실행 대상을 먼저 고정한다

프로젝트 디렉터리, Compose 파일, 프로젝트 이름, 추가 override 파일이 달라지면 다른 구성에 명령을 내릴 수 있다. 기존 배포가 사용하는 `-f`, `-p`, `--env-file`, Docker context를 확인한다. 아래 경로와 서비스명은 예시이며 실서버 값으로 무작정 복사하지 않는다.

```bash
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 연결이나 앱 설정의 의미까지 확인한 것은 아니다.

```bash
docker compose config -q

```

필요하면 `docker compose config`로 최종 구성을, `docker compose config --environment`로 치환 입력을 확인할 수 있다. 두 출력에는 비밀번호·토큰·접속 문자열이 포함될 수 있으므로 공개 게시판이나 채팅에 전체를 붙이지 않는다. `config`는 적용할 모델을 해석하는 명령이지 현재 컨테이너의 설정을 읽는 명령이 아니다. [Docker config 문서](https://docs.docker.com/reference/cli/docker/compose/config/?ref=mlog.me)

## 3\. .env를 바꿨는데 최종 설정부터 옛 값인 경우

다음과 같이 연결된 구성을 생각해 보자.

```yaml
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 변수 치환](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/?ref=mlog.me)

비밀이 아닌 `APP_MODE` 같은 표식으로 먼저 확인한다. 비밀번호를 확인하려고 전체 `env`를 출력하는 습관은 피한다.

서비스 `env_file`과 `environment`가 같은 변수를 정의할 때는 `environment`가 우선한다. `--env-file`은 CLI의 치환 입력이고, 서비스 안의 `env_file`은 컨테이너 환경을 구성하는 속성이다. 둘의 이름이 비슷하다고 같은 역할은 아니다. [환경변수 우선순위](https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/?ref=mlog.me)

## 4\. restart와 up을 구분하는 작은 실습

아래 실습은 운영 Compose와 분리된 **새 디렉터리·테스트 Docker 환경**에서만 진행한다. DB·볼륨·호스트 포트·실제 비밀값은 사용하지 않는다. 이미지 다운로드와 작은 테스트 컨테이너 생성은 발생할 수 있다.

`compose.yaml`을 다음 내용으로 만든다. `busybox:1.37.0`은 실습용 지정 태그이며 최신·보안 권장 버전이라는 뜻이 아니다. [Docker Official Images의 BusyBox 정의](https://github.com/docker-library/official-images/blob/master/library/busybox?ref=mlog.me)에서 해당 태그와 amd64·arm64v8 항목을 확인했다. 실제 이미지 다운로드·실행은 미검증이며, 엄격한 재현이 필요하면 자신의 플랫폼에서 검증한 digest를 사용한다.

```yaml
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`를 만들고 다음 한 줄을 넣는다.

```dotenv
MLOG_DEMO_LABEL=before

```

다음 Bash 함수는 매번 같은 파일과 별도 프로젝트를 가리키며, 실습 변수에 한해 셸 우선순위 간섭을 제거한다. 이 프로젝트 이름을 이미 쓰고 있지 않은지 먼저 확인한다. 운영 셸 설정을 일괄 삭제하는 명령이 아니다.

```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`로 바꾸고 다음 명령을 실행한다.

```bash
dc config
dc restart probe
dc ps -q probe
dc exec -T probe printenv MLOG_DEMO_LABEL

```

이 샘플에는 비밀값이 없으므로 `config`를 비교할 수 있다. 기대하는 차이는 최종 구성에는 `after`, 실행 컨테이너에는 여전히 `before`가 보이는 것이다. 일반적인 재시작에서는 컨테이너 ID도 같다. **이는 여기서 실행해 관측한 출력이 아니라 확인할 기대값이다.**

이번에는 변경된 구성을 반영한다.

```bash
dc up -d probe
dc ps -q probe
dc exec -T probe printenv MLOG_DEMO_LABEL

```

Docker는 기존 서비스의 구성이나 이미지 변경을 감지하면 `up`에서 컨테이너를 재생성한다. 이 예제에서는 새 ID와 `after`를 확인하는 것이 목표다. 변화가 없다면 먼저 파일·프로젝트·최종 설정을 대조한다. `--no-recreate`는 기존 컨테이너의 교체를 막으므로 이 문제 해결용 옵션이 아니다. [Docker up 문서](https://docs.docker.com/reference/cli/docker/compose/up/?ref=mlog.me)

실습을 끝낸 뒤 이 샘플의 컨테이너만 정리한다. 종료 전에 현재 디렉터리와 `dc` 함수의 대상이 실습 프로젝트인지 다시 확인한다.

```bash
dc down

```

여기에는 볼륨이 없으므로 `-v`가 필요 없다. 이 명령을 운영 프로젝트에서 대신 실행하면 안 된다.

## 5\. 운영 반영은 재생성 범위와 데이터 위치를 확인한 뒤

현재 실행 서비스명이 `app`이고 환경변수만 바꾼 상황이라면, 검토 후 `docker compose up -d app`처럼 대상을 제한한다. 재생성에는 서비스 중단이 생길 수 있으므로 무중단을 보장하는 명령으로 소개하지 않는다.

`--force-recreate`는 구성 변경 감지 여부와 무관하게 교체를 강제한다. 올바른 설정을 확인한 뒤 필요할 때만 사용한다. `--no-deps`는 연결 서비스 시작을 생략하는 선택이므로, DB가 실제로 실행 중이고 의존 구성이 바뀌지 않았는지 확인하지 않은 상태에서 습관적으로 붙이지 않는다. [Docker up 문서](https://docs.docker.com/reference/cli/docker/compose/up/?ref=mlog.me)

재생성 전에 앱이 컨테이너 쓰기 계층에만 데이터를 저장하고 있지 않은지도 본다. 컨테이너 삭제 시 그 계층의 데이터는 유지되지 않는다. 영속 데이터가 volume 또는 bind mount에 있다는 사실과 실제 백업 복원 가능성을 구분해야 한다. [Docker Storage](https://docs.docker.com/engine/storage/?ref=mlog.me)

Ghost·DB처럼 상태가 있는 서비스라면 [백업 복원 검증 글](https://mlog.me/server-security-check-backup-restore-docker-forensics/)을 먼저 참고한다. 환경변수 복원과 데이터베이스 마이그레이션 되돌리기는 같은 작업이 아니다. 이미지 버전까지 함께 바꿨다면 이전 설정만 복사해도 복구된다고 보장할 수 없다.

이 글의 문제를 해결하려고 `down -v`, 볼륨 삭제, `system prune --volumes`를 실행하지 않는다. 특히 `down -v`는 Compose에 선언된 named volume과 연결된 anonymous volume의 삭제 범위를 포함한다. `external`로 선언된 볼륨은 제거 대상에서 제외된다. 일반 `down`도 컨테이너·네트워크를 제거하므로 단순 설정 적용보다 영향이 크다. [Docker down 문서](https://docs.docker.com/reference/cli/docker/compose/down/?ref=mlog.me)

## 6\. 새 환경이 보인다고 앱까지 정상인 것은 아니다

실행 중인 서비스에 `printenv`가 있는 경우, 비밀이 아닌 특정 키만 조회할 수 있다.

```bash
docker compose exec -T app printenv APP_MODE

```

이는 컨테이너에서 새로 실행한 프로세스가 받은 환경을 보는 방법이다. 주 애플리케이션이 그 설정을 사용했는지, 별도 파일·DB 설정이 덮어쓰는지는 따로 확인한다. `printenv` 없는 최소 이미지에 이 점검만을 위해 도구를 즉석 설치할 필요는 없다. [Docker exec 문서](https://docs.docker.com/reference/cli/docker/compose/exec/?ref=mlog.me)

검증은 다음 세 가지로 나눈다.

1. **입력 확인:** 올바른 파일을 읽었고 최종 구성에 의도한 값이 있는가.
2. **컨테이너 확인:** 해당 서비스가 새 설정으로 만들어졌고 실행 중인가.
3. **기능 확인:** 외부 응답, DB 연결, 실제 대상 기능이 정상인가.

세 번째에서 실패하면 환경변수 문제만 반복해서 고치지 않는다. DB 서비스명을 찾지 못한다면 [Ghost ENOTFOUND 진단](https://mlog.me/ghost-getaddrinfo-enotfound-db-docker-compose-mysql/)으로, 프록시와 DB의 연결 범위를 바꿨다면 [Compose 네트워크 분리](https://mlog.me/docker-compose-network-isolation-npm-ghost-mysql/)로 점검 범위를 옮긴다.

이미지까지 변경할 때도 단계를 분리한다. `docker compose pull app`은 이미지를 받는 작업이지 실행 컨테이너를 교체하는 작업이 아니다. 이미지 준비 뒤 배포 적용과 기능 검증이 따로 필요하다. 환경변수만 바꾸는 날에 의도치 않은 이미지 업그레이드까지 묶지 않는 편이 원인 분리에 유리하다. [Docker pull 문서](https://docs.docker.com/reference/cli/docker/compose/pull/?ref=mlog.me)

## 결론

설정이 적용되지 않을 때의 질문은 “재시작했나?”보다 구체적이어야 한다. **어떤 입력을 읽었고, 어떤 설정으로 컨테이너를 만들었으며, 앱은 실제로 무엇을 사용했는가?**

이 세 단계를 확인하면 셸 변수가 `.env`를 덮어쓴 경우, 기존 컨테이너를 단순 재시작한 경우, 애플리케이션의 다른 설정이 우선한 경우를 서로 구분할 수 있다. 먼저 최종 설정을 확인하고, 필요한 서비스에 변경을 적용한 뒤 기능으로 검증한다. 데이터 삭제는 그 절차에 포함되지 않는다.