メインコンテンツまでスキップ

第25章:“環境変数で初期化”の設計:最小で安全に🎛️

この章でやることはシンプル!👇 「初期化に必要な値(DB名・ユーザー名・パスワードなど)を、コードやSQLにベタ書きしないで、環境変数(+必要ならsecrets)で渡せる設計」にします 😎📦


1) まず結論:envに入れるのは「変えたい設定」だけ🧠🧩

環境変数に全部入れ始めると、すぐ事故ります🫠 なので、最初はこの2分類でOK👇

✅ envに入れてよい(= 設定・構成)

  • DBホスト(例:db
  • DBポート(例:5432
  • DB名(例:app
  • 実行モード(例:NODE_ENV=development
  • ログレベルなど

🚫 envに入れると事故りやすい(= 秘密)

  • DBパスワード
  • APIキー
  • JWT秘密鍵
  • OAuthクライアントシークレット

秘密をenvに入れると、ログやエラー表示、AI貼り付け、画面共有で漏れる確率が上がります😱🧨 そこで次の章(この章の後半)で Compose secrets を使う「最小の安全策」も入れます🔒✨(secretsはコンテナ内でファイルとして渡せます)(Docker Documentation)


2) Composeには「環境変数の入口」が3つある📮📦📦📦

ここ、初心者が一番混乱するところなので、先に“地図”を置きます🗺️😆

A. .env(プロジェクト直下のやつ)

  • これは主に **compose.yaml内の ${VAR} 展開(interpolation)**に使われる用途です🧩
  • 「コンテナへ入る環境変数」とは別物になりがち(混ぜると混乱)😵‍💫
  • 何が展開に使われたかは docker compose config --environment で確認できます🔎(Docker Documentation)

B. env_file:(サービスに読み込ませるenvファイル)

  • これは コンテナに環境変数を入れる用途📦
  • 複数指定した場合、後ろのファイルが勝つ(上書き)🧯(matsuand.github.io)

C. environment:(compose.yamlに直書き)

  • これも コンテナに環境変数を入れる用途📦
  • env_file より強い(上書き)ので、最後の微調整だけに使うのが安全👍(Docker Documentation)

さらに、同じ変数が複数箇所にあるときの優先順位(どれが勝つ?)も公式で整理されています🧠 「なんで値が変わるの!?」ってなったら、ここが犯人です🕵️‍♂️(Docker Documentation)


3) “初期化で必要なenv”は、DB側とアプリ側で分ける✂️🐘🧑‍💻

例としてPostgreSQLで説明します🐘 公式イメージは、初回起動時に initdb をして、その後 /docker-entrypoint-initdb.d のSQLやシェルを実行して初期化できます🌱(hub.docker.com)

🐘 DBコンテナ(初期化用)でよく使うenv

  • POSTGRES_DB
  • POSTGRES_USER
  • POSTGRES_PASSWORD(これが要。必須扱い)(GitHub)

🧑‍💻 アプリコンテナ(接続用)でよく使うenv

  • DB_HOST
  • DB_PORT
  • DB_NAME
  • DB_USER
  • DB_PASSWORD(ただし後で secrets へ逃がすのが安全🔒)

4) まずは「最小で安全」なファイル構成にする📁✨

おすすめはこれ👇(混乱が減って、チームでも強い)

  • .envcompose.yamlの展開専用(秘密を入れない)
  • env/app.envアプリ用(コンテナに入れる)
  • env/db.envDB用(コンテナに入れる)
  • secrets/秘密(ファイルで渡す)

コツ:.env を「展開専用」にすると、頭がバグらない🧠✨ 逆に「.envを全部に使う」は便利そうに見えて、数日後に混乱して泣きます🥲


5) Compose例(env_file + secrets)🧩🔒

✅ compose.yaml(例)

services:
db:
image: ${POSTGRES_IMAGE}
env_file:
- ./env/db.env
secrets:
- db_password
volumes:
- db-data:/var/lib/postgresql/data
## 第25章:“環境変数で初期化”の設計:最小で安全に🎛️
# ports:
# - "5432:5432"

app:
build: .
env_file:
- ./env/app.env
secrets:
- db_password
depends_on:
- db

secrets:
db_password:
file: ./secrets/db_password.txt

