Claude
Skills
Sign in
Back

containers

Included with Lifetime
$97 forever

Docker and Podman container management: Dockerfile optimization, multi-stage builds, Compose v2 orchestration, networking, volumes, security hardening, supply chain integrity, health checks, resource limits, Quadlet systemd integration, and debugging. Invoke whenever task involves any interaction with containers — writing Dockerfiles, configuring Compose, managing Podman Quadlets, reviewing container security, debugging container issues, or setting up image signing and scanning.

Image & Video

What this skill does


# Containers

Security is not optional. Every container runs non-root, with dropped capabilities, on a minimal base image. Convenience
defaults that weaken security posture are bugs, not trade-offs.

## Security Rules

These are non-negotiable defaults for every container configuration. Apply unconditionally — no exceptions for
development, convenience, or "temporary" setups.

- **Run as non-root.** Add a `USER` instruction with explicit UID/GID. Never run production containers as root.
- **Drop all capabilities.** `--cap-drop=ALL`, then add back only what the application genuinely requires. Most
  applications need zero.
- **Enable no-new-privileges.** `--security-opt=no-new-privileges` prevents setuid/setgid escalation.
- **Use read-only filesystem.** `--read-only` with `tmpfs` mounts for `/tmp`, `/run`, and any directories the app writes
  to.
- **Never pass secrets via ENV.** Visible in `docker inspect`, logs, and child processes. Use mounted secret files or
  Docker secrets.
- **Scan images before deployment.** Use Docker Scout, Trivy, or Grype in CI. Block images with critical CVEs.
- **Use distroless for production** when possible. No shell means no shell-based attacks and dramatically fewer CVEs.
- **Never use `--privileged`.** It removes almost all security restrictions. If a workload claims to need it, decompose
  the requirement into specific capabilities.
- **Never mount the Docker socket** (`/var/run/docker.sock`) into containers. It grants root-equivalent control over the
  host. Use purpose-built APIs or socket proxies with restricted access.
- **Use `--init` or tini/dumb-init for PID 1.** Regular applications don't reap zombie processes or handle signals
  correctly as PID 1. Use `docker run --init`, `RunInit=true` in Quadlets, or install tini in the Dockerfile. Without
  this, `docker stop` waits for the kill timeout and zombies accumulate.
- **Verify supply chain integrity.** Pin images to digest, generate SBOMs (Syft, Docker Scout), sign images with
  cosign/Sigstore. Use VEX documents to distinguish exploitable from non-exploitable CVEs. Block unsigned images in CI.

## References

- **Dockerfile patterns** — [`${CLAUDE_SKILL_DIR}/references/dockerfile-patterns.md`]: Multi-stage templates, layer
  optimization, base image selection, .dockerignore, ENTRYPOINT/CMD, BuildKit cache mounts, signal handling, OCI labels
- **Compose orchestration** — [`${CLAUDE_SKILL_DIR}/references/compose-orchestration.md`]: Service structure, depends_on
  conditions, env vars, secrets, networks, volumes, profiles, restart policies, override files, zero-downtime patterns
- **Security hardening** — [`${CLAUDE_SKILL_DIR}/references/security-hardening.md`]: Non-root patterns, read-only FS,
  capabilities, distroless, secrets, scanning, supply chain security, SBOM, image signing, VEX, hardening checklist
- **Networking** — [`${CLAUDE_SKILL_DIR}/references/networking.md`]: Driver selection, bridge/host/macvlan/ipvlan usage,
  port publishing, DNS, multi-network, common mistakes, iptables bypass
- **Storage and volumes** — [`${CLAUDE_SKILL_DIR}/references/storage-and-volumes.md`]: Volume types, named/bind/tmpfs,
  NFS/CIFS drivers, backup/restore, permissions, storage drivers, performance
- **Operations** — [`${CLAUDE_SKILL_DIR}/references/operations.md`]: Health checks, resource constraints, logging
  drivers, structured logging, debugging, monitoring, Quadlet patterns, Docker/Podman CLI compat

## Dockerfile Rules

- **Use multi-stage builds.** Separate build dependencies from the runtime image. Final stage contains only the
  binary/app and its runtime dependencies.
