Skip to content
Back to skills

Docker

BSecurity

Use when writing or debugging Dockerfiles or docker-compose — layer caching, multi-stage builds, start order, an unreachable service.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
ai-agentsshellnodedockerkubernetesdebugginggit

Works with

  • cli

Security analysis

B85/100
  • highPerforms destructive filesystem operations

Pro shows the line behind each finding and how to fix it

Scanned October 4, 2026

npx -y skills add Getty/skills --skill docker --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Docker?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Docker
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/getty-docker/badge)](https://www.skillsdirectory.com/skills/getty-docker)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: docker
description: "Use when writing or debugging Dockerfiles or docker-compose — layer caching, multi-stage builds, start order, an unreachable service."
user-invocable: true
---

# Docker & Compose Reference

One skill for both: images are built with Docker, run together with Compose —
they ship as one workflow. This covers the decisions and traps, not the
basics.

## CLI generation

`docker compose` (v2, a docker plugin) is current; the standalone
`docker-compose` binary is the legacy v1 — scripts should call
`docker compose`. The top-level `version:` key in compose files is obsolete
and ignored; new files start directly with `services:`. The default project
name (container/network/volume prefix) is the directory name — override with
`name:` at the top level or `-p` when two checkouts of the same stack must
coexist.

## Dockerfile — the rules that pay rent

- **Order layers by change frequency.** Dependency manifests first
  (`COPY package.json .` → install → `COPY . .`), source last — otherwise
  every source edit invalidates the dependency layer and rebuilds the world.
- **Multi-stage** for anything compiled: build stage with toolchain,
  `COPY --from=build` into a slim runtime stage. The runtime image carries no
  compilers, no dev deps.
- **One `RUN` per logical step, cleanup in the same layer** —
  `apt-get update && apt-get install -y … && rm -rf /var/lib/apt/lists/*`
  in one command; a later `RUN rm` does not shrink earlier layers.
- **`USER` non-root** in the final stage; **`HEALTHCHECK`** if compose or an
  orchestrator should gate on readiness.
- `.dockerignore` is not optional: without it `.git`, local `node_modules`,
  and secrets ride into the build context (and bust the cache).
- `CMD ["exec", "form"]` — shell form wraps PID 1 in `/bin/sh`, which
  swallows SIGTERM and turns every stop into the 10 s kill timeout.
- `ARG` is build-time, `ENV` is runtime; an `ARG` value referenced in `ENV`
  is baked into the image — never route secrets through either (use
  `--mount=type=secret` / runtime env).

## Compose — service wiring

- **Startup order needs conditions, not just names.** Bare
  `depends_on: [db]` only orders *creation*. For "wait until ready":

  ```yaml
  services:
    app:
      depends_on:
        db:
          condition: service_healthy
    db:
      image: postgres:17
      healthcheck:
        test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
        interval: 5s
        retries: 10
  ```

- **Services reach each other by service name** on the default network, at
  the *container* port — `ports:` is only for host access. `localhost`
  inside a container is the container itself; that's the classic
  "connection refused" in app→db URLs.
- **Named volumes vs bind mounts:** named volumes for data that belongs to
  the stack (declared under top-level `volumes:`, survive `down`, die with
  `down -v`); bind mounts for source code in dev. First-use of a named
  volume copies the image's existing content into it — later image changes
  to that path are shadowed by the volume.
- **Env precedence:** shell env > `--env-file`/`.env` > `environment:` >
  `env_file:` > image `ENV`. `.env` is read by *compose itself* for
  `${interpolation}` in the YAML; `env_file:` feeds the *container* — they
  are different mechanisms. `$$` escapes a literal `$` in YAML values.
- **Override files:** `compose.yaml` + `compose.override.yaml` merge
  automatically (the standard dev/prod split); explicit stacking with
  `-f base.yaml -f prod.yaml`. **`profiles:`** gate optional services
  (`--profile debug` / `COMPOSE_PROFILES`) — a service with a profile simply
  doesn't exist otherwise, also not as a `depends_on` target.
- `restart: unless-stopped` for long-running business stacks; `on-failure`
  for one-shot jobs.

## Debug ladder

`docker compose ps` (state + health) → `logs -f <svc>` → `exec <svc> sh`
(inspect from inside; test DNS with `getent hosts <other-svc>`) →
`docker inspect <ctr>` (mounts, env, health log, exit code) →
`docker compose config` (the *merged, interpolated* YAML — the truth after
overrides and `.env`). Exit 137 = OOM-kill or SIGKILL timeout; check
memory limits before blaming the app. `compose up -d --build` rebuilds on
run; `--force-recreate` when only env/config changed but containers must
pick it up.

## Related

- `kubernetes-concepts` — when a stack outgrows one host, this is the next
  rung; compose services map to Deployments+Services conceptually.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…