Best for
- Containerizing applications for any language or framework
- Creating or optimizing Dockerfiles and Docker Compose configurations
- Setting up development environments with Docker
einverne/dotfiles/claude/skills/docker/SKILL.md
Guide for using Docker - a containerization platform for building, running, and deploying applications in isolated containers. Use when containerizing applications, creating Dockerfiles, working with Docker Compose, managing images/containers, configuring networking and storage, optimizing builds, deploying to production, or implementing CI/CD pipelines with Docker.
Decision brief
This skill provides comprehensive guidance for working with Docker, covering containerization concepts, practical workflows, and best practices across all major technology stacks.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/einverne/dotfiles --skill "claude/skills/docker"Inspect the Agent Skill "docker" from https://github.com/einverne/dotfiles/blob/7d18cf4fefdeec05853c7420cc0499dde1e88b27/claude/skills/docker/SKILL.md at commit 7d18cf4fefdeec05853c7420cc0499dde1e88b27. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Essential Instructions” section in the pinned source before continuing.
Separate build environment from runtime environment to reduce image size and improve security:
FROM node:20-alpine AS build WORKDIR /app COPY package.json ./ RUN npm install COPY . . RUN npm run build
FROM node:20-alpine AS production WORKDIR /app COPY --from=build /app/dist ./dist COPY --from=build /app/nodemodules ./nodemodules USER node EXPOSE 3000 CMD ["node", "dist/server.js"] dockerfile
docker build -t myapp:dev --target build .
Permission review
The documentation asks the agent to run terminal commands or scripts.
docker-compose.ymlThe documentation asks the agent to run terminal commands or scripts.
docker build -t myapp:1.0 .The documentation includes network, browsing, or remote request actions.
CMD curl -f http://localhost:3000/health || exit 1The documentation includes network, browsing, or remote request actions.
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 85/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 119 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
This skill provides comprehensive guidance for working with Docker, covering containerization concepts, practical workflows, and best practices across all major technology stacks.
Use this skill when:
REGISTRY/NAMESPACE/REPOSITORY:TAG (e.g., docker.io/library/nginx:latest)FROM <image>:<tag> # Base image (use specific versions, not 'latest')
WORKDIR /app # Working directory for subsequent commands
COPY package*.json ./ # Copy dependency files first (for caching)
RUN npm install --production # Execute build commands
COPY . . # Copy application code
ENV NODE_ENV=production # Environment variables
EXPOSE 3000 # Document exposed ports
USER node # Run as non-root user (security)
CMD ["node", "server.js"] # Default command when container starts
Separate build environment from runtime environment to reduce image size and improve security:
# Stage 1: Build
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
Benefits: Compiled assets without build tools in final image, smaller size, improved security
Order matters! Docker reuses layers if instruction unchanged:
# Use specific versions
FROM node:20.11.0-alpine3.19
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Set ownership
COPY --chown=nodejs:nodejs . .
# Switch to non-root
USER nodejs
# Read-only root filesystem (when possible)
# Add --read-only flag when running container
Exclude unnecessary files from build context:
node_modules
.git
.env
.env.local
*.log
.DS_Store
README.md
docker-compose.yml
.dockerignore
Dockerfile
dist
coverage
.vscode
# Build with tag
docker build -t myapp:1.0 .
# Build targeting specific stage
docker build -t myapp:dev --target build .
# Build with build arguments
docker build --build-arg NODE_ENV=production -t myapp:1.0 .
# Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:1.0 .
# View image layers and size
docker image history myapp:1.0
# List all images
docker image ls
# Basic run
docker run myapp:1.0
# Run in background (detached)
docker run -d --name myapp myapp:1.0
# Port mapping (host:container)
docker run -p 8080:3000 myapp:1.0
# Environment variables
docker run -e NODE_ENV=production -e API_KEY=secret myapp:1.0
# Volume mount (named volume)
docker run -v mydata:/app/data myapp:1.0
# Bind mount (development)
docker run -v $(pwd)/src:/app/src myapp:1.0
# Custom network
docker run --network my-network myapp:1.0
# Resource limits
docker run --memory 512m --cpus 0.5 myapp:1.0
# Interactive terminal
docker run -it myapp:1.0 /bin/sh
# Override entrypoint/command
docker run --entrypoint /bin/sh myapp:1.0
docker run myapp:1.0 custom-command --arg
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# View logs
docker logs myapp
docker logs -f myapp # Follow logs
docker logs --tail 100 myapp # Last 100 lines
# Execute command in running container
docker exec myapp ls /app
docker exec -it myapp /bin/sh # Interactive shell
# Stop container (graceful)
docker stop myapp
# Kill container (immediate)
docker kill myapp
# Remove container
docker rm myapp
docker rm -f myapp # Force remove running container
# View container details
docker inspect myapp
# Monitor resource usage
docker stats myapp
# View container processes
docker top myapp
# Copy files to/from container
docker cp myapp:/app/logs ./logs
docker cp ./config.json myapp:/app/config.json
# Tag image
docker tag myapp:1.0 registry.example.com/myapp:1.0
# Push to registry
docker login registry.example.com
docker push registry.example.com/myapp:1.0
# Pull from registry
docker pull nginx:alpine
# Remove image
docker image rm myapp:1.0
# Remove unused images
docker image prune
# Remove all unused resources (images, containers, volumes, networks)
docker system prune -a
# View disk usage
docker system df
# Create named volume
docker volume create mydata
# List volumes
docker volume ls
# Inspect volume
docker volume inspect mydata
# Remove volume
docker volume rm mydata
# Remove unused volumes
docker volume prune
# Create network
docker network create my-network
docker network create --driver bridge my-bridge
# List networks
docker network ls
# Inspect network
docker network inspect my-network
# Connect container to network
docker network connect my-network myapp
# Disconnect container from network
docker network disconnect my-network myapp
# Remove network
docker network rm my-network
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/app
depends_on:
- db
- redis
volumes:
- ./src:/app/src # Development: live code reload
networks:
- app-network
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
networks:
- app-network
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
# Start all services
docker compose up
# Start in background
docker compose up -d
# Build images before starting
docker compose up --build
# Scale specific service
docker compose up -d --scale web=3
# Stop all services
docker compose down
# Stop and remove volumes
docker compose down --volumes
# View logs
docker compose logs
docker compose logs -f web # Follow specific service
# Execute command in service
docker compose exec web sh
docker compose exec db psql -U user -d app
# List running services
docker compose ps
# Restart service
docker compose restart web
# Pull latest images
docker compose pull
# Validate compose file
docker compose config
compose.yml (base configuration):
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/app
compose.override.yml (development overrides, loaded automatically):
services:
web:
volumes:
- ./src:/app/src # Live code reload
environment:
- NODE_ENV=development
- DEBUG=true
command: npm run dev
compose.prod.yml (production overrides):
services:
web:
image: registry.example.com/myapp:1.0
restart: always
environment:
- NODE_ENV=production
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M
Usage:
# Development (uses compose.yml + compose.override.yml automatically)
docker compose up
# Production (explicit override)
docker compose -f compose.yml -f compose.prod.yml up -d
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
FROM python:3.11-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.11-slim AS production
WORKDIR /app
COPY --from=build /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]
FROM golang:1.21-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
FROM scratch
COPY --from=build /app/main /main
EXPOSE 8080
CMD ["/main"]
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN ./mvnw clean package -DskipTests
FROM eclipse-temurin:21-jre-alpine AS production
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
RUN addgroup -g 1001 -S spring && \
adduser -S spring -u 1001
USER spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
In Dockerfile:
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
In Compose:
services:
web:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
start-period: 40s
retries: 3
services:
web:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
services:
web:
restart: unless-stopped # Restart unless manually stopped
# Other options: "no", "always", "on-failure"
services:
web:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Using .env file:
# .env
DATABASE_URL=postgresql://user:pass@db:5432/app
API_KEY=secret
services:
web:
env_file:
- .env
Using Docker secrets (Swarm):
services:
web:
secrets:
- db_password
secrets:
db_password:
external: true
latest)name: Docker Build and Push
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: user/app:latest,user/app:${{ github.sha }}
cache-from: type=registry,ref=user/app:buildcache
cache-to: type=registry,ref=user/app:buildcache,mode=max
- name: Run vulnerability scan
uses: docker/scout-action@v1
with:
command: cves
image: user/app:${{ github.sha }}
# Using Docker Scout
docker scout cves myapp:1.0
docker scout recommendations myapp:1.0
# Quick view
docker scout quickview myapp:1.0
# Read-only root filesystem
docker run --read-only -v /tmp --tmpfs /run myapp:1.0
# Drop all capabilities, add only needed ones
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp:1.0
# No new privileges
docker run --security-opt=no-new-privileges myapp:1.0
# Use security profiles
docker run --security-opt apparmor=docker-default myapp:1.0
# Limit resources
docker run --memory=512m --cpus=0.5 --pids-limit=100 myapp:1.0
latest# Create custom bridge network
docker network create my-bridge
# Run containers on custom bridge
docker run -d --name web --network my-bridge nginx
docker run -d --name db --network my-bridge postgres
# Containers can communicate via container name
# web can connect to: http://db:5432
services:
web:
depends_on:
- db
environment:
# Use service name as hostname
- DATABASE_URL=postgresql://user:pass@db:5432/app
db:
image: postgres:15-alpine
# Publish single port
docker run -p 8080:80 nginx
# Publish range of ports
docker run -p 8080-8090:8080-8090 myapp
# Publish to specific interface
docker run -p 127.0.0.1:8080:80 nginx
# Publish all exposed ports to random ports
docker run -P nginx
# Create and use named volume
docker volume create app-data
docker run -v app-data:/app/data myapp
# Automatic creation
docker run -v app-data:/app/data myapp # Creates if doesn't exist
# Live code reload during development
docker run -v $(pwd)/src:/app/src myapp
# Read-only bind mount
docker run -v $(pwd)/config:/app/config:ro myapp
# Store temporary data in memory
docker run --tmpfs /tmp myapp
# Backup volume
docker run --rm -v app-data:/data -v $(pwd):/backup alpine \
tar czf /backup/backup.tar.gz /data
# Restore volume
docker run --rm -v app-data:/data -v $(pwd):/backup alpine \
tar xzf /backup/backup.tar.gz -C /data
# View logs
docker logs -f myapp
docker logs --tail 100 myapp
# Interactive shell
docker exec -it myapp /bin/sh
# Inspect container
docker inspect myapp
# View processes
docker top myapp
# Monitor resource usage
docker stats myapp
# View changes to filesystem
docker diff myapp
# Build with verbose output
docker build --progress=plain -t myapp .
# Build specific stage for testing
docker build --target build -t myapp:build .
# Run failed build stage
docker run -it myapp:build /bin/sh
# Check build context
docker build --no-cache -t myapp .
Container exits immediately:
# Check logs
docker logs myapp
# Run with interactive shell
docker run -it myapp /bin/sh
# Override entrypoint
docker run -it --entrypoint /bin/sh myapp
Cannot connect to container:
# Check port mapping
docker ps
docker port myapp
# Check network
docker network inspect bridge
docker inspect myapp | grep IPAddress
# Check if service is listening
docker exec myapp netstat -tulpn
Out of disk space:
# Check disk usage
docker system df
# Clean up
docker system prune -a
docker volume prune
docker image prune -a
Build cache issues:
# Force rebuild without cache
docker build --no-cache -t myapp .
# Clear build cache
docker builder prune
# Setup buildx
docker buildx create --use
# Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 \
-t myapp:1.0 --push .
# Use BuildKit (enabled by default in recent versions)
DOCKER_BUILDKIT=1 docker build -t myapp .
# Use build cache from registry
docker build --cache-from myapp:latest -t myapp:1.0 .
# Export cache to registry
docker build --cache-to type=registry,ref=myapp:buildcache \
--cache-from type=registry,ref=myapp:buildcache \
-t myapp:1.0 .
# List contexts
docker context ls
# Create remote context
docker context create remote --docker "host=ssh://user@remote"
# Use context
docker context use remote
docker ps # Now runs on remote host
# Switch back to default
docker context use default
| Task | Command |
|---|---|
| Build image | docker build -t myapp:1.0 . |
| Run container | docker run -d -p 8080:3000 myapp:1.0 |
| View logs | docker logs -f myapp |
| Shell into container | docker exec -it myapp /bin/sh |
| Stop container | docker stop myapp |
| Remove container | docker rm myapp |
| Start Compose | docker compose up -d |
| Stop Compose | docker compose down |
| View Compose logs | docker compose logs -f |
| Clean up all | docker system prune -a |
| Language/Framework | Recommended Base |
|---|---|
| Node.js | node:20-alpine |
| Python | python:3.11-slim |
| Java | eclipse-temurin:21-jre-alpine |
| Go | scratch (for compiled binary) |
| .NET | mcr.microsoft.com/dotnet/aspnet:8.0-alpine |
| PHP | php:8.2-fpm-alpine |
| Ruby | ruby:3.2-alpine |
| Static sites | nginx:alpine |
Docker containerization provides:
Follow multi-stage builds, run as non-root, use specific versions, implement health checks, scan for vulnerabilities, and configure resource limits for production-ready containerized applications.
Alternatives