Docker Composeの.env、env_file、environmentの違い
Docker Composeでenv fileと呼ばれる3つの仕組みを整理します。.env、env_file、environmentの違い、優先順位、秘密情報をSecretsに置く理由をコマンドで確認できます。
env ファイルと呼ばれる 3 つの仕組み
Docker Compose には、名前がよく似ている別々の仕組みが 3 つあります。.env ファイルは、Compose がファイルを解析する前に、compose.yaml 内の ${VARIABLE} プレースホルダーを置き換えます。env_file: 属性は、キーと値の組を記述したファイルをコンテナの環境変数として読み込みます。environment: 属性は、compose ファイルに記述した変数をコンテナに直接設定します。これらは互換性がありません。2 つの仕組みが同じキーを設定した場合、どちらが優先されるかは文書化された優先順位で決まります。
このガイドでは、それぞれの仕組みが動作する様子を示し、実行可能なコマンドで優先順位を検証します。その後、より重要な点を説明します。環境変数は docker inspect を実行できるユーザーなら誰でも読み取れるため、パスワードを環境変数に保存してはいけません。compose ファイル自体に慣れていない場合は、まず VPS 上の Docker Compose の基本 を読み、設定についてはここに戻ってきてください。
.env ファイルはコンテナではなく compose ファイル用です
ディレクトリを作成し、その中に 2 つのファイルを配置します。
mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .envservices:
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 demoprintenv ALPINE_TAG はステータス 1 で終了し、何も出力しません。コンテナ内に変数が存在しないためです。これは最も多い誤解です。.env が設定したのは compose ファイルであり、プロセスではありません。POSTGRES_PASSWORD=hunter2 を含む .env ファイルを用意しても、compose ファイルのどこかでそのファイルを参照しない限り、データベースには何の効果もありません。
${NAME:-default} は、変数が未設定または空の場合にフォールバック値を指定します。${NAME:?message} を指定すると、Compose は起動を拒否し、指定したメッセージを出力します。安全なデフォルト値がない場合は、この方法が適切です。
env_file でコンテナに変数を読み込む
env_file: 属性には、内容をコンテナの環境変数にするファイルを 1 つ以上指定します。
printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.envservices:
demo:
image: alpine:3.20
command: printenv GREETING
env_file:
- ./app.envdocker compose run --rm demoこれは from_env_file を出力します。ファイル形式は通常の KEY=value 行で、1 行に 1 つ記述します。# で始まる行はコメントです。シェルの形式ではありません。ほとんどの場合、引用符は値の一部として保持され、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を使用するリスト形式の2種類の構文を使用できます。動作は同じです。リスト形式には追加の機能があります。値を指定しないキーだけを記述すると、docker composeを実行したシェルからその変数を引き継ぎます。
environment:
- GREETINGGREETING=from_my_shell docker compose run --rm demoこれはfrom_my_shellを出力します。シェルでGREETINGを設定せずに実行すると、Composeは何も設定せず、警告も表示しません。値の引き継ぎが黙って失敗する場合があることを把握しておく必要があります。空のパスワード変数で起動したサービスは正常に起動することが多く、実際には無防備な状態になるためです。
どれが優先されるか
Docker では、優先順位を上位から順に示しています。コマンドラインの docker compose run -e、次にシェルまたは env ファイルから値が補間される environment または env_file、compose ファイル内の通常の environment、次に env_file、最後にイメージへ組み込まれた ENV ディレクティブです。
日常の作業で覚えておくべき点は、environment: が env_file: より優先され、コマンドラインの -e がその両方より優先されることです。1 つのファイルで確認できます。
services:
demo:
image: alpine:3.20
command: printenv GREETING
env_file:
- ./app.env
environment:
GREETING: from_environmentdocker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING最初のコマンドは from_environment を出力します。つまり、environment: が app.env の値を上書きしました。2 番目のコマンドは from_cli を出力します。compose ファイル内の設定で、コマンドラインの指定を上書きするものはありません。
コンテナが設定を適用していないように動作するときは、推測で対応しないでください。docker compose config で完全に解決されたファイルを表示できます。docker compose config --environment では、Compose が使用している補間変数を表示できます。「env ファイルが無視される」という報告の多くは、異なる 2 つのレベルで同じ値を設定していることが原因です。
環境変数が漏えいする理由
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 の secret は値をファイルに保持します
Compose はファイルベースの secret に対応しています。値は環境変数に注入されるのではなく、ファイルとしてコンテナ内にマウントされます。
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txtsecret はコンテナ内の /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_PASSWORD1つ目のコマンドはパスワードを表示します。2つ目のコマンドは何も表示しません。値が環境変数に入っていないためです。これがこの方法の目的です。このコンテナの docker inspect には、安全なパスだけが表示されます。
secret は背後にあるファイルと同じ程度にしか保護されません。そのため、ホスト上の元ファイルを保護してください。
chmod 600 db_password.txtVPS における現実的な中間案
多くのセルフホスト用イメージは _FILE 変数に対応していないため、環境変数が唯一の設定方法になります。1 台の管理者用 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.envinstall -m 600 は、最初からモードを設定した状態でファイルを作成します。そのため、全ユーザーから読み取り可能になる時間が発生しません。ファイルの所有者は root なので、サーバー上の root 以外のユーザーは読み取れません。ただし、docker を実行できるユーザーは、コンテナから値を読み取れます。*.env と .env を .gitignore に追加し、代わりにキー名と空の値を記載した app.env.example をコミットします。コミットされたパスワードは、ローテーションが必要なパスワードです。
値をローテーションするには、サービスを再起動します。環境変数はコンテナプロセスの起動時に 1 回だけ読み込まれるため、ファイルを編集しただけでは変更されません。変更を反映するには docker compose up -d --force-recreate db を実行します。これは、VPS 上で HTTPS 経由で n8n を公開するガイドでも使用しているパターンです。暗号化キーを compose ファイルの外部に置きます。
環境ごとに設定を分割する
Compose はデフォルトでプロジェクトディレクトリから .env を読み込みます。--env-file を使用すると、別の場所を指定できます。
docker compose --env-file .env.staging config複数のファイルは順番に読み込まれ、後のファイルが前のファイルを上書きします。Secret ではないデフォルト値はコミットするファイルに保持し、Secret はサーバーから外部に持ち出さないファイルに保存します。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 は行の残り全体を値として扱うため、引用符が値の文字として含まれることがよくあります。= の前後にはスペースを入れないでください。キーに末尾のスペースが含まれ、そのキーに一致するものがなくなります。