SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-07

Docker Compose .env, env_file, environment 차이점

Docker Compose에서 혼동하기 쉬운 .env와 env_file, environment의 우선순위와 동작 원리를 설명합니다. 각 설정이 컨테이너 환경에 미치는 영향과 비밀번호 관리를 위한 올바른 secrets 사용법을 확인하십시오.

사람들이 env 파일이라 부르는 세 가지 요소

Docker Compose에는 이름이 비슷하여 혼동하기 쉬운 세 가지 별도 메커니즘이 있습니다. .env 파일은 Compose가 파일을 파싱하기 전에 compose.yaml 내부의 ${VARIABLE} 자리 표시자를 채웁니다. env_file: 속성은 키-값 쌍으로 구성된 파일을 컨테이너의 환경 변수로 불러옵니다. environment: 속성은 compose 파일에 직접 작성하여 컨테이너의 변수를 설정합니다. 이들은 서로 대체할 수 없으며, 두 가지 방법으로 동일한 키를 설정할 경우 문서화된 우선순위 규칙에 따라 승자가 결정됩니다.

이 가이드에서는 각 기능을 작동시키는 방법을 보여주고, 실행 가능한 명령어를 통해 우선순위를 증명한 뒤, 더 중요한 부분을 다룹니다. 환경 변수는 docker inspect를 실행할 수 있는 사람이라면 누구나 읽을 수 있으므로, 비밀번호를 환경 변수에 포함해서는 안 됩니다. compose 파일 전반에 대해 처음 접하신다면 VPS에서의 Docker Compose 기초를 먼저 읽고 돌아와 설정을 진행하십시오.

.env 파일은 컨테이너가 아닌 compose 파일을 위한 것입니다

디렉터리를 생성하고 그 안에 두 개의 파일을 넣으십시오.

mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .env
services:
  demo:
    image: alpine:${ALPINE_TAG}
    command: printenv ALPINE_TAG

이제 Compose가 실제로 무엇을 파싱했는지 확인하십시오.

docker compose config

출력 결과는 image: alpine:3.20을 보여줍니다. 파싱 시점에 보간(interpolation)이 발생했기 때문에 플레이스홀더는 사라졌습니다. Compose는 compose 파일이 위치한 프로젝트 디렉터리에서 .env을 찾고, 발견한 모든 ${NAME}을 치환합니다.

그런 다음 서비스를 실행하십시오.

docker compose run --rm demo

printenv ALPINE_TAG는 상태 코드 1로 종료되며 아무것도 출력하지 않습니다. 해당 변수는 컨테이너 내부에 존재하지 않기 때문입니다. 이는 가장 흔히 발생하는 오해입니다. .env은 compose 파일을 구성하는 것이지, 프로세스를 구성하는 것이 아닙니다. POSTGRES_PASSWORD=hunter2가 포함된 .env 파일은 compose 파일의 어떤 부분에서도 이를 참조하지 않는다면 데이터베이스에 아무런 영향을 주지 않습니다.

${NAME:-default}은 변수가 설정되지 않았거나 비어 있을 때 대체 값을 제공합니다. ${NAME:?message}는 Compose가 시작을 거부하고 메시지를 출력하게 만듭니다. 안전한 기본값이 없는 값의 경우 이것이 올바른 선택입니다.

env_file은 컨테이너에 변수를 로드합니다

env_file: 속성은 컨테이너 환경 변수로 변환될 내용을 담은 파일 하나 이상을 지정합니다.

printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.env
services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
docker compose run --rm demo

이 명령은 from_env_file을 출력합니다. 파일 형식은 일반적인 KEY=value 줄이며, 한 줄에 하나씩 작성하고 #로 주석을 시작합니다. 셸 스크립트가 아닙니다. 대부분의 경우 따옴표는 값의 일부로 유지되며, export 접두사는 필요하지 않습니다. = 기호 주변에 공백을 넣지 마십시오. KEY = value은 이름 앞에 공백이 포함된 KEY 라는 이름의 변수를 생성하기 때문입니다.

env_file 경로가 없으면 오류가 발생하며 Compose는 중단됩니다. 파일이 없을 수도 있는 경우라면 선택 사항으로 표시하십시오:

    env_file:
      - path: ./app.env
        required: false

환경 변수 인라인 설정

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    environment:
      GREETING: from_environment

매핑 형식과 - GREETING=from_environment를 사용하는 리스트 형식, 두 가지 문법을 사용할 수 있습니다. 두 방식은 동일하게 동작합니다. 리스트 형식에는 한 가지 추가 기능이 있는데, 값이 없는 키를 입력하면 docker compose를 실행한 셸의 환경 변수를 그대로 전달합니다.

    environment:
      - GREETING
