Claude-skill-registry docker-node
Containerization for TypeScript/Node.js applications. Use when deploying Node.js backends, need consistent dev environments, or setting up CI/CD pipelines. Covers multi-stage builds, docker-compose for development, and production optimization. Choose this skill for containerizing tRPC/Express APIs with Prisma.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/docker-node" ~/.claude/skills/majiayu000-claude-skill-registry-docker-node && rm -rf "$T"
manifest:
skills/data/docker-node/SKILL.mdsource content
Docker (Node.js Containerization)
Overview
Docker enables consistent environments for Node.js applications across development, testing, and production. Multi-stage builds reduce image size, docker-compose simplifies local development.
Base Image:
node:20-alpine (recommended for small size)Use case: Deploy TypeScript APIs, ensure consistent environments
Key Benefit: "Works on my machine" → "Works everywhere"
When to Use This Skill
✅ Use Docker when:
- Deploying to cloud (AWS, GCP, Azure)
- Need consistent dev environment across team
- Running CI/CD pipelines
- Deploying with Kubernetes
- Need isolated PostgreSQL/Redis for development
❌ Skip Docker when:
- Simple scripts or CLI tools
- Serverless deployments (use platform's build)
- Early prototyping (adds complexity)
Multi-Stage Dockerfile (Production)
# Dockerfile # Stage 1: Dependencies FROM node:20-alpine AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production && npm cache clean --force # Stage 2: Build FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY tsconfig.json ./ COPY prisma ./prisma/ COPY src ./src/ RUN npx prisma generate RUN npm run build # Stage 3: Production FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production # Security: non-root user RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs # Copy production artifacts COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /app/prisma ./prisma COPY --from=builder --chown=nodejs:nodejs /app/node_modules/.prisma ./node_modules/.prisma USER nodejs EXPOSE 3000 # Run migrations then start CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"]
Docker Compose (Development)
# docker-compose.yml version: '3.8' services: app: build: context: . target: builder # Use builder stage for dev ports: - "3000:3000" environment: NODE_ENV: development DATABASE_URL: postgresql://postgres:postgres@postgres:5432/myapp volumes: - ./src:/app/src:delegated # Hot reload - ./prisma:/app/prisma:delegated depends_on: postgres: condition: service_healthy command: npm run dev postgres: image: postgres:15-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: myapp ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d myapp"] interval: 5s timeout: 5s retries: 10 redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data volumes: postgres_data: redis_data:
.dockerignore
# Dependencies node_modules # Build outputs dist .next # Git .git .gitignore # Environment .env .env.* !.env.example # IDE .vscode .idea # Docker docker-compose* Dockerfile* # Tests & docs coverage *.md **/*.test.ts **/*.spec.ts # OS .DS_Store Thumbs.db
Commands
Development
# Start all services docker-compose up # Start with rebuild docker-compose up --build # Start in background docker-compose up -d # View logs docker-compose logs -f app # Stop all docker-compose down # Stop and remove volumes (reset DB) docker-compose down -v
Production Build
# Build production image docker build -t myapp:latest . # Run container docker run -p 3000:3000 \ -e DATABASE_URL="postgresql://..." \ -e JWT_SECRET="..." \ myapp:latest # Run with env file docker run -p 3000:3000 --env-file .env.production myapp:latest
Debug
# Shell into running container docker-compose exec app sh # Shell into new container docker run -it myapp:latest sh # View container processes docker-compose top # Check image size docker images myapp
Database Migrations in Docker
Development (Auto-migrate)
# docker-compose.yml app: command: sh -c "npx prisma migrate dev && npm run dev"
Production (Deploy migrations)
# In Dockerfile CMD CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"]
CI/CD Pipeline
# Run migrations in separate step docker run --rm \ -e DATABASE_URL="$PROD_DATABASE_URL" \ myapp:latest \ npx prisma migrate deploy
Health Checks
Application Health
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
Health Endpoint
// src/routes/health.ts app.get('/health', async (req, res) => { try { await prisma.$queryRaw`SELECT 1`; res.json({ status: 'healthy', db: 'connected' }); } catch { res.status(503).json({ status: 'unhealthy', db: 'disconnected' }); } });
Environment Variables
Development
# docker-compose.yml services: app: environment: NODE_ENV: development DATABASE_URL: postgresql://postgres:postgres@postgres:5432/myapp JWT_SECRET: dev-secret-not-for-production
Production
# Pass at runtime docker run -e DATABASE_URL="..." -e JWT_SECRET="..." myapp # Or use env file docker run --env-file .env.production myapp
Optimization Tips
Reduce Image Size
# Use alpine base FROM node:20-alpine # Clean npm cache RUN npm ci && npm cache clean --force # Don't install devDependencies in production RUN npm ci --only=production
Layer Caching
# Copy package files first (changes less often) COPY package*.json ./ RUN npm ci # Then copy source (changes more often) COPY . . RUN npm run build
Security
# Run as non-root RUN adduser -S nodejs USER nodejs # Don't expose unnecessary ports EXPOSE 3000 # Use specific versions FROM node:20.10-alpine
Rules
Do ✅
- Use multi-stage builds for production
- Run containers as non-root user
- Use
to exclude unnecessary files.dockerignore - Add health checks
- Pin base image versions
- Use
for local developmentdocker-compose
Avoid ❌
- Running as root in production
- Storing secrets in Dockerfile
- Using
tag in productionlatest - Including
in imagenode_modules - Skipping
.dockerignore
Troubleshooting
"Prisma client not generated": → Add RUN npx prisma generate in builder stage → Copy node_modules/.prisma to runner stage "Permission denied": → Check file ownership with --chown → Ensure USER matches file owner "Container exits immediately": → Check logs: docker-compose logs app → Verify CMD is blocking (not backgrounded) "Can't connect to database": → Use service name as host (postgres, not localhost) → Check depends_on with healthcheck → Verify DATABASE_URL uses container network "Image too large": → Use alpine base image → Add .dockerignore → Use multi-stage builds → Clean npm cache
File Structure
project/ ├── Dockerfile ├── docker-compose.yml ├── .dockerignore ├── .env.example ├── package.json ├── tsconfig.json ├── prisma/ │ └── schema.prisma └── src/ └── index.ts
References
- https://docs.docker.com — Official documentation
- https://docs.docker.com/compose/ — Docker Compose
- https://hub.docker.com/_/node — Node.js images