| name | docker |
| description | Docker patterns, Dockerfile, multi-stage builds, Compose, dev/prod. Trigger: When writing Dockerfiles, docker-compose, or container configuration.
|
| license | Apache-2.0 |
| metadata | {"author":"gentleman-programming","version":"1.0"} |
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)
# 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)
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)
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
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
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
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
node_modules
.git
.env*
*.md
.nuxt
.output
dist
coverage
.vscode
.idea
Decision Tree
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
docker build -t myapp .
docker build --target production -t myapp .
docker compose up -d
docker compose down
docker compose down -v
docker compose logs -f backend
docker compose exec backend sh
docker compose build --no-cache
docker ps
docker logs <container> -f
docker exec -it <container> sh
docker stats
docker system prune -a
docker volume prune
MySQL Container Tips
- Always use
healthcheck — prevents apps from connecting before MySQL is ready
- Use named volumes (
mysql_data) — anonymous volumes get lost on down
- Set
MYSQL_DATABASE env var — auto-creates database on first run
- For Prisma migrations in Docker: run
npx prisma migrate deploy as entrypoint script