GREETING=from_my_shell docker compose run --rm demo

이 명령은 from_my_shell을 출력합니다. 셸에서 GREETING을 설정하지 않고 실행하면 Compose는 아무것도 설정하지 않으며 경고도 표시하지 않습니다. 이러한 암묵적 전달 실패는 주의해야 합니다. 빈 비밀번호 변수로 시작된 서비스는 성공적으로 실행되더라도 보안이 완전히 노출될 수 있기 때문입니다.

우선순위 결정

Docker는 우선순위 순서를 문서화하고 있으며, 가장 높은 순위부터 나열하면 다음과 같습니다: 명령줄의 docker compose run -e, 셸이나 환경 파일에서 값이 보간되는 environment 또는 env_file, compose 파일의 일반 environment, env_file, 그리고 이미지에 포함된 ENV 지시어 순입니다.

일상적인 작업을 위한 요약: environment:env_file:보다 우선하며, 명령줄의 -e은 두 가지 모두를 앞섭니다. 하나의 파일로 이를 증명해 보겠습니다.

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
    environment:
      GREETING: from_environment
docker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING

첫 번째는 from_environment을 출력하므로, environment:app.env의 값을 덮어썼음을 알 수 있습니다. 두 번째는 from_cli을 출력합니다. compose 파일의 어떤 설정도 명령줄을 덮어쓸 수 없습니다.

컨테이너가 마치 설정이 적용되지 않은 것처럼 동작할 때는 추측하지 마십시오. docker compose config은 완전히 해석된 파일을 출력하며, docker compose config --environment는 Compose가 작업 중인 보간 변수를 출력합니다. "환경 파일이 무시된다"는 대부분의 보고는 서로 다른 두 수준에서 값이 중복 설정된 경우입니다.

환경 변수가 유출되는 이유

environment:에 비밀번호를 설정하면 컨테이너 설정 파일에 저장되어 디스크에 남으며, docker 그룹에 속한 모든 사용자가 이를 볼 수 있습니다.

docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'

출력 결과에는 "DB_PASSWORD=hunter2"가 평문으로 포함됩니다. 세 가지 경로에서 동일한 값이 노출됩니다. docker compose config은 이를 터미널에 출력하며, 이 때문에 지원 포럼에 실수로 붙여넣는 사고가 발생합니다. 컨테이너 내부의 모든 프로세스는 /proc/1/environ을 읽을 수 있으며, 모든 자식 프로세스는 해당 변수를 상속받습니다. 또한 애플리케이션 충돌 처리기는 관례적으로 전체 환경 변수를 로그나 오류 보고서에 덤프합니다.

docker 그룹의 멤버십은 호스트의 root 권한과 다름없으므로, 이를 보안 경계로 의존해서는 안 됩니다. VPS에서 최소 권한 사용자 계정 사용하기 가이드는 공유 서버에서 해당 그룹의 권한을 제한해야 하는 이유를 설명합니다.

Compose secrets는 값을 파일로 유지합니다

Compose는 파일 기반의 secret을 지원합니다. 값은 환경 변수로 주입되는 대신 컨테이너 내부에 파일 형태로 마운트됩니다.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt

secret은 컨테이너 내부의 /run/secrets/db_password 경로에 마운트됩니다. 슬래시 뒤의 이름은 최상위 secrets: 블록에 정의된 secret 이름입니다.

_FILE 접미사는 postgres, mysql, mariadb를 포함한 Docker 공식 이미지에서 사용하는 관례입니다. 해당 엔트리포인트 스크립트는 VARNAME_FILE를 확인하여 파일을 읽고 그 내용을 사용합니다. 이는 Docker의 기능이 아니므로, 이미지가 이를 구현한 경우에만 작동합니다. SOMETHING_FILE이 적용될 것이라고 가정하기 전에 이미지 문서를 확인하십시오. 이를 지원하지 않는 애플리케이션은 시작 시 직접 파일을 읽도록 하거나, 경로를 전달하여 직접 작성한 엔트리포인트에서 처리하게 할 수 있습니다.

실행 중인 컨테이너 내부에서 확인하십시오:

docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORD

첫 번째 명령은 비밀번호를 출력합니다. 두 번째 명령은 아무것도 출력하지 않는데, 이는 값이 환경 변수로 주입되지 않았기 때문입니다. 이것이 바로 핵심입니다. 해당 컨테이너에서 docker inspect을 실행하면 무해한 경로만 표시됩니다.

호스트의 원본 파일을 보호하십시오. secret의 보안 수준은 그 기반이 되는 파일의 보안 수준과 동일하기 때문입니다.

