# 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.

- Skill: `diegosouzapw/docker-node` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diegosouzapw/docker-node`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diegosouzapw/docker-node/raw
- Safety review: pending (external: skill-scanner WARNING, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: diegosouzapw (https://skillmd.com/u/diegosouzapw)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/diegosouzapw/docker-node

---


# 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
# 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)

```yaml
# 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

```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

```bash
# 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

```bash
# 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

```bash
# 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)

```yaml
# docker-compose.yml
app:
  command: sh -c "npx prisma migrate dev && npm run dev"
```

### Production (Deploy migrations)

```dockerfile
# In Dockerfile CMD
CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"]
```

### CI/CD Pipeline

```bash
# Run migrations in separate step
docker run --rm \
  -e DATABASE_URL="$PROD_DATABASE_URL" \
  myapp:latest \
  npx prisma migrate deploy
```

---

## Health Checks

### Application Health

```dockerfile
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

```typescript
// 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

```yaml
# 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

```bash
# 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

```dockerfile
# 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

```dockerfile
# 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

```dockerfile
# 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 `.dockerignore` to exclude unnecessary files
- Add health checks
- Pin base image versions
- Use `docker-compose` for local development

### Avoid ❌

- Running as root in production
- Storing secrets in Dockerfile
- Using `latest` tag in production
- Including `node_modules` in image
- Skipping `.dockerignore`

---

## Troubleshooting

```yaml
"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

