「新しいメンバーが開発環境を構築するのに丸一日かかった」「自分の環境では動くのに、他のメンバーの環境では動かない」「Node.js、PostgreSQL、Redisのバージョンがバラバラ」――チーム開発で頻発するこうした問題を解決するのがDocker Composeです。
本記事では、Docker Composeを使って開発環境を一発で構築する方法を、実際のプロジェクト構成を例に解説します。基本的な使い方から、マルチステージビルド、環境変数の管理、開発と本番の使い分けまで、実践的なテクニックを網羅します。
Docker Composeとは?なぜ必要なのか
Docker Composeは、複数のDockerコンテナをYAMLファイルで定義し、一括で管理するツールです。Dockerの最新版には標準で含まれています。
Docker Composeが解決する問題
モダンなWebアプリケーションは、アプリケーションサーバー、データベース、キャッシュ、メッセージキューなど、複数のサービスで構成されます。これらを個別にdocker runコマンドで起動・管理するのは現実的ではありません。
# Docker Composeなしの場合:個別にコンテナを起動
docker network create myapp-network
docker run -d --name postgres \
--network myapp-network \
-e POSTGRES_PASSWORD=password \
-v pgdata:/var/lib/postgresql/data \
postgres:16
docker run -d --name redis \
--network myapp-network \
redis:7-alpine
docker run -d --name app \
--network myapp-network \
-p 3000:3000 \
-e DATABASE_URL=postgresql://postgres:password@postgres:5432/mydb \
-e REDIS_URL=redis://redis:6379 \
myapp:latest
# 停止するときも個別に...
docker stop app redis postgres
docker rm app redis postgres
# Docker Composeなら1コマンド
docker compose up -d # 起動
docker compose down # 停止・削除
Docker Composeのバージョン
Docker Compose V2はdocker compose(ハイフンなし)コマンドで実行します。旧バージョンのdocker-compose(ハイフンあり)は非推奨です。
# バージョン確認
docker compose version
# Docker Compose version v2.x.x
基本的なcompose.ymlの書き方
Docker Composeの設定ファイルはcompose.yml(またはdocker-compose.yml)です。
最小構成の例
# compose.yml
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
depends_on:
- db
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
基本コマンド
# すべてのサービスをバックグラウンドで起動
docker compose up -d
# ログの確認
docker compose logs -f # 全サービス
docker compose logs -f app # 特定サービス
# サービスの状態確認
docker compose ps
# コンテナ内でコマンド実行
docker compose exec app sh
docker compose exec db psql -U postgres -d mydb
# イメージの再ビルド
docker compose up -d --build
# すべて停止して削除(ボリュームは保持)
docker compose down
# ボリュームも含めて完全に削除
docker compose down -v
実践的な開発環境構成
Next.js + PostgreSQL + Redis の開発環境を構築する実例を紹介します。
プロジェクト構成
my-project/
├── compose.yml
├── compose.prod.yml
├── Dockerfile
├── Dockerfile.prod
├── .env
├── .env.example
├── src/
├── prisma/
│ └── schema.prisma
└── ...
開発用Dockerfile
# Dockerfile(開発用)
FROM node:20-alpine
WORKDIR /app
# パッケージファイルのコピーと依存関係のインストール
COPY package.json package-lock.json ./
RUN npm ci
# ソースコードはvolumesでマウントするのでCOPYしない
EXPOSE 3000
CMD ["npm", "run", "dev"]
開発用compose.yml
# compose.yml
services:
# ===================
# アプリケーション
# ===================
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
env_file:
- .env
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/${DB_NAME}
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- .:/app
- /app/node_modules # node_modulesはコンテナ内のものを使用
- /app/.next # .nextもコンテナ内のものを使用
restart: unless-stopped
# ===================
# PostgreSQL
# ===================
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./docker/db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
# ===================
# Redis
# ===================
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
# ===================
# Mailpit(開発用メールサーバー)
# ===================
mailpit:
image: axllent/mailpit
ports:
- "8025:8025" # Web UI
- "1025:1025" # SMTP
restart: unless-stopped
# ===================
# pgAdmin(DB管理GUI)
# ===================
pgadmin:
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin
ports:
- "5050:80"
depends_on:
- db
restart: unless-stopped
profiles:
- tools # docker compose --profile tools up で起動
volumes:
pgdata:
redis-data:
環境変数ファイル
# .env.example(リポジトリにコミット)
DB_PASSWORD=password
DB_NAME=mydb
SMTP_HOST=mailpit
SMTP_PORT=1025
# .env(.gitignoreに追加、リポジトリにコミットしない)
DB_PASSWORD=password
DB_NAME=mydb
SMTP_HOST=mailpit
SMTP_PORT=1025
データベース初期化スクリプト
-- docker/db/init.sql
-- この SQL は PostgreSQL コンテナの初回起動時に自動実行される
-- 拡張機能の有効化
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- テスト用データベースの作成
CREATE DATABASE mydb_test;
本番環境向けの構成
開発環境と本番環境では求められる設定が異なります。
マルチステージビルドの本番用Dockerfile
# Dockerfile.prod
# ===== Stage 1: 依存関係のインストール =====
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# ===== Stage 2: ビルド =====
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# Prismaクライアントの生成
RUN npx prisma generate
# Next.jsのビルド
RUN npm run build
# ===== Stage 3: 本番イメージ =====
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# セキュリティ:root以外のユーザーで実行
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 必要なファイルだけコピー
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/prisma ./prisma
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]
本番用のcompose.prod.yml
# compose.prod.yml
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
ports:
- "3000:3000"
env_file:
- .env.production
environment:
- NODE_ENV=production
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: always
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
db:
image: postgres:16-alpine
env_file:
- .env.production
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: always
deploy:
resources:
limits:
memory: 1G
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
command: >
redis-server
--appendonly yes
--maxmemory 128mb
--maxmemory-policy allkeys-lru
--requirepass ${REDIS_PASSWORD}
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: always
volumes:
pgdata:
redis-data:
# 本番用のデプロイコマンド
docker compose -f compose.prod.yml up -d --build
よくある構成パターン
フロントエンド + バックエンドの分離
# compose.yml
services:
frontend:
build:
context: ./frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://localhost:8000
volumes:
- ./frontend:/app
- /app/node_modules
backend:
build:
context: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
depends_on:
- db
volumes:
- ./backend:/app
- /app/node_modules
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Nginxリバースプロキシ構成
# compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- app
restart: always
app:
build: .
expose:
- "3000" # portsではなくexposeで内部ネットワークのみに公開
environment:
- NODE_ENV=production
restart: always
# docker/nginx/nginx.conf
events {
worker_connections 1024;
}
http {
upstream app {
server app:3000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app;
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;
}
}
}
トラブルシューティングとベストプラクティス
よくあるトラブルと対処法
コンテナが起動しない
# ログを確認
docker compose logs app
# コンテナの状態を確認
docker compose ps -a
# ビルドキャッシュをクリアして再ビルド
docker compose build --no-cache
docker compose up -d
ポートが既に使用されている
# 使用中のポートを確認(macOS/Linux)
lsof -i :3000
# 別のポートにマッピング
ports:
- "3001:3000" # ホストの3001番ポートをコンテナの3000番に
ボリュームのパーミッション問題
# Linuxでのファイルオーナー問題
# Dockerfileでユーザーを指定
RUN chown -R node:node /app
USER node
ベストプラクティス
1. healthcheckを必ず設定する
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s # 初回チェックまでの待機時間
2. depends_onにconditionを使う
app:
depends_on:
db:
condition: service_healthy # DBが起動完了してから起動
redis:
condition: service_healthy
3. .dockerignoreを設定する
# .dockerignore
node_modules
.next
.git
.env
.env.local
*.md
docker-compose*.yml
4. プロファイルで開発ツールを分離する
services:
pgadmin:
image: dpage/pgadmin4
profiles:
- tools
redis-commander:
image: rediscommander/redis-commander
profiles:
- tools
# 通常の起動(開発ツールは起動しない)
docker compose up -d
# 開発ツールも含めて起動
docker compose --profile tools up -d
5. ボリュームの使い分け
volumes:
# Named Volume:永続化が必要なデータ(DBデータなど)
pgdata:
# Bind Mount:開発時のソースコード同期
# volumes:
# - .:/app
# Anonymous Volume:コンテナ内のパッケージを保護
# volumes:
# - /app/node_modules
まとめ
Docker Composeを使った開発環境構築のポイントを整理します。
- compose.ymlで複数サービスを宣言的に管理し、
docker compose up -dで一発起動 - healthcheck + depends_onでサービスの起動順序を制御
- 環境変数は
.envファイルで管理し、.env.exampleをリポジトリにコミット - マルチステージビルドで本番イメージを最小化
- profilesで開発ツール(pgAdmin等)を分離
- 開発と本番は別のcomposeファイルで管理
Docker Composeを導入すれば、「READMEに書いてある手順通りにやったのに動かない」という問題がなくなります。新しいメンバーが参加しても、git cloneしてdocker compose up -dするだけで開発を始められる環境を目指しましょう。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像