# Docker & Containerization — Cursor Rules
# Comprehensive rules for Docker, Docker Compose, and container best practices

## Project Context
You are working on a project that uses Docker for containerization. Containers are used
for local development, CI/CD pipelines, and production deployment. The codebase includes
Dockerfiles for application services and docker-compose files for orchestrating
multi-container environments.

## Tech Stack
- Docker Engine 24+
- Docker Compose v2
- Multi-stage builds
- Container registries (Docker Hub, GitHub Container Registry, ECR)
- Orchestration: Docker Compose (dev), Kubernetes or ECS (production)

## Dockerfile Best Practices

### Multi-Stage Build Pattern
```dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app

# Install dependencies first (better cache utilization)
COPY package.json package-lock.json ./
RUN npm ci --production=false

# Copy source and build
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app

# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copy only production dependencies and built artifacts
COPY --from=builder /app/package.json /app/package-lock.json ./
RUN npm ci --production && npm cache clean --force

COPY --from=builder /app/dist ./dist

# Switch to non-root user
USER appuser

EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/server.js"]
```

### Python Multi-Stage
```dockerfile
FROM python:3.12-slim AS builder
WORKDIR /app

RUN pip install --no-cache-dir uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-editable

COPY . .

FROM python:3.12-slim AS production
WORKDIR /app

RUN useradd --create-home --no-log-init appuser
COPY --from=builder /app/.venv .venv
COPY --from=builder /app/src ./src

USER appuser
ENV PATH="/app/.venv/bin:$PATH"

EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

## Dockerfile Rules

### Layer Ordering (Most to Least Frequently Changed)
1. Base image
2. System dependencies
3. Create user
4. Copy dependency manifests (package.json, requirements.txt)
5. Install dependencies
6. Copy source code
7. Build step
8. Runtime configuration (ENV, EXPOSE, HEALTHCHECK, CMD)

### Image Size Optimization
- Use `-alpine` or `-slim` base images
- Use multi-stage builds to exclude build tools from production
- Combine `RUN` commands to reduce layers: `RUN apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/*`
- Use `.dockerignore` to exclude unnecessary files
- Remove caches after installing packages: `npm cache clean --force`, `pip --no-cache-dir`
- Don't install dev dependencies in production: `npm ci --production`

### .dockerignore
```
node_modules
.git
.env
.env.*
*.md
.vscode
.idea
coverage
.nyc_output
dist
__pycache__
*.pyc
.pytest_cache
.mypy_cache
docker-compose*.yml
Dockerfile*
```

### Security
- Never run as root — create and switch to a non-root user
- Don't store secrets in the image (use environment variables or secrets manager)
- Pin base image versions: `node:20.11-alpine` not `node:latest`
- Scan images for vulnerabilities: `docker scout cves`
- Use `COPY` instead of `ADD` (ADD has extra behaviors: URL fetch, tar extraction)
- Don't install unnecessary packages (no `vim`, `curl` in production unless needed for healthcheck)
- Set read-only filesystem where possible: `--read-only`

## Docker Compose

### Development Setup
```yaml
# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: builder          # Use build stage for development
    ports:
      - "3000:3000"
    volumes:
      - .:/app                 # Mount source for hot reload
      - /app/node_modules      # Prevent overwriting container's node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://postgres:postgres@db:5432/appdb
    depends_on:
      db:
        condition: service_healthy
    command: npm run dev

  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: appdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  pgdata:
```

### Compose Best Practices
- Use `depends_on` with `condition: service_healthy` for startup ordering
- Define healthchecks on all services
- Use named volumes for persistent data
- Use `.env` file for environment variables
- Override with `docker-compose.override.yml` for local customizations
- Use `profiles` for optional services (like monitoring tools)

## Container Runtime

### Environment Variables
- Use `ENV` in Dockerfile for build-time defaults
- Use `environment` in docker-compose for runtime configuration
- Use Docker secrets or external secret managers for sensitive values
- Never hardcode secrets in Dockerfiles or compose files

### Networking
- Use Docker Compose service names as hostnames (`db`, `redis`)
- Only expose ports that need external access
- Use internal networks for service-to-service communication
- Define custom networks for service isolation

### Health Checks
- Every service must have a health check
- Health checks should be lightweight and fast
- Test the actual service (HTTP endpoint, database ping), not just process existence
- Set reasonable intervals (10-30s) and retries (3-5)

## Development Workflow
```bash
# Build and start all services
docker compose up --build

# Run in background
docker compose up -d

# View logs
docker compose logs -f app

# Execute command in running container
docker compose exec app npm run test

# Rebuild a single service
docker compose up --build app

# Clean up everything
docker compose down -v --rmi local
```

## Production Considerations
- Use specific image tags, never `latest`
- Set resource limits (memory, CPU)
- Use restart policies: `restart: unless-stopped`
- Log to stdout/stderr (Docker captures and forwards)
- Use read-only root filesystem with tmpfs for writable needs
- Implement graceful shutdown (handle SIGTERM)
- Use init process: `--init` flag or `tini`

## Common Pitfalls
- Using `latest` tag in production (unpredictable builds)
- Running as root (security risk)
- Not using `.dockerignore` (large image, slow builds, secrets leaked)
- Installing dev dependencies in production images
- Not using multi-stage builds (bloated production images)
- Copying `node_modules` from host into container (platform mismatch)
- Not setting healthchecks (orchestrator can't detect unhealthy containers)
- Hardcoding configuration instead of using environment variables
- Not handling SIGTERM (container takes 10s to stop instead of shutting down gracefully)
- Storing state in the container filesystem (data lost on restart)
