Claude Code Docker Compose 实战指南:本地 App、Postgres、Redis 与 Worker
用 Claude Code 和 Docker Compose 搭建 App、Postgres、Redis、worker 的本地开发环境。
Docker Compose 可以把 Web app、PostgreSQL、Redis 和后台 worker 放进同一份本地开发蓝图里。Docker 的 Compose 文档 把它描述为定义并运行多容器应用的工具,服务、网络、卷都可以通过一个 YAML 文件管理。
这正是它适合 Claude Code 的原因。与其只说“帮我做一个 Docker 环境”,不如把 compose.yaml、Dockerfile、.env.example、Makefile 放进仓库,让 Claude Code 基于真实文件审查。这样更容易复现,也更容易让团队维护。
Masa 在一个小型 Next.js + queue worker 项目里验证过这个模式。真正节省时间的不是花哨提示词,而是提前决定好数据库等待、Redis 持久化、node_modules volume、migration/test 命令和 CI 的边界。
本文会给出可复制的 app + PostgreSQL + Redis + worker 示例。Compose 非常适合本地开发、集成测试和新人 onboarding;但生产环境需要单独评估编排、安全、secret、监控、备份和成本。
架构图
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 负责邮件、webhook、图片处理或队列任务。postgres 和 redis 都加上 healthcheck,避免只是容器启动了、服务却还不能连接。
语法细节建议参考 Compose file reference 和 services reference。让 Claude Code 审查时也把这两份官方文档作为依据,可以减少旧版 docker-compose 示例带来的误导。
使用场景对比
| 场景 | Compose 的优势 | 注意点 |
|---|---|---|
| 本地开发 | 一个命令启动 app、DB、Redis、worker | 不同 OS 的文件挂载性能不同 |
| 集成测试 | 可以按需创建测试 DB 和 Redis | CI 里要明确端口和缓存策略 |
| 新人加入 | .env.example 和 make setup 固定步骤 | 示例文件不能包含真实 secret |
| 生产环境 | 小型内部工具有时可作为候选 | 必须评估编排、安全、恢复、监控和成本 |
生产边界不能忽略。Compose 很适合在开发者机器上创建同样的工作台,但生产环境应与 ECS、Kubernetes、Cloud Run、Fly.io、Render 或现有平台比较。可以让 Claude Code 列出迁移约束,而不是默认把本地文件当部署方案。
可复制的 compose.yaml
示例假设你使用 Node.js 或 Next.js。请把 npm run dev、npm run worker:dev、migration 命令替换为你项目里的脚本。
# 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:
这里有三个关键点。第一,容器之间用服务名访问,所以 app 应连接 postgres:5432 和 redis:6379,不是 localhost。第二,node_modules 使用 named volume,避免源码 bind mount 覆盖容器内依赖。第三,Postgres healthcheck 里的双美元符号是为了让变量在容器 shell 中展开,而不是被 Compose 提前替换。
Dockerfile
一个 Dockerfile 内分出 dev、build、production stage,通常比维护多份 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 stage 不复制源码,因为 Compose 会通过 volume 挂载。build stage 则复制完整仓库,保证 CI 和生产镜像不依赖本机文件状态。
.env.example
提交 .env.example,不要提交 .env。示例文件只展示配置形状,不放真实凭据。
# .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
env_file 是传给容器的变量;Compose 文件内的变量替换也会读取项目根目录的 .env。这两个概念相近但不完全一样,最好保持命名和值一致。
Makefile 与一次性命令
把常用命令固定下来,可以减少口头说明。
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
exec 用于已启动的容器,run --rm 用于一次性任务,例如 lint、test、migration、seed。这个区分能让本地命令更接近 CI。
Claude Code 审查提示词
Claude Code common workflows 强调把日常开发拆成理解、修改、测试、审查等小任务。Compose 审查也应该这样做。
请审查这个仓库的 Docker Compose 设置。
文件:
- compose.yaml
- Dockerfile
- .env.example
- package.json
- 如果存在,也检查 CI workflow 文件
检查:
1. app + postgres + redis + worker 是否能在本地运行
2. healthcheck 和 depends_on 是否使用合理
3. named volume 和 bind mount 的区分是否安全
4. .env.example 是否包含真实 secret
5. migrate、seed、test、lint 的一次性命令是否齐全
6. CI 中可能出现的端口、缓存、权限、启动等待问题
7. 如果要用于生产,必须额外审查哪些项目
约束:
- 遵循当前框架和 package manager
- 大型重构只提出建议
- 如果修改文件,请说明原因和验证命令
可以把这个提示词放在 CLAUDE.md 附近,作为团队审查标准。相关内容还可以阅读 Dev Container 指南 和 CI/CD 设置指南。
实用场景
第一个场景是新人第一天设置环境。复制 .env.example 后执行 make up,app、DB、Redis、worker 都能启动,README 会短很多。
第二个场景是本地验证队列任务。邮件、图片处理、计费 webhook、通知 fan-out 都不应该假装由 Web server 完成。把 worker 放进 Compose 后,可以更真实地复现 job。
第三个场景是集成测试。很多问题在 SQLite 替代测试中通过,却在 Postgres 上失败。使用 Compose 管理 Postgres 和 Redis,可以更早发现 SQL 方言、migration 和队列行为差异。
第四个场景是让 Claude Code 做基础设施差分审查。人类很容易漏看 localhost、旧 volume、缺少 healthcheck 或示例 secret。模板化提示词可以让审查粒度稳定。
常见坑
最常见的错误是在 app 容器里连接 localhost。Compose 网络里服务名就是 DNS 名,应使用 postgres 和 redis。
第二个错误是把 depends_on 当作完整的就绪保证。condition: service_healthy 很有用,但 app 仍然应该有数据库连接重试,worker 也不能早于 migration 执行关键任务。
旧 named volume 也会制造混乱。schema 变了但 postgres_data 还在时,本地状态可能和仓库不一致。需要重测时使用 make clean,但要知道它会删除本地数据。
不要把真实 API key 放进 .env.example。示例值用 app_password 或 replace_me,生产 secret 放到 Docker secrets、云 secret manager 或 CI secret store。
最后,不要默认把本地 Compose 文件当生产方案。生产环境需要 TLS、网络暴露、DB 备份、Redis 持久化、监控、漏洞扫描、镜像来源、权限边界和成本评估。
ClaudeCodeLab 教材与咨询
如果要把这套方案落到自己的仓库,可以先用 ClaudeCodeLab 的产品与模板 统一 CLAUDE.md、审查提示词和 setup runbook。如果难点是团队导入、Docker Desktop 差异、CI/CD、权限边界,或判断 Compose 到哪里为止,请使用 Claude Code 培训与咨询。
本文使用的官方参考包括 Docker Compose、Compose file reference、services reference 和 Claude Code common workflows。
实际试用本文配置后,Masa 的测试项目可以通过 make up 同时启动 app、Postgres、Redis 和 worker,healthcheck 也减少了应用早于数据库启动的竞态问题。剩下最容易踩的坑是旧 named volume:它可能让 migration 看起来坏了,或者错误地看起来已经修好。结论是,Compose 是本地开发的强力足场,但生产环境仍然需要单独做安全、运维、恢复和成本审查。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。