Claude Code Docker Compose実践ガイド: Postgres/Redis/worker付きローカル開発環境
Claude CodeでDocker Composeを使い、Postgres/Redis/worker付きローカル開発環境を安全に作る実践ガイド。
Docker Compose は、アプリ、データベース、Redis、バックグラウンド worker をまとめて起動するための「ローカル開発の設計図」です。公式ドキュメントでも、Compose は複数コンテナのアプリケーションを定義して実行し、サービス、ネットワーク、ボリュームを1つの YAML で管理する道具として説明されています。まず全体像は Docker Compose 公式ドキュメント を確認しておくと安全です。
Claude Code と相性がよいのは、Compose が「ファイルで説明できる環境」だからです。口頭で「Postgres と Redis も立てて」と頼むより、compose.yaml、Dockerfile、.env.example、Makefile を置き、Claude Code にレビューさせるほうが再現性が出ます。
Masa が小さな Next.js + queue worker の検証環境で試したときも、差が出たのは魔法のプロンプトではありませんでした。DB の起動待ち、Redis の永続化、node_modules の扱い、CI で使う一回実行コマンドを先に決めたことが効きました。
この記事では、初心者でもそのまま試せる形で、app + PostgreSQL + Redis + worker の Docker Compose 構成を作ります。Compose はローカル開発、検証、テストには非常に強い一方、本番運用ではオーケストレーション、シークレット、監視、バックアップ、費用のレビューが別途必要です。ここを混ぜないことが、失敗を減らす最初の線引きです。
全体像
今回の構成は次の通りです。
flowchart LR
Dev["Developer"] --> App["app: web server"]
App --> Pg["postgres: database"]
App --> Redis["redis: cache and queue"]
Worker["worker: background jobs"] --> Pg
Worker --> Redis
App --> Volume["named volume: node_modules"]
Pg --> PgVol["named volume: postgres_data"]
Redis --> RedisVol["named volume: redis_data"]
app はブラウザからアクセスする Web アプリ、worker はメール送信やキュー処理を担当する常駐プロセスです。postgres と redis には healthcheck を付けます。healthcheck は「コンテナが起動した」ではなく「接続できる状態になった」を見るための確認です。
サービス定義の細かい仕様は Compose file reference と services reference が一次情報です。Claude Code に修正を頼むときも、この2つを前提にレビューさせると、古い docker-compose 時代の書き方に引っ張られにくくなります。
使いどころの比較
| 使いどころ | Compose が向いている理由 | 注意点 |
|---|---|---|
| ローカル開発 | app、DB、Redis、worker を1コマンドで再現できる | bind mount の速度差や OS 差を確認する |
| E2E/統合テスト | テスト用 DB と Redis を短時間で立てられる | CI ではポート衝突とキャッシュを明示する |
| 新メンバーのオンボーディング | .env.example と make setup で手順を固定できる | 秘密情報を .env.example に入れない |
| 本番運用 | 小規模な社内ツールでは候補になる場合もある | 監視、復旧、スケール、権限、コストのレビューが必須 |
特に本番境界は重要です。Compose は「開発者の手元に同じ足場を作る」には強いですが、クラウド本番では ECS、Kubernetes、Cloud Run、Fly.io、Render などの運用単位と比較します。Claude Code にも「Compose を本番に持ち込む前提でなく、移行先の制約を列挙して」と頼むのが安全です。
コピペ用 compose.yaml
次の例は Node.js/Next.js 系のプロジェクトを想定しています。npm run dev、npm run worker:dev、npm run db:migrate などは、あなたの package.json に合わせて置き換えてください。
# compose.yaml
services:
app:
build:
context: .
dockerfile: Dockerfile
target: dev
command: npm run dev -- --hostname 0.0.0.0
ports:
- "3000:3000"
env_file:
- .env
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?schema=public
REDIS_URL: redis://redis:6379
volumes:
- .:/workspace
- node_modules:/workspace/node_modules
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
interval: 10s
timeout: 5s
retries: 12
start_period: 20s
worker:
build:
context: .
dockerfile: Dockerfile
target: dev
command: npm run worker:dev
env_file:
- .env
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?schema=public
REDIS_URL: redis://redis:6379
volumes:
- .:/workspace
- node_modules:/workspace/node_modules
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postgres:
image: postgres:16-alpine
ports:
- "5432:5432"
env_file:
- .env
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./docker/postgres/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 5s
timeout: 5s
retries: 10
redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
volumes:
node_modules:
postgres_data:
redis_data:
ポイントは3つです。1つ目は postgres や redis というサービス名をホスト名として使うことです。コンテナ内から localhost:5432 に接続すると、自分自身を見に行くため失敗します。2つ目は node_modules を named volume に逃がすことです。ソースは bind mount しつつ、コンテナ内の依存関係をホスト側の空ディレクトリで上書きしないためです。3つ目は pg_isready -U $$POSTGRES_USER のようにドル記号を2つ書くことです。これは Compose の変数展開ではなく、コンテナ内の環境変数として読むための書き方です。
Dockerfile
ローカル開発では target: dev を使い、本番用ビルドは別ステージに分けます。初学者ほど1つの Dockerfile にまとめたほうが管理しやすいです。
# Dockerfile
# syntax=docker/dockerfile:1
FROM node:22-alpine AS base
WORKDIR /workspace
ENV NEXT_TELEMETRY_DISABLED=1
COPY package*.json ./
RUN npm ci
FROM base AS dev
EXPOSE 3000
CMD ["npm", "run", "dev", "--", "--hostname", "0.0.0.0"]
FROM base AS build
COPY . .
RUN npm run build
FROM node:22-alpine AS production
WORKDIR /workspace
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /workspace/.next ./.next
COPY --from=build /workspace/public ./public
USER node
EXPOSE 3000
CMD ["npm", "start"]
dev ステージではソースコードをコピーしません。Compose の volume でホストのファイルを見せるためです。逆に build ステージでは COPY . . して、CI や本番イメージで再現できるビルドにします。
.env.example
.env はコミットしません。代わりに .env.example をコミットし、全員が同じ変数名で起動できるようにします。
# .env.example
POSTGRES_USER=app
POSTGRES_PASSWORD=app_password
POSTGRES_DB=app_development
DATABASE_URL=postgresql://app:app_password@postgres:5432/app_development?schema=public
REDIS_URL=redis://redis:6379
NODE_ENV=development
PORT=3000
Docker Compose の env_file はコンテナへ渡す環境変数です。一方、Compose ファイル内の変数展開はプロジェクト直下の .env も参照します。この違いで混乱しやすいので、.env.example の値と compose.yaml の値をずらさないことが大切です。
Makefile と一回実行コマンド
よく使うコマンドは Makefile にまとめます。Windows で make を使わないチームなら、同じ中身を package.json の scripts に移しても構いません。
COMPOSE = docker compose --env-file .env -f compose.yaml
.PHONY: setup up up-d down restart logs ps app-shell db-shell redis-cli migrate seed test lint clean
setup:
cp .env.example .env
$(COMPOSE) build
up:
$(COMPOSE) up --build
up-d:
$(COMPOSE) up -d --build
down:
$(COMPOSE) down
restart:
$(COMPOSE) restart app worker
logs:
$(COMPOSE) logs -f app worker postgres redis
ps:
$(COMPOSE) ps
app-shell:
$(COMPOSE) exec app sh
db-shell:
$(COMPOSE) exec postgres psql -U app -d app_development
redis-cli:
$(COMPOSE) exec redis redis-cli
migrate:
$(COMPOSE) run --rm app npm run db:migrate
seed:
$(COMPOSE) run --rm app npm run db:seed
test:
$(COMPOSE) run --rm app npm test
lint:
$(COMPOSE) run --rm app npm run lint
clean:
$(COMPOSE) down --volumes --remove-orphans
docker compose run --rm app npm run lint のような一回実行コマンドは、CI とローカルの差を小さくします。exec は起動済みコンテナに入る操作、run --rm は新しい一時コンテナでコマンドだけを実行する操作、と覚えると迷いません。
Claude Code にレビューさせるプロンプト
Claude Code common workflows では、コードベースの理解、テスト、リファクタリングなどの日常作業を小さな依頼に分ける考え方が紹介されています。Compose でも同じで、「作って」より「観点を指定してレビューして」のほうが成果が安定します。
このリポジトリの Docker Compose 構成をレビューしてください。
対象:
- compose.yaml
- Dockerfile
- .env.example
- package.json
- .github/workflows があれば CI 設定
観点:
1. app + postgres + redis + worker がローカルで再現できるか
2. healthcheck と depends_on の使い方が妥当か
3. named volume と bind mount の使い分けが安全か
4. .env.example に秘密情報が混ざっていないか
5. migrate、seed、test、lint の一回実行コマンドが足りているか
6. CI で失敗しそうなポート、キャッシュ、権限、起動待ちがないか
7. 本番運用に持ち込む前に別途レビューすべき項目は何か
制約:
- 既存のフレームワークと package manager に合わせる
- 大きなリファクタリングは提案に止める
- 修正する場合は理由と確認コマンドも書く
このプロンプトを CLAUDE.md に近い形で残すと、チームのレビュー基準になります。CLAUDE.md の整え方は Claude Code Dev Containerガイド や CI/CDセットアップガイド と合わせて読むと実務に落とし込みやすいです。
実用ユースケース
1つ目は、新メンバーの初日セットアップです。git clone、.env.example のコピー、make up だけでアプリ、DB、Redis、worker が起動すれば、README の手順が短くなります。質問も「Postgres はどこで入れますか」ではなく「migration が失敗しています」に絞れます。
2つ目は、キュー処理のローカル検証です。メール送信、画像変換、課金 webhook 後の非同期処理は、Web アプリだけ起動しても確認できません。worker を Compose に入れておくと、Redis queue に積まれた job を手元で再現できます。
3つ目は、統合テストの安定化です。SQLite の代替テストでは通るのに Postgres で落ちる問題は珍しくありません。Compose で本番に近い DB を立て、docker compose run --rm app npm test を CI と同じ形に寄せると、型や SQL 方言の差に早く気づけます。
4つ目は、Claude Code によるインフラ差分レビューです。人間が YAML を眺めるだけでは、localhost 接続、.env 漏れ、volume の上書き、healthcheck 不足を見落としがちです。レビュー観点をテンプレート化して渡すと、毎回同じ粒度で確認できます。
よくある落とし穴
一番多い失敗は、アプリコンテナから localhost で DB に接続することです。Compose 内ではサービス名が DNS 名になります。postgres:5432、redis:6379 と書きます。
次に多いのは、depends_on を「完全な起動保証」と誤解することです。condition: service_healthy は強力ですが、アプリ側にもリトライ処理が必要です。DB migration の直後に worker が先に走るような構成では、起動順だけでは解決しません。
named volume の消し忘れも危険です。スキーマを変えたのに古い postgres_data が残ると、「自分の環境だけ壊れる」状態になります。検証をやり直すときは make clean で volume も削除します。ただし、手元のデータは消えるので実行前に確認してください。
.env.example に本物の API キーを入れるのも避けます。サンプル値は app_password や replace_me にし、本番秘密情報は Docker secrets、クラウドの secret manager、CI の secret store で扱います。
最後に、Compose ファイルをそのまま本番設計書にしないことです。開発環境ではポートを公開して便利にしますが、本番では公開範囲、TLS、DB バックアップ、Redis persistence、監視、脆弱性スキャン、イメージ署名、権限境界、費用を見ます。ここは Claude Code に任せきりにせず、人間のレビューを入れるべき領域です。
ClaudeCodeLab の教材と相談
この構成を自分のリポジトリに合わせて固めるなら、まず ClaudeCodeLabの教材・テンプレート で CLAUDE.md、レビュー用プロンプト、セットアップ手順の型を作ると早いです。チーム導入、Docker Desktop の設定差、CI/CD、権限境界、本番移行の判断まで一緒に設計したい場合は Claude Code研修・導入相談 を使ってください。
Compose の一次情報は Docker Compose、Compose file reference、services reference にあります。Claude Code 側の作業分解は Common workflows が参考になります。
この記事で紹介した内容を実際に試した結果、Masa の検証環境では make up から app、Postgres、Redis、worker までまとめて起動でき、DB が準備できる前にアプリが落ちる問題も healthcheck とリトライで減りました。一方で、古い named volume が残ると migration の検証結果が変わることも確認しました。結論として、Compose はローカル開発の再現性を上げる強力な足場ですが、本番判断はセキュリティ、監視、復旧、費用を別枠でレビューするのが安全です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
ObsidianメモをCLAUDE.mdに変えるClaude Code運用: 文脈を毎回説明しない仕組み
Obsidianの作業メモからCLAUDE.md用の運用ノートを作り、Claude Codeに安定した文脈を渡す方法。
Claude Code Revenue CTA Routing: 記事からPDF、Gumroad、相談へ送る設計
PVだけで終わらせず、読者の状態に合わせて無料PDF、Gumroad教材、導入相談へ分岐するCTA設計です。
Claude Codeチーム引き継ぎルール: レビュー、権限、収益導線まで渡す実務手順
Claude Codeの作業をチームで渡すための証拠、権限、ロールバック、無料PDF/Gumroad/相談導線の実務ルール。