docker-compose.yml の書き方完全ガイド【Docker Compose v2対応・MySQL・Redis・Nginx設定サンプル付き】【2026年版】

2017年1月18日


Docker Composeを使い始めたとき「公式ドキュメントを読んだはずなのに、なぜか動かない」「バージョンが変わって書き方が違う」と感じた経験はないだろうか。

このガイドでは、Docker Compose v2への移行後の最新記法に対応した docker-compose.yml の書き方を、実際に動作するサンプルコード付きで解説する。MySQL・Redis・Nginxを組み合わせた本番同等の構成まで実際に手を動かして学べるよう構成した。

筆者は本番環境・開発環境ともにDocker Composeを日常的に使用しており、v1系からv2への移行で遭遇したトラブルも含めて実体験を交えて解説する。

docker-compose.yml の全体構成図(app + MySQL + Redis + Nginx)


Docker Compose v1 vs v2:何が変わったのか

2026年現在、docker-compose(ハイフンあり)コマンドはEnd of Life(EoL)を迎えており、docker compose(スペース区切り)が正式コマンドとなっている。

主な変更点を整理しておく。

項目 v1(旧) v2(現行)
コマンド docker-compose up docker compose up
インストール pip/単体バイナリ Docker CLI プラグイン(同梱)
version: フィールド 必須("3.8" 等) 不要(廃止推奨)
depends_on の条件指定 非対応 condition: service_healthy 対応
プロファイル機能 非対応 profiles: で環境分離可能

version: "3.8" のような記述はもはや必要ない。 Docker Composeのスキーマバージョン管理はEoLとなり、現在は単一スキーマに統一されている。既存のコードに書かれていても動作はするが、新規作成時は省略するのが推奨される。

# v2のバージョン確認
docker compose version
# Docker Compose version v2.24.0

Docker Compose v1とv2のコマンド比較図


前提条件

このガイドを進める前に、以下の環境が必要です。

  • OS: macOS / Linux / Windows(WSL2推奨)
  • Docker: Docker Desktop 4.x 以上 または Docker Engine 24.x 以上
  • 前提知識: コマンドライン基本操作、Dockerの基本概念(コンテナ・イメージ)
# バージョン確認
docker --version
# Docker version 26.x.x

docker compose version
# Docker Compose version v2.24.x

Windowsユーザーへ: Docker DesktopとWSL2の組み合わせを推奨する。WSL2なしのHyper-V構成ではボリュームマウントが遅く、開発体験が著しく悪化する。


全体の流れ

このガイドは以下の5ステップで構成されています。

  1. 基本構文の理解 — 10分
  2. MySQL設定の実装 — 15分
  3. Redis設定の追加 — 10分
  4. Nginx設定の追加 — 15分
  5. 本番同等構成への仕上げ — 20分

ステップ1: docker-compose.ymlの基本構文

最小構成から始めて、徐々に複雑な設定を加えていく。まず単一のWebアプリコンテナを定義する例を見てみよう。

# docker-compose.yml(最小構成)
services:
  app:
    image: nginx:alpine
    ports:
      - "8080:80"

この6行だけで docker compose up を実行すると、ローカルの8080番ポートでNginxが起動する。

サービス定義の主要フィールド

各サービス(コンテナ)で使用頻度の高いフィールドをまとめた。

フィールド 用途
image 使用するDockerイメージ mysql:8.0
build Dockerfileからビルド build: .
ports ポートマッピング ホスト:コンテナ "3306:3306"
volumes ボリュームマウント ./data:/var/lib/mysql
environment 環境変数の設定 MYSQL_ROOT_PASSWORD: secret
depends_on 起動順序の依存関係 depends_on: - db
networks 接続するネットワーク networks: - backend
restart 再起動ポリシー restart: unless-stopped

ビルドとイメージの使い分け

services:
  # パターン1: 公式イメージをそのまま使う
  db:
    image: mysql:8.0

  # パターン2: カレントディレクトリのDockerfileをビルド
  app:
    build: .

  # パターン3: ビルドコンテキストとDockerfileを明示指定
  worker:
    build:
      context: ./worker
      dockerfile: Dockerfile.prod
      args:
        APP_ENV: production

ステップ1完了の確認: docker compose config を実行してYAML構文エラーがないことを確認できれば完了。


ステップ2: MySQL設定の実装