volumes:
db-data:
  • secretsはコンテナ内で /run/secrets/<secret_name> のファイルとして渡されます🔒(Docker Documentation)
  • env_file は「コンテナに入れるenv」用途です📦(Docker Documentation)
  • .env${POSTGRES_IMAGE} みたいな compose.yaml内の展開に使えます🧩(Docker Documentation)

.env(展開専用:秘密入れない)

POSTGRES_IMAGE=postgres:latest

env/db.env(DB初期化用:ユーザーとDB名だけ)

POSTGRES_DB=app
POSTGRES_USER=appuser
## パスワードは secrets に逃がす(ここには書かない)

env/app.env(アプリ接続用:パスワードは secrets から読む)

DB_HOST=db
DB_PORT=5432
DB_NAME=app
DB_USER=appuser

6) TypeScript側:環境変数チェック&secrets対応(最小)🧪✅

「動かない理由がわからない」を潰す最強手段は、起動直後に設定チェックして落ちることです💥(落ち方が親切になる)

// src/config.ts
import fs from "node:fs";

function must(name: string): string {
const v = process.env[name];
if (!v) throw new Error(`Missing env: ${name}`);
return v;
}

function readSecretIfExists(path: string): string | null {
try {
return fs.readFileSync(path, "utf8").trim();
} catch {
return null;
}
}

const DB_PASSWORD =
readSecretIfExists("/run/secrets/db_password") ?? process.env.DB_PASSWORD;

if (!DB_PASSWORD) {
throw new Error("Missing DB password (secret or env DB_PASSWORD)");
}

export const config = {
db: {
host: must("DB_HOST"),
port: Number(must("DB_PORT")),
name: must("DB_NAME"),
user: must("DB_USER"),
password: DB_PASSWORD,
},
};

// 事故防止:ログにパスワードは出さないこと🙅‍♂️

これで、

  • secretsがあるなら secrets を使う🔒
  • なければ env にフォールバック(ローカル緊急用)🧯 ができます✨

7) 事故あるある集(ここが地雷)💣🤣

💥 あるある1:.env に書いたのに反映されない

→ それ、コンテナ用じゃなくて展開用だった可能性大です😵‍💫 .env は主に ${VAR} 展開(interpolation)向けです🧩(Docker Documentation)

✅ 対策:

  • コンテナへ入れるのは env_file or environment
  • 展開に使われた値は docker compose config --environment で確認🔎(Docker Documentation)

💥 あるある2:同じ変数が複数箇所にあって、どれが勝ってるかわからない

→ 公式の優先順位ルールがまさにこれです🕵️‍♂️ (shellの環境変数が勝ったり、environment: が勝ったり)(Docker Documentation)

✅ 対策:

  • 変数の置き場所を減らす(最小に)✂️
  • “最後に勝つ場所” を1つだけにする(おすすめは env_file に寄せる)📦

💥 あるある3:パスワードをenvに入れてAIに貼ってしまう

→ これ、最も多い漏えいパターンです😱 ✅ 対策:

  • 秘密は secrets(ファイル)へ🔒(Docker Documentation)
  • AIに投げるときは、値を *** に置換してから🤖🛡️

8) 今日のミニ課題(10〜15分)⏱️🎮

課題A:構成を分離してみよう📁

  1. .envPOSTGRES_IMAGE だけにする
  2. env/db.envPOSTGRES_DB, POSTGRES_USER を入れる
  3. secrets/db_password.txt を作って secrets で渡す
  4. docker compose up -d で起動!

課題B:反映チェック🔎

docker compose config --environment を実行して、展開に使われた値を見てみよう!(Docker Documentation)


9) AIの使い方(安全版)🤖🛡️✨

  • ✅ 「env設計メモ」を貼って、“この変数は設定?秘密?” の分類を出してもらう
  • compose.yaml の雛形を作ってもらう(値はダミーにする)
  • ✅ 起動エラー文(秘密は伏せる)から原因候補を3つ出させる

🚫 ダメ:本物のパスワード/APIキーを貼る🔐❌


まとめ:第25章のゴール🏁🎉

  • envは「設定」、secretsは「秘密」🔒
  • .env(展開)と env_file(コンテナ)は役割が違う🧩
  • 迷ったら docker compose config --environment で確認🔎(Docker Documentation)
  • PostgreSQLの初期化は “初回にenv + init scripts” が基本形🌱(hub.docker.com)

次の章に進むなら、ここで作った構成をベースに「Windowsで重いときの逃げ道」に繋げると、開発体験が一気に良くなります🐢➡️🐇✨