When to Use
- •Writing or modifying Dockerfiles
- •Configuring docker-compose services
- •Setting up dev vs production containers
- •Multi-stage builds for Node.js/NestJS/Nuxt
- •Database containers (MySQL, Redis)
- •CI/CD pipeline container configs
Critical Patterns
Multi-Stage Build (NestJS)
dockerfile
# Stage 1: Dependencies FROM node:22-alpine AS deps WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN corepack enable && pnpm install --frozen-lockfile # Stage 2: Build FROM node:22-alpine AS build WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN corepack enable && pnpm build RUN pnpm prune --prod # Stage 3: Production FROM node:22-alpine AS production WORKDIR /app RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser COPY --from=build --chown=appuser:appgroup /app/dist ./dist COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules COPY --from=build --chown=appuser:appgroup /app/package.json ./ USER appuser EXPOSE 3000 CMD ["node", "dist/main.js"]
Multi-Stage Build (Nuxt)
dockerfile
FROM node:22-alpine AS deps WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN corepack enable && pnpm install --frozen-lockfile FROM node:22-alpine AS build WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN corepack enable && pnpm build FROM node:22-alpine AS production WORKDIR /app RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser COPY --from=build --chown=appuser:appgroup /app/.output ./.output USER appuser EXPOSE 3000 CMD ["node", ".output/server/index.mjs"]
Docker Compose (Full Stack)
yaml
# docker-compose.yml
services:
mysql:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
dockerfile: Dockerfile
target: production
restart: unless-stopped
ports:
- "3001:3000"
environment:
DATABASE_URL: mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@mysql:3306/${MYSQL_DATABASE}
JWT_SECRET: ${JWT_SECRET}
NODE_ENV: production
depends_on:
mysql:
condition: service_healthy
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
target: production
restart: unless-stopped
ports:
- "3000:3000"
environment:
NUXT_PUBLIC_API_BASE: http://backend:3000
depends_on:
- backend
volumes:
mysql_data:
Dev Compose Override
yaml
# docker-compose.dev.yml
services:
mysql:
ports:
- "3306:3306"
backend:
build:
target: deps
command: pnpm start:dev
volumes:
- ./backend:/app
- /app/node_modules
environment:
NODE_ENV: development
frontend:
build:
target: deps
command: pnpm dev
volumes:
- ./frontend:/app
- /app/node_modules
environment:
NODE_ENV: development
bash
# Dev docker compose -f docker-compose.yml -f docker-compose.dev.yml up # Prod docker compose up -d
Dockerfile Rules
| Rule | Why |
|---|---|
Use specific image tags (node:22-alpine) | Reproducible builds |
| Multi-stage builds | Smaller production images |
COPY package.json before COPY . | Cache dependencies layer |
--frozen-lockfile | Deterministic installs |
Non-root user (USER appuser) | Security |
.dockerignore everything unnecessary | Smaller context, faster builds |
EXPOSE only needed ports | Documentation + security |
healthcheck on services | Orchestration reliability |
.dockerignore
code
node_modules .git .env* *.md .nuxt .output dist coverage .vscode .idea
Decision Tree
code
Dev environment with hot reload? → Compose with volumes + target: deps Production single service? → Multi-stage Dockerfile Full stack (DB + API + Frontend)? → docker-compose.yml Need DB only for local dev? → Compose with just mysql service CI/CD pipeline? → Multi-stage + --target production
Commands
bash
# Build docker build -t myapp . # Build image docker build --target production -t myapp . # Build specific stage # Compose docker compose up -d # Start detached docker compose down # Stop and remove docker compose down -v # Stop + remove volumes docker compose logs -f backend # Follow logs docker compose exec backend sh # Shell into container docker compose build --no-cache # Rebuild without cache # Debugging docker ps # Running containers docker logs <container> -f # Follow logs docker exec -it <container> sh # Shell access docker stats # Resource usage # Cleanup docker system prune -a # Remove unused images/containers docker volume prune # Remove unused volumes
MySQL Container Tips
- •Always use
healthcheck— prevents apps from connecting before MySQL is ready - •Use named volumes (
mysql_data) — anonymous volumes get lost ondown - •Set
MYSQL_DATABASEenv var — auto-creates database on first run - •For Prisma migrations in Docker: run
npx prisma migrate deployas entrypoint script