name: devops-docker description: Docker containerization best practices. Use when creating Dockerfiles or docker-compose configurations.
DevOps Docker Standards
This skill provides Docker containerization best practices.
When to Use
- Use this skill when creating or modifying Dockerfiles
- Use this skill when setting up docker-compose configurations
- Use this skill when optimizing container images
Instructions
1. Dockerfile Best Practices
Multi-Stage Builds (Mandatory)
ALWAYS use multi-stage builds to minimize final image size.
# Stage 1: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Runner
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 appuser
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER appuser
EXPOSE 3000
CMD ["node", "dist/main.js"]
Base Images
- Use
alpineorslimversions (e.g.,node:20-alpine). - NEVER use
:latesttag. - Pin specific versions for reproducibility.
# Good
FROM node:20.10.0-alpine
# Bad
FROM node:latest
User Permissions
Implement a non-root user for security.
RUN addgroup --system --gid 1001 appgroup
RUN adduser --system --uid 1001 appuser
USER appuser
2. Layer Optimization
Order instructions from least to most frequently changed.
# Good order (least changed first)
FROM node:20-alpine
WORKDIR /app
# Dependencies change less often
COPY package*.json ./
RUN npm ci --only=production
# Source changes more often
COPY . .
CMD ["node", "index.js"]
3. .dockerignore (Mandatory)
Create .dockerignore to exclude unnecessary files.
# .dockerignore
.git
.gitignore
node_modules
npm-debug.log
Dockerfile
docker-compose*.yml
.env
.env.*
*.md
.vscode
.idea
coverage
dist
.next
4. Docker Compose
# docker-compose.yml
version: '3.8'
services:
api:
container_name: myapp-api
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
depends_on:
db:
condition: service_healthy
networks:
- app-network
restart: unless-stopped
db:
container_name: myapp-db
image: postgres:15-alpine
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres-data:
5. Key Docker Compose Rules
- Naming: Explicitly name containers (
container_name: my-app-api). - Networks: Define a custom bridge network; do not use the default.
- Healthchecks: Define for dependent services.
- Volumes: Use named volumes for data persistence.
- Restart Policy: Use
unless-stoppedoralways.
6. Environment Variables
Never hardcode secrets. Use .env file passing.
# docker-compose.yml
services:
api:
env_file:
- .env
environment:
# Override or add specific vars
- NODE_ENV=production
Create .env.example for documentation:
# .env.example
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
JWT_SECRET=your-secret-here
7. Production Checklist
- Multi-stage build used
- Non-root user configured
- Specific image versions (no
:latest) - .dockerignore present
- Health checks defined
- Resource limits set (optional but recommended)
- Secrets not hardcoded