Docker .env를 고쳤는데 왜 안 바뀔까

고쳤는데, 그대로다

NAS에서 돌아가는 블로그 자동화 서비스의 설정값을 하나 바꿔야 했습니다. .env 파일을 열어 값을 고치고, 컨테이너를 다시 올렸습니다. 그런데 아무것도 바뀌지 않았습니다. 파일을 다시 열어봐도 분명히 수정된 상태였습니다.

결론부터 말하면, 원인이 하나가 아니었습니다. 세 가지가 겹쳐 있었고, 그중 둘은 검색해도 잘 나오지 않는 종류였습니다. 이 글은 그 과정을 그대로 적은 기록입니다.

이 글은 Docker와 Docker Compose가 이미 설치되어 있고, 리눅스 계열 서버(또는 NAS)에서 컨테이너를 운영 중이라고 가정합니다.

원인 1: 같은 이름이면 environment가 우선입니다

Compose 파일에서 환경변수를 넣는 방법은 두 가지입니다. env_file로 파일을 통째로 읽어오는 방법과, environment에 직접 적는 방법입니다.

services:
  backend:
    env_file:
      - .env
    environment:
      DATABASE_URL: postgresql://user:${POSTGRES_PASSWORD}@postgres:5432/db
      MONTHLY_BUDGET: 5

핵심은 이겁니다. 같은 이름의 변수가 env_fileenvironment에 모두 있으면, environment에 지정한 값이 우선 적용됩니다. 위 설정에서 DATABASE_URL.env에 적어도 반영되지 않습니다. Compose 파일에 같은 이름이 이미 있기 때문입니다.

제 경우가 정확히 이랬습니다. 양쪽에 중복으로 들어 있는 변수가 세 개 있었고, 하필 제가 고치려던 게 그중 하나였습니다. 파일은 분명히 바뀌었는데 컨테이너는 옛날 값을 쥐고 있던 이유입니다.

지금 내 프로젝트가 어떤 상태인지는 아래 명령으로 확인할 수 있습니다. Compose가 최종적으로 해석한 값을 보여줍니다.

docker compose config

Compose는 여러 곳에서 온 설정을 하나로 합친 뒤 컨테이너에 넘깁니다. 그 과정에 우선순위가 정해져 있기 때문에, 파일을 고치는 것만으로는 결과를 확신할 수 없습니다. 최종 결과를 확인하는 명령이 따로 있는 이유입니다.

원인 2: 컨테이너는 만들어질 때 값을 받습니다

환경변수는 컨테이너를 생성할 때 주입됩니다. 이미 만들어진 컨테이너 안으로 나중에 스며들지 않습니다. 그래서 다음 세 명령의 결과가 전부 다릅니다.

# 기존 컨테이너를 재시작만 한다 → 예전 값 그대로
docker compose restart

# 변경을 감지해 컨테이너를 다시 만든다 → .env 반영됨
docker compose up -d

# 이미지까지 다시 굽는다 → 소스코드 변경까지 반영됨
docker compose up --build -d

restart는 기존 컨테이너를 재시작하기만 하므로, .env 변경은 반영되지 않습니다. 습관적으로 restart를 치고 있었다면 여기서 막힙니다.

--build가 필요한지는 Dockerfile을 보면 압니다. 제 백엔드는 COPY . .로 소스를 이미지 안에 굽습니다. 코드를 고쳤으면 이미지를 다시 만들어야 반영됩니다. 반대로 볼륨으로 마운트한 폴더(프롬프트, 저장소 등)는 파일만 바꾸면 바로 반영됩니다. 같은 프로젝트 안에서도 규칙이 다르니, 한 번은 확인해 둘 만합니다.

Docker Compose는 컨테이너를 생성하는 시점에 환경변수를 확정합니다. 그래서 이미 만들어진 컨테이너는 재시작만으로 값을 다시 읽지 않고, 컨테이너 자체를 새로 만들어야 새 값이 들어갑니다.

원인 3: 공유 폴더로 편집한 흔적

NAS를 쓰면 공유 폴더를 윈도우 탐색기로 열 수 있습니다. 편해서 메모장으로 .env를 고쳤습니다. 나중에 서버에서 파일 목록을 보니 이렇게 되어 있었습니다.

$ ls -l

-rwxrwx--- 1 계정명 admin  .env          ← 공유 폴더로 저장한 파일
-rwx------ 1 root   root   .gitignore    ← 서버에서 직접 만든 파일