chmod 600 db_password.txt

VPS에서의 실용적인 절충안

많은 셀프 호스팅 이미지는 _FILE 변수를 지원하지 않으므로, 환경 변수가 유일한 설정 방법입니다. 관리자가 한 명인 VPS 환경에서 현실적인 목표는 프로젝트 디렉터리의 누구나 읽을 수 있는 파일에 값을 방치하지 않고, 이를 git에서 제외하는 것입니다.

sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env
    env_file:
      - /etc/myapp/app.env

install -m 600 명령은 파일 생성 시점에 권한을 미리 설정하므로, 누구나 파일을 읽을 수 있는 취약한 상태가 발생하지 않습니다. 파일 소유자는 root이므로 서버의 일반 사용자는 해당 파일을 읽을 수 없습니다. 다만, docker 명령을 실행할 수 있는 사용자라면 컨테이너 내부에서 값을 읽어낼 수 있습니다. *.env.env.gitignore에 추가하고, 대신 키 이름만 있고 값은 비어 있는 app.env.example 파일을 커밋하십시오. 커밋된 비밀번호는 이미 유출된 비밀번호와 같습니다.

값을 교체하려면 서비스를 재시작해야 합니다. 환경 변수는 컨테이너 프로세스가 시작될 때 한 번만 읽히므로, docker compose up -d --force-recreate db 명령을 실행하기 전까지는 파일을 수정해도 아무런 변화가 없습니다. 이는 VPS에서 HTTPS 뒤에 n8n 구성하기 가이드에서 사용된 방식과 동일하며, 암호화 키를 compose 파일 외부에 보관합니다.

환경별 설정 분리

Compose는 기본적으로 프로젝트 디렉터리에서 .env을 읽습니다. --env-file을 사용하여 다른 경로를 지정할 수 있습니다.

docker compose --env-file .env.staging config

여러 파일이 순서대로 읽히며, 나중에 읽히는 파일이 앞선 파일의 설정을 덮어씁니다. 보안과 무관한 기본값은 커밋되는 파일에 유지하고, 비밀 정보는 서버 밖으로 나가지 않는 파일에 보관하십시오. 이는 env_file:에도 동일하게 적용되며, 중복된 키가 있을 경우 마지막에 나열된 파일의 값이 우선합니다.

FAQ

컨테이너 내부에서 .env 파일이 무시되는 이유는 무엇입니까?

무시되는 것이 아닙니다. .env 파일은 compose 파일 내의 ${NAME} 자리표시자를 대체할 뿐입니다. 컨테이너 내부의 변수를 직접 설정하지는 않습니다. 컨테이너 내부로 값을 전달하려면 environment: { KEY: "${NAME}" }와 같이 참조하거나, 대신 env_file: ./that-file.env를 사용하십시오.

environment가 env_file을 덮어쓰나요, 아니면 그 반대인가요?

environment:가 우선합니다. Docker의 공식 문서에 따르면 environment 속성이 env_file 속성보다 우선하며, 둘 다 명령줄의 docker compose run -e보다는 낮은 우선순위를 갖습니다. 동일한 키가 두 곳 모두에 설정되어 있다면 env_file의 값은 무시됩니다.

Compose가 사용할 최종 값을 어떻게 확인합니까?

docker compose config을 실행하면 모든 보간이 적용되어 완전히 해석된 compose 파일을 출력할 수 있습니다. 이미 실행 중인 컨테이너라면 docker inspect <container> --format '{{json .Config.Env}}'를 통해 해당 프로세스가 실제로 전달받은 값을 확인할 수 있습니다.

Compose secret은 암호화됩니까?

아니요. 파일 기반 secret은 /run/secrets/<name> 경로에 일반 파일 형태로 컨테이너에 마운트되며, 호스트 디스크의 원본 파일도 암호화되지 않은 상태로 저장됩니다. 이 기능의 이점은 암호화가 아니라 범위 제한입니다. 값이 컨테이너 환경 변수에 노출되지 않고, docker inspect 출력 결과나 환경 변수를 포함하는 크래시 덤프에 남지 않게 됩니다.

env 파일에서 따옴표와 공백을 사용할 수 있습니까?

KEY=value with spaces를 사용하고 따옴표는 생략하십시오. Compose는 줄의 나머지 전체를 값으로 처리하므로, 따옴표를 넣으면 따옴표 자체가 값의 일부로 포함됩니다. = 주변에는 절대 공백을 두지 마십시오. 공백이 포함되면 키에 후행 공백이 생겨 일치하는 항목을 찾을 수 없게 됩니다.