目次
Docker 環境変数チュートリアル:.env と secrets で設定・機密情報を管理する
同じイメージでも、開発環境、テスト環境、本番環境では接続先のデータベースや設定が異なります。こうした設定は環境変数に入れ、コンテナの実行時に渡すのが一般的です。
-e で設定する
$ docker run --rm -e NODE_ENV=production -e PORT=8080 alpine:3.23 sh -c "echo \$NODE_ENV \$PORT"
production 8080
$ export API_KEY=abc123 && docker run --rm -e API_KEY alpine:3.23 sh -c "echo API_KEY=\$API_KEY"
API_KEY=abc123
-e 名前=値で値を直接設定します。- 値を指定せず
-e 名前だけを書くと、ホストにある同名の環境変数を引き継ぎます。
--env-file でファイルを読み込む
変数が多いと、コマンドに直接書くのは管理しにくくなります。ファイルにまとめましょう。
$ cat app.env
# app.env
NODE_ENV=production
PORT=8080
GREETING="hello world"
DATABASE_URL=postgres://app:secret@db:5432/app
$ docker run --rm --env-file app.env alpine:3.23 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=e311730363f7
NODE_ENV=production
PORT=8080
GREETING="hello world"
DATABASE_URL=postgres://app:secret@db:5432/app
HOME=/root
GREETING の行に注目してください。ファイルに書いた値が "hello world" の場合、コンテナ内の値にも引用符が含まれます。docker run --env-file のルールは次のとおりです。
- 1 行に
KEY=VALUEを 1 つ書きます。#で始まる行はコメントです。 - 値を引用符で囲まないでください。引用符も値の一部として扱われます。
$OTHER_VARによる変数展開には対応していません。
(後で見るように、Compose で同じファイルを読み込むと引用符が取り除かれます。動作が異なります。)
Dockerfile でデフォルト値を設定する
ENV NODE_ENV=production \
PORT=3000
ENV はデフォルト値を設定します。docker run -e で上書きできます。変わることが少なく、機密情報でもない設定に適しています。
優先順位
優先順位の高い順です。
docker run -edocker run --env-file- Dockerfile の
ENV - ベースイメージの
ENV
環境変数は見える?
環境変数は便利ですが、安全な保管場所ではありません。docker inspect を実行できる人なら誰でも値を確認できます。
$ docker run -d --name envdemo -e SECRET_TOKEN=abc123 alpine:3.23 sleep 60
431168d97dc8187ec79e420638155b2f53fd78a9d82f8a6d13eb6bd9aec09bcb
$ docker inspect --format '{{json .Config.Env}}' envdemo
["SECRET_TOKEN=abc123","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]
$ docker rm -f envdemo
envdemo
また、プログラムのエラー時に環境変数がログへ出力されることがあり、子プロセスにも引き継がれます。パスワードや API キーには、「機密情報を扱う」の方法を使ってください。
Docker Compose の環境変数
Compose には混同しやすい「環境変数」が 2 種類あります。例を見てみましょう。compose.yaml:
services:
app:
image: alpine:3.23
command: ["sh", "-c", "env | sort | grep -E 'APP_|DATABASE|NODE_ENV|PORT|GREETING'"]
environment:
APP_VERSION: ${APP_VERSION}
DATABASE_PASSWORD: ${DB_PASSWORD}
env_file:
- app.env
1. コンテナに渡す変数:environment と env_file
environment は compose.yaml に直接記述します。env_file はファイルを読み込みます(ここでは先ほどの app.env を使います)。
2. compose.yaml の内容を置換する変数:.env
Compose はプロジェクトフォルダー内の .env ファイルを自動的に読み込み、compose.yaml 内の ${...} を置換します。docker compose config を使うと、置換後の最終設定を確認できます。
$ cat .env
APP_VERSION=1.4.2
DB_PASSWORD=change-me
$ docker compose config app
name: ch15
services:
app:
command:
- sh
- -c
- env | sort | grep -E 'APP_|DATABASE|NODE_ENV|PORT|GREETING'
environment:
APP_VERSION: 1.4.2
DATABASE_PASSWORD: change-me
DATABASE_URL: postgres://app:secret@db:5432/app
GREETING: hello world
NODE_ENV: production
PORT: "8080"
image: alpine:3.23
networks:
default: null
networks:
default:
name: ch15_default
$ docker compose run --rm app
[+] run 1/1
✔ Network ch15_default Created 0.0s
Container ch15-app-run-dfbcf95b7271 Creating
Container ch15-app-run-dfbcf95b7271 Created
APP_VERSION=1.4.2
DATABASE_PASSWORD=change-me
DATABASE_URL=postgres://app:secret@db:5432/app
GREETING=hello world
NODE_ENV=production
PORT=8080
${APP_VERSION}と${DB_PASSWORD}が.envの値に置換されました。app.envのGREETING="hello world"は、Compose で読み込むと引用符が取り除かれます。docker run --env-fileとは異なります。PORT: "8080"は文字列であることを示すため、引用符が付いています(環境変数はすべて文字列です)。
.env の内容は自動的にはコンテナに渡されません。.env は compose.yaml 内の置換に使われるだけです。コンテナに渡すには、environment で参照する(例:APP_VERSION: ${APP_VERSION})か、env_file: .env を指定します。
デフォルト値と必須項目のチェック
| 書き方 | 意味 |
|---|---|
${VAR} | VAR の値に置換 |
${VAR:-デフォルト値} | 未設定または空の場合にデフォルト値を使用 |
${VAR:?エラーメッセージ} | 未設定または空の場合にエラーで停止 |
${VAR:?} は、パスワードのように「必ず設定する」変数に適しています。設定を忘れるとすぐに処理が停止し、空のパスワードで起動するのを防げます。
services:
db:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${MISSING_PASSWORD:? .env で MISSING_PASSWORD を設定してください}
$ docker compose -f compose.required.yaml config
error while interpolating services.db.environment.POSTGRES_PASSWORD: required variable MISSING_PASSWORD is missing a value: .env で MISSING_PASSWORD を設定してください
機密情報を扱う
- パスワードを Dockerfile の
ENVやARGに書かないでください。イメージ内に残ります(ARGの検証結果はDockerfile 命令リファレンスを参照してください)。 .envを Git にコミットしないでください。.gitignoreに.envを追加し、実際の値を含まない.env.exampleを別途用意します。.dockerignoreに.envを追加し、COPY . .でイメージに含まれないようにします。
ファイルで機密情報を渡す(Compose secrets)
多くの公式イメージは、_FILE で終わる変数を使ってファイルからパスワードを読み込めます。
services:
db:
image: postgres:18
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
$ docker compose up -d db
[+] up 1/1
✔ Container ch15-db-1 Started 0.3s
$ docker compose exec db sh -c "ls -l /run/secrets; cat /run/secrets/db_password; echo; env | grep POSTGRES"
total 4
-rw-rw-r-- 1 1001 1001 16 Oct 12 14:25 db_password
s3cret-from-file
POSTGRES_PASSWORD_FILE=/run/secrets/db_password
$ docker compose down
[+] down 2/2
✔ Container ch15-db-1 Removed 0.1s
✔ Network ch15_default Removed 0.1s
機密情報は /run/secrets/db_password にファイルとしてコンテナ内に置かれます。環境変数に入るのはファイルパス POSTGRES_PASSWORD_FILE だけなので、docker inspect でパスワード自体を見ることはできません。
ビルド時に必要な機密情報
たとえば、プライベートパッケージのインストールにトークンが必要な場合は、BuildKit の secret マウントを使います。
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci
docker build --secret id=npm_token,env=NPM_TOKEN -t myapp .
トークンはこの RUN の実行中だけ利用でき、どのレイヤーにも残りません。
まとめ
- 優先順位:
-e/environment>--env-file/env_file> DockerfileENV。 .envはcompose.yaml内の${VAR}の置換に使われ、コンテナには自動で渡されません。docker run --env-fileは値の引用符を取り除きませんが、Compose は取り除きます。- 機密情報はファイル(secrets)で渡し、イメージに書き込まないでください。
練習問題
練習 1:イメージに ENV MODE=prod が設定されています。docker run -e MODE=dev を実行した場合、コンテナ内の MODE は何になりますか?
解答
dev です。-e は Dockerfile の ENV より優先されます。
練習 2:compose.yaml に POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required} と書かれていますが、.env に DB_PASSWORD がありません。docker compose up を実行するとどうなりますか?
解答
Compose は DB_PASSWORD is required と表示してエラーで停止します。空のパスワードでデータベースを作成することはありません。
よくある質問
.env を変更したのにコンテナの環境変数が変わらないのはなぜですか?
環境変数はコンテナの作成時に決まります。変更を反映するにはコンテナを再作成してください。Compose では docker compose up -d を使います。restart では反映されません。
docker inspect でパスワードが見えてしまいますか?
はい。-e で渡した値はすべて docker inspect に表示されます。機密情報はファイル(secrets)で渡してください。
${VAR:-デフォルト値} と ${VAR:?メッセージ} の違いは何ですか?
前者は未設定の場合にデフォルト値を使います。後者は未設定の場合にエラーで停止します。空文字列で起動するのを防げるため、必須のパスワードに適しています。
更新日 2026年10月14日