DevToolBoxFREE
BlogAdvertise

Docker Compose Secrets & 環境変数:正しい方法

10分by DevToolBox

環境変数は Docker Compose でコンテナを設定する主要なメカニズムです。しかし、4つの異なる渡し方があり、それぞれ異なる動作、セキュリティへの影響、注意点があります。このガイドでは、シンプルな .env ファイルから本番グレードの Docker secrets まで、マルチ環境セットアップの実践的なパターンを網羅します。

Docker Compose で環境変数を渡す4つの方法

Docker Compose はコンテナに環境変数を注入する4つの方法をサポートしています。それぞれの使い分けを理解することが、保守しやすく安全な設定の鍵です。

1. environment ディレクティブでインライン定義

docker-compose.yml にキーバリューペアを直接定義します。機密性がなく、サービス固有で、めったに変更されない設定に最適です。

services:
  api:
    image: node:20-alpine
    environment:
      NODE_ENV: production
      DB_HOST: postgres
      DB_PORT: "5432"
      LOG_LEVEL: info

  # List syntax (also valid)
  worker:
    image: node:20-alpine
    environment:
      - NODE_ENV=production
      - QUEUE_NAME=tasks
      - CONCURRENCY=4

2. env_file で外部ファイルを読み込み

1つ以上の外部ファイルから変数を読み込みます。関連する変数のグループ管理やバージョン管理からのシークレット除外に最適です。

services:
  api:
    image: node:20-alpine
    env_file:
      - .env              # Shared variables
      - .env.api          # Service-specific variables
      - .env.local        # Local overrides (gitignored)

  db:
    image: postgres:16
    env_file:
      - .env
      - .env.db

3. シェル環境変数のパススルー

ホストシェルの変数をコンテナに渡します。CI/CD パイプラインや動的な値に最適です。

# In your shell:
export API_KEY=sk-abc123
export DEBUG=true

# docker-compose.yml
services:
  api:
    image: node:20-alpine
    environment:
      - API_KEY          # Passes host $API_KEY into container
      - DEBUG            # Passes host $DEBUG into container
      - CI               # Passes host $CI (empty string if unset)

4. docker-compose.yml での変数展開

Compose ファイルの値にホスト環境変数を直接展開します。Compose ファイルを環境間で設定可能にするのに最適です。

# .env file
POSTGRES_VERSION=16
APP_PORT=3000
REPLICAS=3

# docker-compose.yml
services:
  db:
    image: postgres:${POSTGRES_VERSION}    # Resolved from .env or shell
  app:
    ports:
      - "${APP_PORT}:3000"                 # Resolved from .env or shell
    deploy:
      replicas: ${REPLICAS}               # Resolved from .env or shell

比較表

方法最適な用途VCS に含める?シークレット安全?
environmentNon-sensitive defaultsYesNo
env_fileGrouped config, secretsNo (.gitignore)Partial
Shell passthroughCI/CD, dynamic valuesN/APartial
${} substitutionCompose file templatingYes (file), No (.env)No

.env ファイル:構文、配置、展開

Docker Compose は docker-compose.yml と同じディレクトリにある .env ファイルを自動的に読み取ります。設定の外部化で最も一般的な方法です。

構文ルール

# Comments start with #
# Blank lines are ignored

# Simple key=value (no spaces around =)
DB_HOST=localhost
DB_PORT=5432

# Values with spaces need quotes
APP_NAME="My Docker App"
GREETING='Hello World'

# Multi-line values (double quotes only)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"

# Variable expansion (Compose v2.20+)
BASE_URL=https://api.example.com
HEALTH_URL=${BASE_URL}/health

# Export prefix is ignored (compatible with shell source)
export SECRET_KEY=mysecretkey

# Empty values
EMPTY_VAR=
ALSO_EMPTY=''

ファイル配置と優先順位

Compose はプロジェクトディレクトリ(docker-compose.yml のある場所)で .env を探します。--env-file フラグで上書きできます:

# Default: reads .env from project directory
docker compose up

# Override with --env-file
docker compose --env-file .env.staging up

# Multiple --env-file flags (Compose v2.24+)
docker compose --env-file .env --env-file .env.local up

