Claude Code and Docker: Compose, Volumes, Networks, and CI Builds
Build safer Dockerfiles, Compose stacks, and CI image checks with Claude Code.
Decide What Claude Code Should Own
If you ask Claude Code to “Dockerize this app” without constraints, it may produce a Dockerfile that runs once but is hard to review. Docker is a way to package an application runtime: an image is the template, a container is the running instance, a volume keeps data outside the container, and a network lets services talk to each other by name.
For production-minded work, do not treat the Dockerfile as the whole task. Review the Dockerfile, docker-compose.yml, .dockerignore, environment variables, health checks, and CI build together. Keep the primary references open: Dockerfile reference, Compose file reference, Docker build best practices, Docker Build with GitHub Actions, and the Claude Code overview.
For adjacent work, pair this article with Claude Code CI/CD setup, Claude Code approval and sandbox guide, and Claude Code security best practices.
Four Practical Use Cases
Docker integration is useful when it removes ambiguity from daily work. Name the job before asking Claude Code to edit files.
| Use case | What Docker standardizes | What to ask Claude Code |
|---|---|---|
| New developer onboarding | Node.js, PostgreSQL, Redis, ports, startup commands | Make docker compose up boot the stack |
| Production-like debugging | Env vars, service names, health checks, startup order | List local vs production differences |
| Pull request verification | Dockerfile, lockfile, build cache, CI logs | Add a workflow that builds the image |
| Training and handoff | Commands, failure modes, recovery steps | Write CLAUDE.md and README rules |
This matters for monetized software. If checkout, lead forms, admin screens, or product pages only work on one laptop, revenue depends on an unreviewed local state. Solo builders can start with ClaudeCodeLab products. Teams that want repository-specific rules, permissions, CI checks, and rollout support can use Claude Code training and consultation.
Prompt Claude Code With Guardrails
Give Claude Code the target, requested files, forbidden behavior, and verification commands in one prompt. This prevents a plausible but unsafe Docker setup.
Dockerize this Node.js + TypeScript API.
Context:
- pnpm is the package manager.
- The app listens on port 3000.
- GET /health returns 200.
- Local development needs PostgreSQL 16 and Redis 7.
Create:
- Production multi-stage Dockerfile
- docker-compose.yml for local development
- .dockerignore
- Docker-related package.json scripts
- GitHub Actions workflow that only verifies docker build
Constraints:
- Do not run the production container as root.
- Do not COPY .env files or secrets into the image.
- Explain the volume and network choices for the README.
- Include verification commands and how to inspect failures.
A multi-stage build separates the build environment from the runtime environment. TypeScript compilation tools stay out of the final image, while the runtime stage receives only production dependencies and compiled output.
Copy-Paste Dockerfile
This example assumes a Node.js API that builds src/index.ts into dist/index.js. The dev stage supports Compose-based local development. The runner stage is the production image.
# syntax=docker/dockerfile:1
FROM node:22-bookworm-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
WORKDIR /app
RUN corepack enable
FROM base AS dev
ENV NODE_ENV=development
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
CMD ["pnpm", "dev"]
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
FROM deps AS build
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM base AS prod-deps
ENV NODE_ENV=production
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod && pnpm store prune
FROM base AS runner
ENV NODE_ENV=production
RUN groupadd --system --gid 1001 nodejs \
&& useradd --system --uid 1001 --gid nodejs appuser
COPY --from=prod-deps --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=appuser:nodejs /app/dist ./dist
COPY --chown=appuser:nodejs package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD node -e "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "dist/index.js"]
Ask Claude Code to review the file for three things: whether secrets can enter the image, whether the COPY order preserves cache efficiency, and whether development dependencies are excluded from the runtime image.
Compose: Volumes and Networks Without Mystery
Compose starts multiple containers together. In this stack, the API, PostgreSQL, and Redis share a network, so the API connects to postgres and redis by service name. Volumes keep state outside the disposable container. pgdata preserves database files, and api_node_modules prevents host node_modules from overriding container dependencies.
services:
api:
build:
context: .
target: dev
command: pnpm dev
ports:
- "3000:3000"
environment:
NODE_ENV: development
DATABASE_URL: postgres://app:app@postgres:5432/app
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- .:/app
- api_node_modules:/app/node_modules
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 10s
timeout: 3s
retries: 5
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
volumes:
pgdata:
api_node_modules:
depends_on controls startup order. It does not automatically prove that a database is ready unless you combine it with health checks. That is why the example waits for service_healthy.
Keep Secrets Out of the Build Context
Start with .dockerignore. It reduces build context size and prevents accidental secret copies.
.git
node_modules
dist
coverage
.env
.env.*
!.env.example
npm-debug.log*
pnpm-debug.log*
Dockerfile*
docker-compose*.yml
Then add scripts that the team can run without memorizing command sequences.
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.json",
"start": "node dist/index.js",
"docker:dev": "docker compose up --build",
"docker:check": "bash scripts/docker-check.sh"
},
"dependencies": {
"@fastify/redis": "latest",
"fastify": "latest",
"pg": "latest"
},
"devDependencies": {
"tsx": "latest",
"typescript": "latest"
}
}
Add a Repeatable Verification Script
Manual verification is easy to skip. Put the check in scripts/docker-check.sh and ask Claude Code to keep it updated when services change.
#!/usr/bin/env bash
set -euo pipefail
docker compose build api
docker compose up -d postgres redis api
cleanup() {
docker compose down
}
trap cleanup EXIT
for attempt in {1..30}; do
if curl -fsS http://127.0.0.1:3000/health >/dev/null; then
echo "healthcheck ok"
exit 0
fi
echo "waiting for api... ${attempt}/30"
sleep 2
done
docker compose logs api
exit 1
This script builds the API image, starts the stack, waits for the health endpoint, and prints logs when the service never becomes healthy.
CI: Build First, Push Later
The first pull request should verify the image build without publishing anything to a registry. Add push, signing, vulnerability scanning, and SBOM generation after the base image is stable.
name: docker-build
on:
pull_request:
push:
branches:
- main
jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v7
with:
context: .
target: runner
push: false
tags: claude-code-docker-sample:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
Ask Claude Code to explain whether this workflow pushes anything, whether the permissions are larger than needed, and how the job behaves when the cache is empty.
Common Failure Modes
The most expensive mistake is copying .env into the image. Once a secret enters a registry, cleanup is slow. Always check .dockerignore, CI secrets, and build logs together.
Another common failure is mounting host node_modules into a Linux container. Native packages can differ across macOS, Windows, and Linux. Keeping api_node_modules as a named volume avoids that cross-platform mismatch.
The third failure is a startup race: the API starts before the database is ready. Combine database health checks, service_healthy, and application-level retries.
The fourth failure is running the production container as root. It often works in demos, but it expands the blast radius of an application vulnerability. Ask Claude Code to point to the exact Dockerfile lines that prove the runtime user is non-root.
Safe Development Workflow
The safest order is: inspect the repository, list runtime dependencies, write .dockerignore, create local Compose, create the production Dockerfile, add the verification script, then add CI. After each step, read the diff and ask Claude Code for four short receipts: changed files, reason, verification command, and remaining risk.
This works well with session handoff templates because another developer can continue without guessing. Docker integration should become a repeatable team habit, not a one-time generated file.
What Happened in Practice
In practice, smaller Claude Code tasks produced better Docker work. Starting with .dockerignore, then Compose, then health checks, then CI reduced review churn. The named node_modules volume and database health gates prevented the two beginner issues that show up most often: “it works only on my machine” and “the API started but cannot connect to Postgres.”
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Obsidian to CLAUDE.md Workflow: Stop Re-explaining Context
Turn Obsidian working notes into concise CLAUDE.md operating notes that make Claude Code sessions easier to resume.
Claude Code Revenue CTA Routing: Send Articles to PDF, Gumroad, and Consultation
A Claude Code workflow for routing article readers to the free PDF, Gumroad products, or consultation by intent.
Claude Code Team Handoff Rules: Review Evidence, Permissions, Rollback, and Revenue Paths
A practical Claude Code handoff format for team review, proof, permission rules, rollback, free PDF, Gumroad, and consultation paths.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.