- **Choose minimal base images.** `alpine`, `*-slim`, `distroless`, or `scratch` for static binaries. Never use full OS
  images in production.
- **Pin base image versions.** Use digest pinning (`@sha256:...`) in CI for reproducibility. Use minor version tags
  (`3.13-slim`) in development. Never use `:latest`.
- **Order layers by change frequency.** Stable instructions first (OS packages), volatile instructions last (source
  code). Copy dependency manifests before source code for layer caching.
- **Combine RUN statements.** Merge `apt-get update` with `apt-get install` in the same `RUN`. Clean caches in the same
  layer. Sort packages alphabetically.
- **Use COPY, not ADD.** `COPY` for local files. `ADD` only when you need remote URL fetching or automatic tar
  extraction.
- **Use exec form for CMD/ENTRYPOINT.** `CMD ["app", "--flag"]` — not `CMD app --flag`. Shell form wraps in
  `/bin/sh -c`, making the shell PID 1. Shells don't forward signals to children — your app never receives SIGTERM on
  `docker stop`.
- **Use `exec "$@"` in entrypoint scripts.** Without `exec`, the shell spawns the app as a child and swallows signals.
  `exec` replaces the shell process with the app, making it PID 1.
- **Always create a .dockerignore.** Exclude `.git`, `node_modules`, `.env`, build artifacts, and documentation from the
  build context.
- **Use `--mount=type=secret` for build-time secrets.** Never `COPY` or `ENV` secrets — they persist in image layers.
- **Set WORKDIR to an absolute path.** Never use `RUN cd ... && ...`.
- **Use BuildKit cache mounts** for package manager caches:
  `RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt`. Avoids re-downloading dependencies
  on every build.
- **Use `docker buildx` for multi-platform images.** `docker buildx build --platform linux/amd64,linux/arm64` produces
  images for multiple architectures. Required for ARM/x86 portability.
- **Add OCI labels.** Use `LABEL org.opencontainers.image.*` for source, version, description. Enables registry
  identification and automated tooling.

## Compose Rules

- **Use Compose v2 specification.** No `version:` field. Use `compose.yml` (not `docker-compose.yml`).
- **Use `depends_on` with `condition: service_healthy`** for services that need initialization time (databases, caches).
- **Use `service_completed_successfully`** for one-shot dependencies like migrations.
- **Isolate networks.** Create separate networks for frontend/backend tiers. Use `internal: true` on backend networks to
  block outbound internet access.
- **Use named volumes for persistent data.** Bind mounts for development, named volumes for production. Never rely on
  anonymous volumes.
- **Always set resource limits.** `deploy.resources.limits` for memory and CPU. A container without memory limits can
  OOM-kill the host.
- **Configure log rotation.** Use `local` logging driver, or configure `json-file` with `max-size` and `max-file`.
  Default has no rotation.
- **Use restart policies.** `unless-stopped` for production services. `on-failure` for tasks that should retry but
  eventually stop.
- **Use profiles for optional services.** Debug tools, monitoring, and test runners behind `profiles:` — not started by
  default.
- **Use env_file for environment variables.** Keep `.env.example` in version control, `.env` in `.gitignore`.
- **Use file-based secrets.** Compose `secrets:` mounts files at `/run/secrets/<name>` — granular per-service access,
  not visible in `docker inspect` or process listings like env vars.
- **Use override files for multi-environment.** `compose.yml` for common defaults, `compose.prod.yml` for production
  overrides. Use explicit `-f` flags in production — never rely on automatic `compose.override.yml` loading.
- **Use structured logging (JSON).** Configure apps to emit JSON logs to stdout. Use Pino (Node), Zap (Go), or stdlib
  JSON formatters. Structured logs enable filtering and aggregation.

## Networking Rules

- **Always use user-defined bridge networks.** Default bridge has no DNS resolution and no isolation. User-defined
  bridges provide automatic service discovery by container name.
- **Use `host` network only for performance-critical workloads** that bind many dynamic ports. Not available on Docker
  Desktop.
- **Use `macvlan` when containers need LAN presence** with unique MAC addresses. 

Related in Image & Video