# Project directory structure:
my-project/
  docker-compose.yml    # Compose file
  .env                  # Auto-loaded by Compose
  .env.staging          # Loaded with --env-file
  .env.production       # Loaded with --env-file

docker-compose.yml での展開

.env の変数は docker-compose.yml での置換に使えますが、自動的にコンテナには注入されません:

# .env
TAG=3.2.1
EXTERNAL_PORT=8080

# docker-compose.yml
services:
  web:
    image: myapp:${TAG}              # Uses TAG from .env -> myapp:3.2.1
    ports:
      - "${EXTERNAL_PORT}:80"        # Uses EXTERNAL_PORT from .env -> 8080:80
    environment:
      - TAG                           # WRONG! This passes host $TAG, not .env TAG
      - TAG=${TAG}                   # RIGHT! Explicitly interpolate .env value

# Key insight:
# .env -> docker-compose.yml interpolation   (automatic)
# .env -> container environment              (NOT automatic, use env_file)

env_file vs environment:違いと注意点

これら2つのディレクティブは似ていますが動作が異なります。違いを理解することで微妙なバグを防げます。

ディレクティブ動作
environment:Inlined in docker-compose.yml. Visible in version control. Supports variable substitution. Overrides env_file.
env_file:Loaded from external file(s). Can be gitignored. No variable substitution inside the file. Lower priority than environment.
.env (auto)Only for Compose file interpolation (${} syntax). NOT injected into containers. Lowest priority.

重要な注意点

  • 1. 優先順位:environment の値は同じキーの env_file の値を上書きします。これは上書きのために意図的に設計されています。
  • 2. .env ファイル(自動読み込み)は Compose ファイルの展開専用です。env_file または environment を明示的に使わない限り、コンテナには注入されません。
  • 3. env_file のパスは docker-compose.yml ファイルからの相対パスで、現在の作業ディレクトリからではありません。
  • 4. env_file が見つからない場合、Compose はエラーで失敗します。required: false(Compose v2.24+)でオプションにできます。
# Precedence demonstration:

# .env.shared
DB_HOST=shared-db
DB_PORT=5432

# docker-compose.yml
services:
  api:
    env_file:
      - .env.shared              # DB_HOST=shared-db, DB_PORT=5432
    environment:
      DB_HOST: override-db       # Overrides env_file! DB_HOST=override-db
      # DB_PORT not listed here  # Keeps env_file value: DB_PORT=5432

# Result inside container:
# DB_HOST=override-db    (from environment, overrides env_file)
# DB_PORT=5432           (from env_file)

# Optional env_file (Compose v2.24+):
services:
  api:
    env_file:
      - path: .env.local
        required: false          # Won't fail if file doesn't exist

Docker Secrets:本番環境のファイルベースのシークレット

Docker secrets は機密データに対して環境変数よりも安全な代替手段を提供します。シークレットは環境変数としてではなく、コンテナ内にファイルとしてマウントされます。

ステップ1:シークレットファイルを作成

# Create secret files (don't commit these!)
echo "SuperSecretPassword123" > db_password.txt
echo "sk-prod-abc123xyz789" > api_key.txt

# Add to .gitignore
echo "*.txt" >> .gitignore
echo "secrets/" >> .gitignore

# Alternative: use a secrets directory
mkdir secrets
echo "SuperSecretPassword123" > secrets/db_password
echo "sk-prod-abc123xyz789" > secrets/api_key

ステップ2:docker-compose.yml でシークレットを定義

# docker-compose.yml
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password  # Note: _FILE suffix
    secrets:
      - db_password                    # Grant access to this secret

  api:
    image: myapp:latest
    secrets:
      - db_password
      - api_key
    # Secrets available at:
    # /run/secrets/db_password
    # /run/secrets/api_key

# Top-level secrets definition
secrets:
  db_password:
    file: ./secrets/db_password        # From local file
  api_key:
    file: ./secrets/api_key            # From local file

  # Alternative: use environment variable as source
  # jwt_secret:
  #   environment: JWT_SECRET_VALUE    # From host env var (Compose v2.23+)