MySQLコンテナの設定は「初回起動時の初期化処理」「文字コード設定」「データ永続化」の3点が重要だ。

MySQLコンテナの基本定義

services:
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: myapp_development
      MYSQL_USER: myapp
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - ./docker/mysql/conf.d:/etc/mysql/conf.d:ro
      - ./docker/mysql/init:/docker-entrypoint-initdb.d:ro
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  db_data:

MySQL設定ファイル(my.cnf)

./docker/mysql/conf.d/my.cnf として配置する。

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
explicit_defaults_for_timestamp = 1
default_authentication_plugin = mysql_native_password

# パフォーマンス調整(開発環境向け)
innodb_buffer_pool_size = 256M
max_connections = 100

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

注意: MySQL 8.0以降、デフォルトの認証方式が caching_sha2_password に変更された。古いクライアントライブラリを使う場合は default_authentication_plugin = mysql_native_password を明示しないと接続エラーになる。ただし default_authentication_pluginMySQL 8.4.0 で完全削除されており、mysql:8.4 以降のイメージを使う場合は代わりに authentication_policy = mysql_native_password を設定すること。

初期データの投入(init スクリプト)

./docker/mysql/init/01_create_db.sql として配置する。/docker-entrypoint-initdb.d/ ディレクトリに配置したファイルはコンテナの初回起動時のみ実行される。

-- 開発・テスト用データベースを作成
CREATE DATABASE IF NOT EXISTS myapp_development
  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE DATABASE IF NOT EXISTS myapp_test
  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- アプリユーザーに権限付与
GRANT ALL PRIVILEGES ON myapp_development.* TO 'myapp'@'%';
GRANT ALL PRIVILEGES ON myapp_test.* TO 'myapp'@'%';
FLUSH PRIVILEGES;

環境変数は .env ファイルで管理する

パスワード等の機密情報はdocker-compose.yml内に直書きせず、.env ファイルで管理する。

# .env(.gitignoreに追加すること)
MYSQL_ROOT_PASSWORD=your_root_password_here
MYSQL_PASSWORD=your_app_password_here

Docker Composeは .env ファイルを自動で読み込むため、${MYSQL_ROOT_PASSWORD} のように参照できる。

MySQLコンテナの初期化フロー図

ステップ2完了の確認: docker compose up db を実行後、docker compose exec db mysql -u root -p でログインできれば完了。


ステップ3: Redisの設定

Redisはセッション管理・キャッシュ・ジョブキューで多用される。設定は比較的シンプルだが、データ永続化の要否を最初に決めておくことが重要だ。

Redis基本設定

services:
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  redis_data:

Redisの永続化オプション

オプション コマンド 特徴
永続化なし (デフォルト) 再起動でデータ消失。キャッシュ用途に最適
AOF(Append Only File) --appendonly yes 全書き込みをログ記録。信頼性高いが容量大
RDB(スナップショット) --save 900 1 定期スナップショット。バランス型

開発環境ではデータ消失してもよいなら volumes マウントを省略してもよい。本番相当の動作確認をしたい場合は AOF 永続化を推奨する。

アプリからの接続設定

# .env に追加
REDIS_PASSWORD=your_redis_password_here
REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0

ステップ3完了の確認: docker compose exec redis redis-cli -a ${REDIS_PASSWORD} pingPONG が返れば完了。


ステップ4: Nginxをリバースプロキシとして設定

Nginxをリバースプロキシとして追加し、Webアプリへのリクエストを転送する構成を作る。

Nginxサービス定義

services:
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./docker/nginx/ssl:/etc/nginx/ssl:ro
      - nginx_logs:/var/log/nginx
    depends_on:
      app:
        condition: service_healthy
    networks:
      - frontend
      - backend

volumes:
  nginx_logs:

Nginx設定ファイル(リバースプロキシ)

./docker/nginx/conf.d/default.conf として配置する。

upstream app_backend {
    server app:3000;
    # 複数インスタンスへの分散(スケールアウト時)
    # server app_2:3000;
}

