SSD Nodes Learn 8GB RAM — 연 $66
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-01

Docker Compose .env, env_file, environment 차이

Docker Compose에서 .env, env_file, environment는 서로 다른 방식입니다. 변수 보간, 컨테이너 환경, 우선순위와 비밀번호를 둘 위치를 명령으로 확인합니다.

사람들이 env 파일이라고 부르는 3가지

Docker Compose에는 이름이 비슷해 혼동하기 쉬운 별도의 메커니즘이 3가지 있습니다. .env 파일은 Compose가 파일을 파싱하기도 전에 compose.yaml 자체에 있는 ${VARIABLE} 자리 표시자를 채웁니다. env_file: 속성은 키/값 쌍으로 구성된 파일을 컨테이너의 환경에 로드합니다. environment: 속성은 compose 파일에 직접 작성하여 컨테이너에 변수를 설정합니다. 이들은 서로 대체할 수 없습니다. 2가지 메커니즘이 같은 키를 설정하면 어떤 값이 적용되는지는 문서화된 우선순위에 따라 고정됩니다.

이 가이드에서는 각 메커니즘이 작동하는 방식을 설명하고, 실행할 수 있는 명령으로 우선순위를 확인합니다. 그런 다음 더 중요한 내용을 다룹니다. 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이 표시됩니다. 보간은 파싱 시점에 수행되므로 자리 표시자가 사라졌습니다. Compose는 프로젝트 디렉터리에서 .env을 찾습니다. 프로젝트 디렉터리는 compose 파일이 있는 디렉터리입니다. 그런 다음 찾은 모든 ${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

environment는 변수를 인라인으로 설정합니다

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는 아무 값도 설정하지 않으며 경고도 표시하지 않습니다. 이러한 자동 전달 실패를 알아 두어야 합니다. 비어 있는 password 변수를 사용해 시작된 서비스는 대개 정상적으로 시작되지만, 외부에 완전히 노출될 수 있습니다.

어느 설정이 우선하는가

Docker는 우선순위를 높은 순서로 문서화합니다. 명령줄의 docker compose run -e이 가장 높고, 그다음은 셸 또는 env 파일에서 값이 보간되는 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가 사용하는 보간 변수를 출력합니다. “내 env 파일이 무시된다”는 보고의 대부분은 서로 다른 두 수준에서 값이 중복 설정된 경우입니다.

환경 변수가 유출되는 이유

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"가 평문으로 포함됩니다. 동일한 값을 노출하는 경로가 3개 더 있습니다. 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 Official Images에서 사용하는 규칙입니다. 이러한 entrypoint 스크립트는 VARNAME_FILE를 확인하고 파일을 읽은 후 그 내용을 사용합니다. 이는 Docker 기능이 아니므로 이미지가 이를 구현한 경우에만 작동합니다. SOMETHING_FILE가 적용된다고 가정하기 전에 이미지 문서를 확인합니다. 이를 지원하지 않는 애플리케이션은 시작할 때 파일을 직접 읽을 수 있습니다. 또는 경로를 전달하고 자체 entrypoint에서 처리할 수 있습니다.

실행 중인 컨테이너 내부에서 확인합니다.

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

첫 번째 명령은 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는 파일을 이미 설정된 mode로 생성하므로, 모든 사용자가 읽을 수 있는 상태가 되는 순간이 없습니다. 파일의 소유자는 root이므로 서버의 비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는 줄의 나머지 전체를 값으로 처리하므로, 따옴표가 일반적으로 값에 리터럴 문자로 포함됩니다. = 주위에 공백을 넣지 마십시오. 그러면 키에 후행 공백이 포함되어 일치하는 항목이 없어집니다.