Generate Docker setup for development and production
Generates complete Docker setup with multi-stage builds, docker-compose for dev/prod, hot reload, and SSR-aware networking.
/plugin marketplace add lenneTech/claude-code/plugin install lt-dev@lenne-techdocker/Create a complete Docker setup for this project with the following requirements:
Create two compose files:
docker-compose.yml (Base/Production):
docker-compose.override.yml (Local Development):
npm ci in container for consistent installsAdd these only in the override:
Database (choose based on project or PostgreSQL as default):
Mailhog:
Database UI:
For SSR frameworks (Nuxt, Next.js, SvelteKit, Analog, etc.):
Understand the Problem:
| Context | Runs Where | Network | Does http://api:3000 work? |
|---|---|---|---|
| Server (SSR) | Docker Container | Docker Network | Yes |
| Browser (Client) | User's Machine | Host Network | No |
Implement Solution - One of the following options:
Option A: Different URLs for Server/Client
environment:
# Server-side (SSR) - Docker internal network
NUXT_API_BASE_SERVER: http://api:3000
# OR for Next.js (without PUBLIC prefix = server-only)
API_URL_INTERNAL: http://api:3000
# Client-side (Browser) - MUST be localhost!
NUXT_PUBLIC_API_BASE: http://localhost:3001
# OR for Next.js
NEXT_PUBLIC_API_URL: http://localhost:3001
Option B: Reverse Proxy (Traefik) - Recommended
Unify everything under one URL so server and client can use the same URL:
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
web:
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=PathPrefix(`/`)"
- "traefik.http.routers.web.priority=1"
- "traefik.http.services.web.loadbalancer.server.port=3000"
api:
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=PathPrefix(`/api`)"
- "traefik.http.routers.api.priority=2"
- "traefik.http.services.api.loadbalancer.server.port=3000"
Option C: Built-in Proxy (Nuxt Nitro / Next.js Rewrites)
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
devProxy: {
'/api': {
target: 'http://api:3000',
changeOrigin: true
}
}
},
routeRules: {
'/api/**': { proxy: 'http://api:3000' }
}
})
NEVER use a single API_URL pointing to a Docker service!
.env.example with all required variables# .env.example Template
# === Database ===
DATABASE_URL=postgresql://postgres:postgres@db:5432/app
DB_HOST=db
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=app
# === API Service ===
API_PORT=3000
# === Web/Frontend Service ===
WEB_PORT=3000
# === API URLs (SSR Setup) ===
# Server-Side (Docker internal)
API_URL_INTERNAL=http://api:3000
NUXT_API_BASE_SERVER=http://api:3000
# Client-Side (Browser) - MUST be localhost!
NUXT_PUBLIC_API_BASE=http://localhost:3001
NEXT_PUBLIC_API_URL=http://localhost:3001
# === Mail (Mailhog) ===
SMTP_HOST=mailhog
SMTP_PORT=1025
MAIL_FROM=noreply@localhost
# === Development ===
NODE_ENV=development
.dockerignore:
node_modules
.git
.gitignore
*.md
.env
.env.*
!.env.example
dist
.output
.nuxt
.next
coverage
.nyc_output
*.log
Makefile or package.json Scripts:
.PHONY: dev build logs shell db-reset clean
dev:
docker compose up -d --build
build:
docker compose -f docker-compose.yml build
logs:
docker compose logs -f
shell:
docker compose exec app sh
db-reset:
docker compose down -v
docker compose up -d db
sleep 5
docker compose exec app npm run db:migrate
clean:
docker compose down -v --remove-orphans
docker system prune -f
Create a DOCKER.md with:
# Create .env from .env.example if not present
if [ ! -f .env ]; then
cp .env.example .env
echo "Created .env from .env.example"
fi
# Stop and remove old containers and volumes
docker compose down -v --remove-orphans
# Build and start fresh
docker compose build --no-cache
docker compose up -d
# Wait for containers to start
echo "Waiting for containers to start..."
sleep 15
# Check container status
docker compose ps
# Check logs of all services
docker compose logs --tail=200
Check for the following patterns:
Success:
Errors:
echo "=== Health Checks ==="
# App/Frontend
if curl -sf http://localhost:3000 > /dev/null 2>&1; then
echo " Frontend reachable (localhost:3000)"
else
echo " Frontend NOT reachable"
fi
# API (if separate service)
if curl -sf http://localhost:3001/health > /dev/null 2>&1 || curl -sf http://localhost:3001 > /dev/null 2>&1; then
echo " API reachable (localhost:3001)"
else
echo " API not on localhost:3001 (maybe different port or integrated)"
fi
# Database UI (Adminer)
if curl -sf http://localhost:8080 > /dev/null 2>&1; then
echo " DB UI reachable (localhost:8080)"
else
echo " DB UI NOT reachable"
fi
# Mailhog
if curl -sf http://localhost:8025 > /dev/null 2>&1; then
echo " Mailhog reachable (localhost:8025)"
else
echo " Mailhog NOT reachable"
fi
# Database Connection Check
if docker compose exec -T db pg_isready -U postgres > /dev/null 2>&1; then
echo " PostgreSQL ready"
else
echo " PostgreSQL NOT ready"
fi
echo "=== SSR Checks ==="
# Server-to-API connection (inside container)
if docker compose exec -T web curl -sf http://api:3000/health > /dev/null 2>&1; then
echo " SSR -> API connection OK (Docker internal)"
else
echo " Check SSR -> API connection"
fi
# Check that no Docker-internal URLs end up in client bundle
if docker compose exec -T web sh -c 'find .output .next dist -name "*.js" 2>/dev/null | head -20 | xargs grep -l "api:3000" 2>/dev/null'; then
echo " WARNING: 'api:3000' found in client bundle! SSR URLs misconfigured."
else
echo " No Docker-internal URLs in client bundle"
fi
When errors are found, analyze and fix them:
WHILE errors exist AND attempts < 5:
1. Identify the specific error from logs/health checks
2. Determine the cause (see table below)
3. Apply the appropriate fix
4. Restart affected services: docker compose up -d --build <service>
5. Wait for startup (sleep 10-15)
6. Run health checks again
7. attempts++
| Symptom | Cause | Fix |
|---|---|---|
| Container won't start | Missing env vars | Check/complete .env file |
| Container won't start | Syntax error in code | Check logs, fix code |
| Container crash loop | Wrong Node version | Adjust Node version in Dockerfile |
| "Module not found" | node_modules problem | Delete volume, rebuild |
| "EADDRINUSE" / Port in use | Port already used | Change port in compose |
| "ECONNREFUSED" to DB | DB not ready yet | Add depends_on + healthcheck |
| "ECONNREFUSED api:3000" in browser | SSR URL problem | Change client URL to localhost |
| API works in SSR, not in browser | Wrong PUBLIC env var | Use NUXT_PUBLIC_* / NEXT_PUBLIC_* |
| Permission denied | User/permissions wrong | Adjust Dockerfile user |
| CORS errors in browser | API CORS not configured | Add CORS headers or use proxy |
node_modules Problem:
# Identify and delete volume
docker compose down
docker volume ls | grep node_modules
docker volume rm <volume_name>
docker compose up -d --build
DB Connection Timing:
# docker-compose.yml - add healthcheck
services:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
app:
depends_on:
db:
condition: service_healthy
SSR URL Fix:
# Wrong:
environment:
API_URL: http://api:3000
# Correct:
environment:
NUXT_API_BASE_SERVER: http://api:3000 # Server
NUXT_PUBLIC_API_BASE: http://localhost:3001 # Client
When all checks pass, output the following summary:
╔══════════════════════════════════════════════════════════════╗
║ Docker Setup Successful! ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ Container Status: ║
║ ├── app/web Running ║
║ ├── api Running (if separate service) ║
║ ├── db Running ║
║ ├── adminer Running ║
║ └── mailhog Running ║
║ ║
║ Available Services: ║
║ ├── Frontend: http://localhost:3000 ║
║ ├── API: http://localhost:3001 (if separate) ║
║ ├── DB UI: http://localhost:8080 ║
║ │ └── Login: postgres / postgres / app ║
║ └── Mailhog: http://localhost:8025 ║
║ ║
║ Commands: ║
║ ├── Logs: docker compose logs -f ║
║ ├── Stop: docker compose down ║
║ ├── Rebuild: docker compose up -d --build ║
║ └── Shell: docker compose exec app sh ║
║ ║
╚══════════════════════════════════════════════════════════════╝
List all created/modified files:
Dockerfiledocker-compose.ymldocker-compose.override.yml.dockerignore.env.example.env (copied from example)DOCKER.mdMakefile (optional)Document what was fixed:
Fixed Issues:
1. [Problem]: API URL in client bundle was Docker-internal
[Fix]: Set NUXT_PUBLIC_API_BASE to localhost:3001
2. [Problem]: Container started before DB was ready
[Fix]: Added healthcheck and depends_on condition
docker compose up -d --builddocker compose down -v and restart fresh