server {
    listen 80;
    server_name localhost;

    # アクセスログ
    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;

    # ファイルアップロードサイズ上限
    client_max_body_size 10M;

    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;

        # WebSocket対応(必要な場合)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';

        # ヘッダー転送
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 静的ファイルはNginxで直接配信
    location /assets/ {
        alias /var/www/app/public/assets/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Nginx → App → MySQL/Redis のリクエストフロー図

ステップ4完了の確認: curl http://localhost でアプリのレスポンスが返れば完了。


ステップ5: 本番同等構成への仕上げ

ここまでの設定を統合し、healthcheckdepends_on で起動順序を制御した完全版を組み上げる。

完成版 docker-compose.yml

# docker-compose.yml(全サービス統合版)

services:
  # ==============================
  # Webアプリケーション
  # ==============================
  app:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    environment:
      RAILS_ENV: development
      DATABASE_URL: mysql2://myapp:${MYSQL_PASSWORD}@db/myapp_development
      REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0
    volumes:
      - .:/app
      - bundle_cache:/usr/local/bundle
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - backend

  # ==============================
  # MySQL データベース
  # ==============================
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: myapp_development
      MYSQL_USER: myapp
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - ./docker/mysql/conf.d:/etc/mysql/conf.d:ro
      - ./docker/mysql/init:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - backend

  # ==============================
  # Redis キャッシュ・セッション
  # ==============================
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

  # ==============================
  # Nginx リバースプロキシ
  # ==============================
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
    depends_on:
      app:
        condition: service_healthy
    networks:
      - frontend
      - backend

# ==============================
# ネットワーク定義
# ==============================
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

# ==============================
# ボリューム定義
# ==============================
volumes:
  db_data:
  redis_data:
  bundle_cache:

.env ファイルのテンプレート

# .env.example(リポジトリに含める)
MYSQL_ROOT_PASSWORD=change_me_root
MYSQL_PASSWORD=change_me_app
REDIS_PASSWORD=change_me_redis

実際の .env ファイルは .gitignore に追加して、リポジトリに含めないこと。


よく使うdocker composeコマンド

# サービスをバックグラウンドで起動
docker compose up -d

# 特定のサービスのみ起動
docker compose up -d db redis

# ログをリアルタイム確認
docker compose logs -f app

# 特定サービスのシェルに入る
docker compose exec app bash

# コンテナ内でコマンド実行(使い捨てコンテナ)
docker compose run --rm app bundle exec rails db:migrate

# サービスを停止・削除
docker compose down

# ボリュームごと削除(データ初期化)
docker compose down -v

# 設定の構文チェック
docker compose config

v1コマンドからv2への対応表

# 旧コマンド → 新コマンド
docker-compose up       → docker compose up
docker-compose down     → docker compose down
docker-compose exec     → docker compose exec
docker-compose run      → docker compose run
docker-compose logs     → docker compose logs
docker-compose ps       → docker compose ps
docker-compose build    → docker compose build
docker-compose pull     → docker compose pull

トラブルシューティング

症状 原因 解決策
depends_on を設定したのにDB接続エラー depends_on はプロセス起動を待つだけ。DB準備完了は待たない healthcheck + condition: service_healthy を使う
MySQL初期化スクリプトが実行されない db_data ボリュームにデータが残っている docker compose down -v でボリュームごと削除して再起動
version: "3.8" 警告が出る Docker Compose v2ではversionフィールドは廃止 version: 行を削除する
Macでボリュームマウントが遅い osxfsのパフォーマンス問題 delegated オプション追加、またはDocker Desktop VirtioFS設定を有効化
Redis認証エラー WRONGPASS パスワードが一致していない .envREDIS_PASSWORD とコマンドの --requirepass を確認
コンテナ間でホスト名解決できない 同一ネットワークに属していない 各サービスに同じ networks を設定する

まとめ

このガイドでは、Docker Compose v2対応の docker-compose.yml の書き方を以下の順で解説した。

  • v1とv2の違い: docker-compose コマンドのEoL、version: フィールドの廃止、depends_on のhealthcheck条件対応
  • MySQL設定: 文字コード設定、初期データ投入、named volumesによる永続化
  • Redis設定: パスワード認証、AOF永続化、healthcheckの設定
  • Nginx設定: リバースプロキシ設定、静的ファイル配信、ヘッダー転送
  • 統合構成: healthcheck + depends_on による起動順序制御、ネットワーク分離

Docker Compose v2への移行を済ませておくことで、今後のDockerエコシステムのアップデートにも対応しやすくなる。次のステップとして、Docker buildxでマルチプラットフォームイメージをAWS ECRにプッシュする完全ガイドも参照してほしい。


関連記事

Docker,Mac

Posted by GENDOSU