Docker Expert
Build small, reproducible, non-root Docker images using Docker 27+ and BuildKit.
When to Use This Skill
- Creating or changing a
Dockerfile
- Creating or debugging a
docker-compose.yml or compose.yaml
- Converting a single-stage image into a multi-stage production image
- Improving Docker build cache behavior or BuildKit usage
- Adding
.dockerignore, health checks, or non-root execution
- Diagnosing Docker image-size, startup, networking, volume, or Compose issues
Core Workflow
Inspect the runtime contract - identify the app start command, listening port, required files, environment variables, native dependencies, and health endpoint. Read existing Dockerfiles, Compose files, lockfiles, and .dockerignore before making changes.
Design build and runtime stages - separate build dependencies from runtime dependencies. Pin an explicit maintained base-image version, use named build stages, and copy only required runtime artifacts into the final image.
Implement Dockerfile and ignore rules - order layers from least frequently changed to most frequently changed: base image, OS packages, dependency manifests, dependency installation, then application source. Add .dockerignore before relying on COPY . ..
Verify the image build - run docker build --progress=plain -t app:local .; fix every reported issue and re-run until clean. Change only an application source file, rebuild, and confirm dependency installation is cached.
Run the container - run docker run --rm --name app-local -p 8080:8080 app:local, replacing 8080 with the real application port. Check logs with docker logs app-local, verify the endpoint, fix failures, and re-run until clean.
Implement Compose configuration when needed - use Compose for multi-service applications or local development. Define explicit services, ports, networks, volumes, health checks, and dependency readiness.
Verify Compose configuration - run docker compose config; fix every reported issue and re-run until clean. Then run docker compose up --build --wait, inspect docker compose ps and docker compose logs, verify the application endpoint, and run docker compose down --volumes after testing.
Harden the final image - run as an unprivileged user, avoid latest tags, do not copy secrets into the image, and add a health check for long-running HTTP services. Run docker image inspect app:local; fix identified security or runtime problems and re-run until clean.
Reference Guide
Load detailed guidance only when the task needs it:
| Topic |
Reference |
Load When |
| Production Dockerfiles |
references/production-dockerfiles.md |
Creating, optimizing, or reviewing a Dockerfile |
| Compose development |
references/compose-development.md |
Working with Compose services, volumes, networks, or local dependencies |
| Security and health checks |
references/security-healthchecks.md |
Hardening an image, handling secrets, or adding health checks |
Key Patterns
Use multi-stage builds so compilers, source code, and development dependencies do not ship in the runtime image:
# syntax=docker/dockerfile:1.7
FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
RUN npm run build
FROM node:22-bookworm-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
RUN groupadd --system app && useradd --system --gid app app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER app
EXPOSE 8080
CMD ["node", "dist/server.js"]
Copy lockfiles before application source so dependency installation remains cached:
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
Wait for healthy dependencies in Compose instead of treating container startup as readiness:
services:
api:
build: .
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://localhost:8080/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
Common Mistakes
- Using a single-stage image - build tools, source files, and development dependencies ship to production. Use a separate build stage and minimal runtime stage.
- Copying source before installing dependencies - every source change invalidates dependency layers. Copy lockfiles first, install dependencies, then copy source.
- Using
latest image tags - builds are not reproducible. Use an explicit maintained tag such as node:22-bookworm-slim.
- Running as root - create and use an unprivileged runtime user.
- Missing
.dockerignore - build contexts can include node_modules, Git history, test output, and secrets. Exclude files that are not needed.
- Using
depends_on as readiness - startup order does not prove the dependency is ready. Define health checks and use condition: service_healthy.
1---2name: docker-expert3description: Use when working with Dockerfile, docker-compose.yml, .dockerignore, or multi-stage builds. Build, debug, secure, and optimize Docker 27+ images and Docker Compose services.4license: MIT5---67# Docker Expert89Build small, reproducible, non-root Docker images using Docker 27+ and BuildKit.1011## When to Use This Skill1213- Creating or changing a `Dockerfile`14- Creating or debugging a `docker-compose.yml` or `compose.yaml`15- Converting a single-stage image into a multi-stage production image16- Improving Docker build cache behavior or BuildKit usage17- Adding `.dockerignore`, health checks, or non-root execution18- Diagnosing Docker image-size, startup, networking, volume, or Compose issues1920## Core Workflow21221. **Inspect the runtime contract** - identify the app start command, listening port, required files, environment variables, native dependencies, and health endpoint. Read existing Dockerfiles, Compose files, lockfiles, and `.dockerignore` before making changes.23242. **Design build and runtime stages** - separate build dependencies from runtime dependencies. Pin an explicit maintained base-image version, use named build stages, and copy only required runtime artifacts into the final image.25263. **Implement Dockerfile and ignore rules** - order layers from least frequently changed to most frequently changed: base image, OS packages, dependency manifests, dependency installation, then application source. Add `.dockerignore` before relying on `COPY . .`.27284. **Verify the image build** - run `docker build --progress=plain -t app:local .`; fix every reported issue and re-run until clean. Change only an application source file, rebuild, and confirm dependency installation is cached.29305. **Run the container** - run `docker run --rm --name app-local -p 8080:8080 app:local`, replacing `8080` with the real application port. Check logs with `docker logs app-local`, verify the endpoint, fix failures, and re-run until clean.31326. **Implement Compose configuration when needed** - use Compose for multi-service applications or local development. Define explicit services, ports, networks, volumes, health checks, and dependency readiness.33347. **Verify Compose configuration** - run `docker compose config`; fix every reported issue and re-run until clean. Then run `docker compose up --build --wait`, inspect `docker compose ps` and `docker compose logs`, verify the application endpoint, and run `docker compose down --volumes` after testing.35368. **Harden the final image** - run as an unprivileged user, avoid `latest` tags, do not copy secrets into the image, and add a health check for long-running HTTP services. Run `docker image inspect app:local`; fix identified security or runtime problems and re-run until clean.3738## Reference Guide3940Load detailed guidance only when the task needs it:4142| Topic | Reference | Load When |43|-------|-----------|-----------|44| Production Dockerfiles | `references/production-dockerfiles.md` | Creating, optimizing, or reviewing a Dockerfile |45| Compose development | `references/compose-development.md` | Working with Compose services, volumes, networks, or local dependencies |46| Security and health checks | `references/security-healthchecks.md` | Hardening an image, handling secrets, or adding health checks |4748## Key Patterns4950Use multi-stage builds so compilers, source code, and development dependencies do not ship in the runtime image:5152```dockerfile53# syntax=docker/dockerfile:1.75455FROM node:22-bookworm-slim AS build56WORKDIR /app5758COPY package.json package-lock.json ./59RUN --mount=type=cache,target=/root/.npm npm ci6061COPY . .62RUN npm run build6364FROM node:22-bookworm-slim AS runtime65WORKDIR /app66ENV NODE_ENV=production6768RUN groupadd --system app && useradd --system --gid app app6970COPY package.json package-lock.json ./71RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev72COPY --from=build /app/dist ./dist7374USER app75EXPOSE 808076CMD ["node", "dist/server.js"]77```7879Copy lockfiles before application source so dependency installation remains cached:8081```dockerfile82COPY package.json package-lock.json ./83RUN --mount=type=cache,target=/root/.npm npm ci84COPY . .85```8687Wait for healthy dependencies in Compose instead of treating container startup as readiness:8889```yaml90services:91 api:92 build: .93 depends_on:94 db:95 condition: service_healthy96 healthcheck:97 test: ["CMD", "node", "-e", "fetch('http://localhost:8080/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]98 interval: 30s99 timeout: 5s100 retries: 3101 start_period: 10s102```103104## Common Mistakes105106- **Using a single-stage image** - build tools, source files, and development dependencies ship to production. Use a separate build stage and minimal runtime stage.107- **Copying source before installing dependencies** - every source change invalidates dependency layers. Copy lockfiles first, install dependencies, then copy source.108- **Using `latest` image tags** - builds are not reproducible. Use an explicit maintained tag such as `node:22-bookworm-slim`.109- **Running as root** - create and use an unprivileged runtime user.110- **Missing `.dockerignore`** - build contexts can include `node_modules`, Git history, test output, and secrets. Exclude files that are not needed.111- **Using `depends_on` as readiness** - startup order does not prove the dependency is ready. Define health checks and use `condition: service_healthy`.