| name | docker-advanced-workflows |
|---|---|
| description | Multi-stage pipelines, sidecar patterns, healthchecks, init containers, signal handling, and graceful shutdown. |
| standards-version | 1.9.0 |
Use this skill when:
- Designing multi-stage build pipelines for production images
- Implementing sidecar containers alongside a primary service
- Configuring healthchecks for containers or compose services
- Setting up init containers or entrypoint scripts
- Handling process signals and graceful shutdown in containers
- Copying files between containers and the host during development
- Monitoring container resource usage in real time
- Updating container resource limits without restarting
- The use case: pipeline design, sidecar setup, healthcheck config, signal handling, resource tuning
- Language/runtime of the containerized application (affects signal handling and healthcheck commands)
- Whether the workload is development or production (affects patterns and strictness)
- Identify the pattern - determine which advanced workflow applies (multi-stage, sidecar, healthcheck, init, signal handling, live monitoring).
- Check current state - use
docker_listContainers,docker_inspectContainer, anddocker_statsto understand the running environment. - Apply the pattern - write or modify Dockerfiles, compose files, or entrypoint scripts.
- Validate - use
docker_topto verify processes,docker_statsfor resource usage,docker_eventsto watch lifecycle events. - Tune - use
docker_updateto adjust CPU/memory limits live, ordocker_waitto synchronize on container completion.
- Multi-stage builds
- Healthcheck reference
- docker update reference
- docker events reference
- Graceful shutdown in containers
- docker cp reference
User: I want to set up a proper healthcheck for my Node.js API container.
Agent: Here are three approaches depending on your setup:
Dockerfile healthcheck:
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"Compose healthcheck:
services:
api:
build: .
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
depends_on:
db:
condition: service_healthyUsing curl (if available in the image):
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1You can verify it's working with docker_inspectContainer - look at State.Health.Status and State.Health.Log.
User: My container is using too much memory. Can I change the limit without restarting it?
Agent: Yes, use docker_update:
docker_update with containerId="my-api", memory="512m", memorySwap="1g"
Then verify with docker_stats:
docker_stats with containers=["my-api"]
If the container is already exceeding the new limit, it may get OOMKilled. Check current usage with docker_stats first, then set the limit above current usage but below the problematic threshold.
User: How do I handle graceful shutdown in my container?
Agent: Docker sends SIGTERM to PID 1 when stopping a container, then SIGKILL after the grace period (default 10s).
Key rules:
- Trap SIGTERM in your app - don't ignore it
- Use exec form CMD -
CMD ["node", "server.js"]notCMD node server.js(shell form wraps in/bin/shwhich may not forward signals) - Use tini for multi-process containers - if PID 1 isn't your app
# Good - exec form, signals go directly to node
CMD ["node", "server.js"]
# For multi-process - use tini as init
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]Node.js signal handler:
process.on('SIGTERM', () => {
console.log('SIGTERM received, closing server...');
server.close(() => {
console.log('Server closed, exiting');
process.exit(0);
});
setTimeout(() => process.exit(1), 5000);
});You can test with docker_stop using a short timeout and watch events with docker_events.
| Tool | Purpose |
|---|---|
docker_stats |
Monitor CPU, memory, network I/O for running containers |
docker_top |
See running processes inside a container (verify PID 1, check for zombies) |
docker_events |
Watch container lifecycle events (start, stop, die, health_status) |
docker_update |
Change CPU/memory limits and restart policy without restarting |
docker_wait |
Block until a container exits and get its exit code |
docker_cp |
Copy files between container and host (configs, logs, debug artifacts) |
docker_inspectContainer |
Check healthcheck status, restart count, resource config |
docker_exec |
Run commands inside a running container for diagnostics |
Multi-stage build pipeline flow:
1. docker_build with context and Dockerfile
2. docker_run to test the image
3. docker_stats to verify resource usage
4. docker_top to check process tree
5. docker_tag for versioning
6. docker_push to registry
Live resource tuning flow:
1. docker_stats - observe current CPU/memory usage
2. docker_update - adjust limits (cpus, memory, restartPolicy)
3. docker_stats - verify the change took effect
4. docker_events - watch for OOMKill or restart events
- Shell form CMD swallows signals -
CMD node server.jsruns under/bin/sh -c, which does not forward SIGTERM to the node process. Always use exec form:CMD ["node", "server.js"]. - Healthcheck using curl in distroless images - curl isn't available. Use a language-native healthcheck or a static binary.
- start_period too short - if your app takes 30s to boot but start_period is 5s, Docker marks it unhealthy before it's ready. Set start_period generously.
- Forgetting --init or tini - without an init process, zombie processes accumulate. Use
--initflag ondocker runor install tini. - Updating memory below current usage - calling
docker_updatewith a memory limit lower than current RSS triggers an immediate OOMKill. Checkdocker_statsfirst. - docker events without --until - blocks forever waiting for events. Always set
--untilor use thedocker_eventsMCP tool which defaults to a bounded window. - Ignoring restart policy interactions - setting
restart: alwayswith a crashing container creates an infinite restart loop. Useon-failurewith a max retry count during development. - COPY --from in multi-stage without naming stages - use
FROM node:22-alpine AS builderandCOPY --from=builderinstead of numeric indices, which break when stages are reordered.