ステップ3:アプリケーションでシークレットを読み取る

# Inside the container, secrets are plain text files:
$ docker compose exec api cat /run/secrets/db_password
SuperSecretPassword123

$ docker compose exec api cat /run/secrets/api_key
sk-prod-abc123xyz789

$ docker compose exec api ls -la /run/secrets/
total 8
-r--r--r-- 1 root root 24 Jan  1 00:00 db_password
-r--r--r-- 1 root root 22 Jan  1 00:00 api_key

ステップ4:コードでシークレットを処理

// Node.js - Read secret from file
const fs = require('fs');

function getSecret(name) {
  const secretPath = `/run/secrets/${name}`;
  try {
    return fs.readFileSync(secretPath, 'utf8').trim();
  } catch (err) {
    // Fallback to environment variable (for development)
    return process.env[name.toUpperCase()];
  }
}

const dbPassword = getSecret('db_password');
const apiKey = getSecret('api_key');
# Python - Read secret from file
import os

def get_secret(name: str) -> str:
    secret_path = f"/run/secrets/{name}"
    try:
        with open(secret_path) as f:
            return f.read().strip()
    except FileNotFoundError:
        # Fallback to environment variable
        return os.environ.get(name.upper(), "")

db_password = get_secret("db_password")
api_key = get_secret("api_key")

環境変数よりも Secrets を選ぶ理由

  • 環境変数は docker inspect、/proc/environ、エラーログ、クラッシュダンプで漏洩する可能性があります
  • シークレットはファイルベースで、コンテナ内の /run/secrets/ でのみアクセス可能です
  • シークレットはサービスごとのきめ細かなアクセス制御をサポートします
  • 多くのデータベースやサービスは _FILE サフィックスのシークレットファイルをネイティブサポートしています(例:POSTGRES_PASSWORD_FILE)

変数展開:${VAR:-default} 構文

Docker Compose はデフォルト値とエラー処理を含むシェルスタイルの変数展開をサポートしています。

構文結果
${VAR}Value of VAR. Error if unset.
${VAR:-default}Value of VAR if set, otherwise "default".
${VAR-default}Value of VAR if set (even if empty), otherwise "default".
${VAR:?error msg}Value of VAR if set, otherwise exit with "error msg".
${VAR?error msg}Value of VAR if set (even if empty), otherwise exit with "error msg".
${VAR:+replacement}"replacement" if VAR is set and non-empty, otherwise empty.
${VAR+replacement}"replacement" if VAR is set (even if empty), otherwise empty.

実践例

# docker-compose.yml with variable substitution
services:
  web:
    image: nginx:${NGINX_VERSION:-1.25-alpine}     # Default: 1.25-alpine
    ports:
      - "${WEB_PORT:-80}:80"                        # Default: 80
      - "${SSL_PORT:-443}:443"                      # Default: 443
    volumes:
      - ${CONFIG_PATH:-./nginx.conf}:/etc/nginx/nginx.conf:ro

  api:
    image: ${REGISTRY:-docker.io}/myapp:${TAG:?TAG is required}
    #                                     ^ Fails if TAG is not set
    environment:
      NODE_ENV: ${NODE_ENV:-development}
      LOG_LEVEL: ${LOG_LEVEL:-info}
      DATABASE_URL: postgres://${DB_USER:-postgres}:${DB_PASS:?DB_PASS required}@db:5432/${DB_NAME:-myapp}

  db:
    image: postgres:${PG_VERSION:-16}
    volumes:
      - ${DATA_DIR:-./data}/postgres:/var/lib/postgresql/data

マルチ環境セットアップ:開発/ステージング/本番パターン

実際のプロジェクトには開発、ステージング、本番の異なる設定が必要です。実証済みのパターンを紹介します。

パターン1:オーバーライドファイル(推奨)

ベースの docker-compose.yml と環境固有のオーバーライドファイルを使用します。Compose が自動的にマージします。

# File structure:
project/
  docker-compose.yml              # Base configuration
  docker-compose.override.yml     # Dev overrides (auto-loaded!)
  docker-compose.staging.yml      # Staging overrides
  docker-compose.prod.yml         # Production overrides