차이가 보이시나요. 서버에서 직접 만든 파일은 소유자가 root인데, 공유 폴더를 통해 저장한 파일은 소유자가 다른 계정이고 실행 권한(x)까지 붙어 있습니다. 설정 파일에 실행 권한이 붙을 이유는 없습니다.

다만 이건 일반 법칙이라기보다 제 환경의 결과입니다. 제 NAS 환경에서는 공유 폴더를 통해 저장하면서 파일 소유자와 권한이 예상과 다르게 바뀌었습니다. NAS 기종, 공유 설정, 접속 계정에 따라 결과는 달라질 수 있으니, 각자 확인해 보시는 편이 정확합니다.

문제는 770이라는 권한입니다. 같은 그룹에 속한 계정이면 누구나 읽고 쓸 수 있다는 뜻인데, .env 안에는 API 키, 데이터베이스 비밀번호, 인증 토큰이 들어 있습니다. 예전에 백업해 둔 .env.bak 파일들도 같은 상태로 방치돼 있었습니다.

정리했습니다. 소유자를 root로 되돌리고, 권한을 600으로 조입니다.

# 정리 전
-rwxrwx--- 1 계정명 admin  .env
-rwxrwx--- 1 계정명 admin  .env.bak.xxxx

# 실행
$ chown root:root .env
$ chmod 600 .env .env.bak.*
$ ls -l .env*

# 정리 후
-rw------- 1 root root  .env
-rw------- 1 root root  .env.bak.xxxx

600은 소유자만 읽고 쓸 수 있다는 뜻입니다. 그룹도, 다른 사용자도 아예 접근하지 못합니다. 실행 권한도 사라졌습니다. 설정 파일에는 이게 맞습니다.

이렇게 하면 공유 폴더에서는 더 이상 못 고칩니다. 불편해 보이지만, 그게 맞습니다. 리눅스는 파일을 만든 주체에 따라 소유자와 권한이 결정되므로, 설정 파일은 서버에서 nanovi로 고치는 편이 예측 가능하고 안전합니다.

덤: 그런데 프로젝트가 어디 있더라

확인하려고 폴더에 들어갔는데 .envdocker-compose.yml도 없었습니다. 잠시 당황했습니다. 예전에 프로젝트 폴더 이름을 바꿔놓고 잊어버린 것이었습니다. 껍데기 폴더만 남아 있었고, 저는 그 안에서 열심히 명령을 치고 있었습니다.

이럴 때는 컨테이너에게 직접 물어보면 됩니다.

docker inspect 컨테이너이름 --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'

Compose로 만든 컨테이너에는 어느 폴더에서 생성됐는지가 라벨로 기록됩니다. 그래서 폴더를 뒤지고 다닐 필요 없이, 컨테이너 쪽에서 역으로 경로를 찾을 수 있습니다.

정리

.env를 고쳤는데 반영이 안 된다면, 아래 순서로 확인해 보세요.

  • docker compose configenvironment가 같은 이름으로 덮어쓰고 있진 않은가
  • docker compose up -drestart가 아니라 up으로 컨테이너를 다시 만들었는가
  • □ Dockerfile에 COPY가 있는가 — 있다면 --build가 필요하다
  • ls -l .env — 권한이 600인가, 소유자가 root인가

그리고 한 가지 더. 제 .env에는 .env.example에 없는 변수가 네댓 개 있었습니다. 기능을 붙일 때마다 .env에만 추가하고 예제 파일은 안 고친 결과입니다. 나중에 이 프로젝트를 다른 곳에 다시 세우려면 그만큼 헤맵니다. 변수 하나 늘릴 때 예제 파일도 같이 고치는 습관이, 나중의 자신을 구합니다.

관련 글: NAS Docker 컨테이너 데이터 자동 백업 및 복구 가이드, FastAPI와 PostgreSQL 연동하기, API 호출 실패, 언제 재시도하고 언제 포기할까

비슷한 삽질을 하고 계신 분께 도움이 됐다면, 구독이나 공유로 알려 주세요.

댓글

이 블로그의 인기 게시물

UGREEN DXP4800PLUS NAS로 유튜브 쇼츠 자동화 시스템 구축기 (Docker · FastAPI · PostgreSQL · Edge-TTS)

YouTube Data API 쿼터 초과를 피하는 방법 (700개 쇼츠 수집 사례)

FastAPI app.mount 사용 후 API가 404가 되는 이유와 해결 방법