About docker-development
docker-development is a practical guide for Docker development and CI, covering image builds, security, caching, Compose, and test troubleshooting. Short examples and topic-specific references help developers address common container engineering tasks.
Key features
- 01Node.js and Go multi-stage build examples
- 02Image and container security guidance
- 03Build cache and Docker Bake guidance
- 04Compose lifecycle and configuration notes
- 05CI testing patterns and troubleshooting
- 06Index of topic-specific references
Use cases
- 01Design multi-stage images for Node.js or Go services
- 02Review container settings for secret and permission risks
- 03Improve build caching by ordering dependency layers
- 04Troubleshoot Compose service behavior and CI tests
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ netresearch/ docker-development-skill --skill docker-developmentAfter installing, type/docker-developmentin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
checkpoints.yamlreferences/bake-ci-cache.mdbind-mount-ownership.mdbuild-secret-leaks.mdci-testing.mdcompose-runtime-lifecycle.mddatabase-container-readiness.mddind-testing-patterns.mdengine-and-image-upgrades.mdgpg-verification.mdimage-ca-trust.mdmulti-stage-caching.mdphp-fpm-worker-starvation.mdregistry-catalogue-and-pin-rot.mdDetails
From the project READMEDocker Development
Core Principles
- Minimal -- Alpine/distroless, multi-stage
- Secure -- Non-root USER, no layer secrets, pin versions
- Testable -- entrypoint bypass, DNS mocking
- Cache-efficient -- deps first, clean in-layer
Quick Reference
Multi-Stage Build (Node.js)
FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
FROM node:24-alpine
RUN addgroup -g 1001 app && adduser -u 1001 -G app -D app
USER app
COPY --from=builder /app .
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
Multi-Stage Build (Go -- scratch/distroless)
FROM golang:1.26-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
CMD ["/server"]
Layer Optimization
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
Build Cache: Copy Dependency Files First
COPY package*.json ./
RUN npm ci
COPY . .
Manifests before source keeps install layers cached.
BuildKit Secrets
RUN --mount=type=secret,id=ssh_key,dst=/root/.ssh/id_rsa git clone [email protected]:org/repo.git
ARG leaks via docker history and SLSA provenance --
references/build-secret-leaks.md
Docker Bake (Multi-Platform)
target "app" {
platforms = ["linux/amd64", "linux/arm64"]
}
CI builds go through Bake and netresearch/.github/.github/workflows/build-container-bake.yml@main, one image or several. The reusable adds a GHA cache per target, so the HCL declares none; a scheduled rebuild passes cache: false -- references/bake-ci-cache.md
Security Anti-Patterns
| Anti-pattern | Fix |
|---|---|
FROM image:latest | Pin version: image:1.2.3-alpine |
No USER directive | adduser + USER appuser |
chmod 777 | Use specific permissions: chmod 550 |
privileged: true in compose | Remove or use specific cap_add |
volumes: [/:/host] | Mount only needed paths |
ports: ["0.0.0.0:3000:3000"] | Bind to 127.0.0.1:3000:3000 |
ENV DB_PASSWORD=secret | Use --mount=type=secret or compose secrets |
CI Testing Gotchas
- Bypass entrypoint:
docker run --rm --entrypoint php myimage -v - Mock upstream DNS:
docker run --rm --add-host backend:127.0.0.1 nginx-image nginx -t - Compose validation:
cp .env.example .envbeforedocker compose config - Secret scanning: exclude
.env.example, README, docs - Root-owned artifacts: bind-mount dirs (
EACCES) --references/bind-mount-ownership.md
.dockerignore
Exclude: .git, node_modules/vendor, .env*, *.pem, *.key
Compose Essentials
- startup ordering:
depends_on.condition: service_healthy+healthcheckstart_period networks.internal: trueisolates databasesprofiles: [debug]: start only with--profile debug.downand--remove-orphansignore a disabled profile's containers, so a service you stop enabling keeps running --docker compose --profile '*' down, ordocker compose down <service>(references/compose-runtime-lifecycle.md)- logging drivers block by default: a stalled remote endpoint (
awslogs, ...) stalls the app's writes;mode: non-blocking+max-buffer-sizedrops lines instead, protecting a running container only (references/compose-runtime-lifecycle.md) - shared image ref: define ONCE per file as top-level extension field + anchor --
x-app-image: &app-image registry/app:${APP_IMAGE_VERSION:-85}, services useimage: *app-image. The field must sit ABOVEservices:— an alias is only valid after its anchor in document order.:-defaults cover unset AND empty vars (a bare omitted tag silently resolves:latest). Anchors are file-local: every overlay file needs its own. Verify both paths:APP_IMAGE_VERSION= docker compose configand with an override
References
references/bake-ci-cache.md-- the shared bake reusable;-f docker-bake.hclbeside a compose file; GHA cache scope per target; uncached scheduled rebuild that still writes the cache; helper steps in bake-action's directoryreferences/ci-testing.md-- CI testing patterns for Docker images; busyboxshvs bash in-cstrings;timeoutarounddocker compose runleaves the container running when PID 1 ignores the signalreferences/dind-testing-patterns.md-- Docker-in-Docker testing patternsreferences/bind-mount-ownership.md-- root-owned bind-mount artifacts; a tmpfs on a parent path hiding a volume below itreferences/gpg-verification.md-- gpgv patterns; stale keybox locksreferences/registry-catalogue-and-pin-rot.md-- catalogue probes; pin rotreferences/build-secret-leaks.md--ARGin provenancereferences/php-fpm-worker-starvation.md-- keepalive pins php-fpmreferences/engine-and-image-upgrades.md--MinAPIVersioncuts off socket-reading sidecars; probe-container upgrade check; a base image changing itsUSER; PHP 8.5 OPcache built into the corereferences/database-container-readiness.md-- seeded DB images logready for connectionstwice; verify the seed, not the logreferences/compose-runtime-lifecycle.md-- a disabled profile's container survivesdownandup --remove-orphans; blocking vs non-blocking log delivery and what non-blocking does not protectreferences/image-ca-trust.md-- after a CA compromise: measure the published image'sca-certificatesversion and root subjects;grepfor a CA name in the bundle always returns 0