# docker-compose.yml (base)
services:
  api:
    image: myapp:${TAG:-latest}
    restart: unless-stopped
    depends_on:
      - db
      - redis

  db:
    image: postgres:16
    volumes:
      - pg-data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine

volumes:
  pg-data:
# docker-compose.override.yml (development - auto-loaded)
services:
  api:
    build: .
    volumes:
      - .:/app                          # Hot reload
      - /app/node_modules
    ports:
      - "3000:3000"
      - "9229:9229"                     # Debugger
    environment:
      NODE_ENV: development
      LOG_LEVEL: debug
      DB_HOST: db

  db:
    ports:
      - "5432:5432"                     # Expose DB for local tools
    environment:
      POSTGRES_PASSWORD: devpassword    # OK for dev only
# docker-compose.prod.yml (production)
services:
  api:
    # No build, no volumes, no debug port
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      LOG_LEVEL: warn
    env_file:
      - .env.production
    secrets:
      - db_password
      - api_key
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '1.0'
          memory: 512M

  db:
    # No exposed ports
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1G

secrets:
  db_password:
    file: ./secrets/db_password
  api_key:
    file: ./secrets/api_key
# Usage:

# Development (auto-loads docker-compose.override.yml)
docker compose up

# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Validate merged config before deploying
docker compose -f docker-compose.yml -f docker-compose.prod.yml config

パターン2:環境固有の .env ファイル

--env-file フラグで環境ごとに異なる変数セットを読み込みます。

# .env.development
TAG=latest
APP_PORT=3000
DB_PASSWORD=devpassword
LOG_LEVEL=debug
REPLICAS=1

# .env.staging
TAG=rc-1.2.3
APP_PORT=3000
DB_PASSWORD=staging-secret-pw
LOG_LEVEL=info
REPLICAS=2

# .env.production
TAG=1.2.3
APP_PORT=3000
DB_PASSWORD=prod-ultra-secret-pw
LOG_LEVEL=warn
REPLICAS=3

# Usage:
docker compose --env-file .env.development up       # Dev
docker compose --env-file .env.staging up -d         # Staging
docker compose --env-file .env.production up -d      # Production

パターン3:オプショナルサービスの Profiles

Compose profiles で環境ごとにサービスを含めたり除外したりします。

# docker-compose.yml
services:
  api:
    image: myapp:${TAG:-latest}
    ports:
      - "3000:3000"

  db:
    image: postgres:16
    volumes:
      - pg-data:/var/lib/postgresql/data

  # Dev-only services
  adminer:
    image: adminer
    ports:
      - "8080:8080"
    profiles:
      - dev                           # Only starts with --profile dev

  mailhog:
    image: mailhog/mailhog
    ports:
      - "1025:1025"
      - "8025:8025"
    profiles:
      - dev                           # Only starts with --profile dev

  # Monitoring (staging + production)
  prometheus:
    image: prom/prometheus
    profiles:
      - monitoring                    # Only starts with --profile monitoring

volumes:
  pg-data:

# Usage:
docker compose --profile dev up                     # Includes adminer + mailhog
docker compose --profile monitoring up -d           # Includes prometheus
docker compose --profile dev --profile monitoring up  # Both profiles

セキュリティチェックリスト:環境変数に絶対入れてはいけないもの

環境変数は便利ですが、機密データには本質的に安全ではありません。以下のルールに従ってください:

絶対にやってはいけない代わりにこうする
docker-compose.yml にパスワードをハードコード.env ファイル(gitignore済み)または Docker secrets を使用
.env ファイルをバージョン管理にコミット.env を .gitignore に追加し、代わりに .env.example をコミット
環境変数で秘密鍵や証明書を渡すボリュームまたは Docker secrets でファイルとしてマウント
アプリケーションコードで環境変数をログに記録ログで機密値をマスク
Slack、メール、チャットで .env ファイルを共有シークレットマネージャー(Vault、AWS Secrets Manager、1Password)を使用
開発/ステージング/本番で同じシークレットを使用環境ごとに一意のシークレットを生成
# .gitignore — essential entries for Docker projects
.env
.env.*
!.env.example
secrets/
*.pem
*.key
*.crt
# .env.example — commit this as a template
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USER=myapp
DB_PASSWORD=CHANGE_ME

