| name | docker |
| description | Use when building, running, or managing containers with Docker or Podman. Covers Dockerfiles, multi-stage builds, Docker Compose, image optimization, security, registries, and container networking for development and CI/CD.
USE FOR: Docker, Podman, Dockerfile, docker-compose, multi-stage builds, container images, container networking, Docker volumes, container registries, image optimization, Docker security, rootless containers, BuildKit
DO NOT USE FOR: Kubernetes orchestration (use iac or platform-specific skills), container runtime security scanning (use security/security-testing), cloud container services (use cloud-specific skills)
|
| license | MIT |
| metadata | {"displayName":"Docker & Containers","author":"Tyler-R-Kendrick"} |
| compatibility | claude, copilot, cursor |
| references | [{"title":"Docker Documentation","url":"https://docs.docker.com"},{"title":"Podman Documentation","url":"https://podman.io/docs"},{"title":"Dockerfile Reference","url":"https://docs.docker.com/reference/dockerfile/"}] |
Docker & Containers
Overview
Containers package an application with all its dependencies into a portable, reproducible unit. Docker is the dominant container runtime; Podman is a daemonless, rootless alternative with Docker CLI compatibility. Containers are the foundation of modern deployment workflows, enabling consistent environments from development through production.
Docker vs Podman
| Feature | Docker | Podman |
|---|
| Daemon | dockerd required | Daemonless |
| Rootless | Supported | Default |
| CLI Compatibility | docker | podman / alias docker=podman |
| Compose | docker compose v2 | podman-compose |
| OCI Compliant | Yes | Yes |
| Systemd Integration | Limited | Native with quadlets |
| Build Tool | BuildKit | Buildah (integrated) |
Dockerfile
Common Instructions
| Instruction | Purpose | Example |
|---|
FROM | Set base image | FROM node:22-alpine |
RUN | Execute command during build | RUN npm ci |
COPY | Copy files from host to image | COPY package.json ./ |
ADD | Copy files (supports URLs, tar extraction) | ADD app.tar.gz /app |
WORKDIR | Set working directory | WORKDIR /app |
ENV | Set environment variable | ENV NODE_ENV=production |
ARG | Build-time variable | ARG VERSION=latest |
EXPOSE | Document container port | EXPOSE 3000 |
CMD | Default command (overridable) | CMD ["node", "server.js"] |
ENTRYPOINT | Main executable (not easily overridden) | ENTRYPOINT ["python", "app.py"] |
USER | Set runtime user | USER node |
HEALTHCHECK | Container health check | HEALTHCHECK CMD curl -f http://localhost:3000/ |
LABEL | Add metadata | LABEL maintainer="team@example.com" |
Example: Node.js Application
FROM node:22-alpine
WORKDIR /app
# Copy dependency files first (better layer caching)
COPY package.json package-lock.json ./
RUN npm ci --only=production
# Copy application code
COPY src/ ./src/
# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "src/server.js"]
Example: Python Application
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Non-root user
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Multi-Stage Builds
Multi-stage builds produce smaller production images by separating the build environment from the runtime environment. Build tools, source code, and intermediate artifacts are left behind in the build stage.
# Stage 1: Build
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:22-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]
Go Application (Scratch Base)
FROM golang:1.22-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server .
FROM scratch
COPY --from=build /app/server /server
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
Image Optimization
| Technique | Impact |
|---|
| Use alpine/slim base images | 5-10x smaller than full images |
| Multi-stage builds | Remove build dependencies from production image |
| Order layers for cache | COPY package.json before source code |
| Combine RUN commands | Fewer layers, smaller image |
| Use .dockerignore | Exclude unnecessary files from build context |
Use specific tags, not :latest | Reproducible builds, predictable behavior |
| Clean up in the same RUN layer | RUN apt install -y pkg && rm -rf /var/lib/apt/lists/* |
| Pin base image by SHA digest | Immutable, tamper-proof base images |
Layer Caching Example
# GOOD: Dependencies change less often than source code
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# BAD: Any source change invalidates the npm ci cache
COPY . .
RUN npm ci
Docker Compose
Docker Compose defines multi-container applications in a single YAML file.
Example: Web App + Database + Redis
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- ./src:/app/src
networks:
- app-network
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout:
Common Compose Commands
docker compose up -d
docker compose down
docker compose down -v
docker compose logs
docker compose logs -f web
docker compose build
docker compose up -d --build
docker compose up -d --scale web=3
docker compose exec web sh
docker compose run --rm web npm test
docker compose ps
Common Commands
Images
docker build -t myapp:1.0 .
docker build -t myapp:1.0 -f Dockerfile.prod .
DOCKER_BUILDKIT=1 docker build -t myapp:1.0 .
docker pull nginx:alpine
docker tag myapp:1.0 registry.example.com/myapp:1.0
docker push registry.example.com/myapp:1.0
docker images
docker rmi myapp:1.0
docker inspect myapp:1.0
docker history myapp:1.0
Containers
docker run -d --name web -p 3000:3000 myapp:1.0
docker run -it --rm ubuntu:22.04 bash
docker run -d --name web -e NODE_ENV=production -p 3000:3000 myapp:1.0
docker exec -it web sh
docker exec web cat /etc/os-release
docker stop web
docker start web
docker restart web
docker rm web
docker rm -f web
docker ps
docker ps -a
docker logs web
docker logs -f web
docker logs --tail 100 web
docker cp web:/app/data.json ./data.json
docker cp ./config.yml web:/app/config.yml
Volumes
docker volume create mydata
docker volume ls
docker volume rm mydata
docker run -d -v mydata:/app/data myapp:1.0
docker run -d -v $(pwd)/src:/app/src myapp:1.0
docker run -d -v $(pwd)/config:/app/config:ro myapp:1.0
Named Volumes vs Bind Mounts:
- Named volumes — managed by Docker, persist across container restarts, portable, use for databases and persistent data
- Bind mounts — map a host directory into the container, use for development (hot reloading source code)
Networks
docker network create mynet
docker network ls
docker run -d --name web --network mynet myapp:1.0
docker network connect mynet web
docker network inspect mynet
Network Drivers:
- bridge — default, isolated network for containers on a single host
- host — share host network stack (no isolation, best performance)
- none — no networking
System Maintenance
docker system prune
docker system prune --volumes
docker system df
docker builder prune
docker image prune -a
Container Security
Run as Non-Root User
# Create a non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Read-Only File System
docker run --read-only --tmpfs /tmp myapp:1.0
Drop Capabilities
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp:1.0
No Privileged Mode
docker run --cap-add SYS_PTRACE myapp:1.0
Scan Images
trivy image myapp:1.0
docker scout cves myapp:1.0
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:1.0
Use Distroless or Scratch Base Images
# Distroless — minimal base with no shell, no package manager
FROM gcr.io/distroless/static-debian12
COPY --from=build /app/server /server
CMD ["/server"]
# Scratch — empty base image (for statically compiled binaries)
FROM scratch
COPY --from=build /app/server /server
CMD ["/server"]
Secrets Handling
echo "my-secret-value" | docker secret create db_password -
docker build --secret id=mysecret,src=./secret.txt .
# Use secret during build (not persisted in image)
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
Never use ENV for secrets — environment variables are visible in docker inspect and image history.
Registries
| Registry | Provider | Key Feature |
|---|
| Docker Hub | Docker | Default public registry |
| GitHub Container Registry (ghcr.io) | GitHub | Integrated with GitHub Actions |
| AWS ECR | Amazon | Integrated with AWS IAM |
| Azure ACR | Microsoft | Integrated with Azure AD |
| GCP Artifact Registry | Google | Multi-format (Docker, npm, Maven) |
| Harbor | CNCF / self-hosted | Enterprise self-hosted registry |
Pushing to a Registry
docker login
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
docker tag myapp:1.0 ghcr.io/username/myapp:1.0
docker push ghcr.io/username/myapp:1.0
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
docker tag myapp:1.0 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0
.dockerignore
A .dockerignore file excludes files from the build context, reducing build time and image size.
# Version control
.git
.gitignore
# Dependencies (rebuilt in container)
node_modules
vendor
__pycache__
*.pyc
bin/
obj/
# Environment and secrets
.env
.env.*
*.pem
*.key
# IDE and OS files
.vscode
.idea
*.swp
.DS_Store
Thumbs.db
# Documentation
*.md
LICENSE
docs/
# Docker files (not needed inside the image)
docker-compose*.yml
Dockerfile*
.dockerignore
# Build artifacts
dist/
build/
coverage/
.nyc_output/
# Tests (if not running tests in container)
test/
tests/
*.test.js
*.spec.js
Best Practices
- Use multi-stage builds — Separate build and runtime stages to keep production images small and free of build tools and source code.
- Pin base image versions with SHA — Use
FROM node:22-alpine@sha256:abc123... for immutable, reproducible builds that cannot be affected by upstream tag changes.
- Run as non-root — Always add a
USER instruction to run the container process as a non-root user for defense in depth.
- Use .dockerignore — Exclude unnecessary files from the build context to speed up builds and avoid leaking secrets into images.
- Scan images in CI — Integrate Trivy or Docker Scout into your CI pipeline to catch vulnerabilities before deployment.
- Use health checks — Add
HEALTHCHECK instructions so orchestrators can detect and replace unhealthy containers automatically.
- Prefer COPY over ADD —
COPY is explicit and predictable; ADD has implicit behaviors (URL fetching, tar extraction) that can surprise you.
- Keep images small — Use alpine/slim bases, clean up package manager caches in the same
RUN layer, and avoid installing unnecessary packages.