# API Keys
API_KEY=CHANGE_ME
JWT_SECRET=CHANGE_ME

# Application
NODE_ENV=development
LOG_LEVEL=debug
APP_PORT=3000

デバッグ:環境変数がロードされない理由

環境変数がコンテナに届かない場合、この体系的なフローチャートに従ってください。

ステップ1:変数は定義されていますか?

.env ファイル、env_file、または environment ディレクティブに変数が存在するか確認します。

ステップ2:コンテナに届いていますか?

docker compose exec <service> env を実行してコンテナ内のすべての環境変数を確認します。

ステップ3:.env ファイルは正しい場所にありますか?

.env ファイルは docker-compose.yml と同じディレクトリに置くか、--env-file で指定します。

ステップ4:名前の競合はありますか?

タイポ、大文字小文字の区別、優先順位(environment > env_file > .env)を確認します。

ステップ5:Compose ファイルは有効ですか?

docker compose config を実行して、すべての変数が展開された完全な設定を確認します。

ステップ6:変数が上書きされていませんか?

シェル環境変数は .env ファイルの値を上書きします。ホストで echo $VAR_NAME で確認してください。

便利なデバッグコマンド

# 1. See the fully resolved Compose config (all variables substituted)
docker compose config

# 2. Check environment variables inside a running container
docker compose exec api env

# 3. Check a specific variable
docker compose exec api sh -c 'echo $DATABASE_URL'

# 4. Inspect container config (shows env vars, mounts, etc.)
docker inspect <container_id> --format '{{json .Config.Env}}' | jq .

# 5. Check if .env file is being read
docker compose config --format json | jq '.services.api.environment'

# 6. Verify env_file exists and is readable
docker compose config 2>&1 | grep -i "env_file"

# 7. Run a one-off container to test environment
docker compose run --rm api env | sort

# 8. Check for shell variable conflicts
env | grep -i "DB_"

# 9. Validate Compose file syntax
docker compose config --quiet && echo "Valid" || echo "Invalid"

# 10. Show variable substitution warnings
docker compose --verbose up 2>&1 | grep -i "variable"

よくある質問

Docker Compose の .env と env_file の違いは?

.env ファイルは Docker Compose が docker-compose.yml 自体の変数展開のために自動的に読み取ります。env_file ディレクティブは変数をコンテナ環境に直接ロードします。用途が異なります:.env は Compose ファイルの展開用、env_file はコンテナ環境注入用です。

ホストからDockerコンテナに環境変数を渡すには?

environment ディレクティブで値なしの変数名をリスト(例:"- MY_VAR")するか、docker-compose.yml で ${MY_VAR} 変数展開構文を使用します。docker compose up 実行前にホストシェルで変数を設定しておく必要があります。

Docker の環境変数は安全ですか?

いいえ。環境変数は docker inspect で表示でき、コンテナ内の /proc/environ に表示され、アプリケーションでログに記録される可能性があります。パスワードや API キーなどの機密データには Docker secrets またはシークレットマネージャーを使用してください。

Docker Compose の環境変数の優先順位は?

高い順に:1) docker compose run -e、2) ホストのシェル環境変数、3) docker-compose.yml の environment ディレクティブ、4) env_file ディレクティブ、5) Dockerfile の ENV 命令。高優先度が低優先度を上書きします。

開発と本番で異なる環境変数を使うには?

オーバーライドファイルを使用:共有設定のベース docker-compose.yml を作成し、開発用に docker-compose.override.yml、本番用に docker-compose.prod.yml を用意します。本番は docker compose -f docker-compose.yml -f docker-compose.prod.yml up で実行します。

Docker secrets と環境変数の違いは?

Docker secrets は環境変数ではなくファイル(/run/secrets/<secret_name>)としてマウントされます。docker inspect、プロセスリスト、クラッシュダンプに表示されないためより安全です。多くの公式 Docker イメージは _FILE サフィックス規則をサポートしています。

𝕏 Twitterin LinkedIn
この記事は役に立ちましたか?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy