| name | docker-pilot |
| version | 1.0.0 |
| description | Safe, intelligent Docker container management — fleet status, lifecycle operations, cleanup, compose stacks, troubleshooting, and security hardening. Classifies every command by risk level (READ / RISKY / DESTRUCTIVE) with mandatory confirmation gates. Use when managing Docker containers, images, volumes, networks, compose stacks, or debugging container issues. |
| changelog | Initial release — fleet view, safety classification, cleanup playbook, compose setup, troubleshooting runbooks, Telegram formatting |
| metadata | {"clawdbot":{"emoji":"🚢","requires":{"bins":["docker"]},"os":["linux","darwin","win32"]}} |
Docker Pilot 🚢
Safe, intelligent Docker management. Not just a command reference — an operational guide that classifies risk, protects critical services, and formats output for chat.
When to Use
Use when the task involves Docker, Dockerfiles, containers, images, Compose, volumes, networking, debugging, or any container lifecycle operation. This is the default Docker skill — apply it whenever Docker work appears.
Companion Skills
This skill extends the existing ClawHub docker skill (v1.0.4 by ivangdavila). Install both for full coverage:
clawhub install docker — Dockerfile patterns, image building, security hardening reference
clawhub install docker-pilot — Operational management, safety rails, fleet view, troubleshooting
Safety Architecture ⚠️
Every Docker command is classified by risk level. Follow these rules without exception.
🟢 READ (Safe — Can Always Run)
No side effects. Use freely.
docker ps
docker ps -a
docker ps --format '{{json .}}'
docker images
docker images --filter "dangling=true"
docker system df
docker system df -v
docker logs --tail 50 CONTAINER
docker logs --since 1h CONTAINER
docker inspect CONTAINER
docker stats --no-stream
docker network ls
docker network inspect NETWORK
docker volume ls
docker volume inspect VOLUME
docker history IMAGE
docker diff CONTAINER
docker port CONTAINER
docker top CONTAINER
docker events --since 1h
Parsing tip: Always use --format '{{json .}}' with python3 -m json.tool for structured data. docker inspect returns an array — always index [0].
🟡 RISKY (Modifies State — Show Impact First)
Requires showing the user what will change before executing.
docker stop CONTAINER
docker start CONTAINER
docker restart CONTAINER
docker pull IMAGE
docker tag SOURCE TARGET
docker network create/connect
docker volume create
docker update --restart=always
docker container rename
docker compose up -d
docker compose stop
docker compose restart
Rule: Before any 🟡 command, show:
- Current state (what's running, what will be affected)
- Expected impact (downtime, resource usage)
- Ask for confirmation
🔴 DESTRUCTIVE (Irreversible — Mandatory Confirmation)
NEVER run without:
- Showing exactly what will be destroyed
- Getting explicit verbal confirmation from the user
- No chained destructive commands (
docker rm $(docker ps -aq) is FORBIDDEN)
docker rm CONTAINER
docker rmi IMAGE
docker volume rm VOLUME
docker system prune
docker system prune -a
docker system prune --volumes
docker compose down -v
docker network rm NETWORK
docker rm -f CONTAINER
docker exec CONTAINER rm -rf /
docker swarm leave --force
Confirmation pattern:
⚠️ DESTRUCTIVE OPERATION
Will remove: [list items]
Impact: [data loss / service disruption / etc.]
Type "confirm" to proceed:
🛡️ Protected Services
Some services are critical infrastructure. Never stop, restart, or remove these without explicit override:
protected_services:
- adguardhome
- unbound
- nginx
- traefik
- pihole
Rule: Before stopping a protected service, check DNS fallback:
cat /etc/resolv.conf | grep -v adguard | grep nameserver
Fleet Status 📊
The primary interface for understanding what's running. Use this format for all status reports in chat:
Fleet Overview (Telegram-Formatted)
🐳 Docker Fleet — 5 containers
🟢 adguardhome Up 4 days 43MB DNS/ad-blocking [PROTECTED]
🟢 buck-dashboard Up 8 days 120MB System dashboard
🟢 verdaccio Up 21 days 58MB NPM registry
🟢 mockserver Up 21 days 42MB API mocking
🟢 gitbox Up 21 days 35MB Git server
📦 Images: 45 total (37 dangling, ~3GB reclaimable)
💾 Disk: 68GB/233GB used (31%)
🔧 Compose: NOT INSTALLED
Commands to Generate Fleet View
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}'
docker images | wc -l
docker images --filter "dangling=true" -q | wc -l
docker system df
docker compose version 2>/dev/null || docker-compose version 2>/dev/null || echo "NOT INSTALLED"
Service Map
Map container names to functional roles. Maintain this in a local config:
services:
adguardhome:
role: "DNS/ad-blocking"
critical: true
protected: true
port: 53
network: host
buck-dashboard:
role: "System dashboard"
critical: false
port: 8080
network: bridge
verdaccio:
role: "NPM registry"
critical: false
port: 4873
network: bridge
mockserver:
role: "API mocking"
critical: false
port: 1080
network: bridge
gitbox:
role: "Git server"
critical: false
port: 8081
network: bridge
Compose Setup 🔧
If docker compose is not installed, install it first:
docker compose version 2>/dev/null || echo "NOT INSTALLED"
sudo apt install docker-compose-v2
docker compose version
Why compose matters: Without compose, every container is a docker run command with 10+ flags that must be memorized or scripted. Compose gives you declarative, version-controlled, reproducible deployments.
Cleanup Playbook 🧹
Run this when disk usage is high or when docker system df shows bloat.
Step 1: Audit (Always READ first)
docker system df
docker images --filter "dangling=true"
docker ps --filter "status=exited" --filter "status=created"
docker network ls --filter "type=custom"
docker volume ls --filter "dangling=true"
docker system df -v | grep "Build Cache"
Step 2: Safe Cleanup (No data loss)
docker image prune
docker container prune
docker network prune
docker builder prune
Step 3: Aggressive Cleanup (⚠️ Confirm first)
docker image prune -a
docker volume prune
docker system prune -a --volumes
Step 4: Verify
docker system df
docker ps
docker images
Health Checks 🩺
Add Health Checks to Running Containers
docker inspect --format='{{.Config.Health}}' CONTAINER
docker update --health-cmd="curl -f http://localhost:8080/ || exit 1" \
--health-interval=30s \
--health-timeout=5s \
--health-retries=3 \
CONTAINER
Common Health Check Commands
curl -f http://localhost:PORT/ || exit 1
nc -z localhost PORT || exit 1
dig +short google.com @localhost || exit 1
pgrep -x PROCESS_NAME || exit 1
Restart Policies
docker update --restart=always CONTAINER
docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' CONTAINER
Log Management 📋
Configure Log Rotation (Prevents Disk Fill)
docker run --log-opt max-size=10m --log-opt max-file=3 ...
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Smart Log Reading
docker logs --tail 50 CONTAINER
docker logs --since 1h CONTAINER
docker logs -f --since 5m CONTAINER & PID=$! ; sleep 30 ; kill $PID
docker logs CONTAINER 2>&1 | grep -i "error\|exception\|fail\|fatal" | tail -20
docker logs CONTAINER --since 1h | python3 -m json.tool | grep "error"
Troubleshooting Runbooks 🔍
Container Won't Start
docker inspect --format='{{.State.ExitCode}}' CONTAINER
docker logs --tail 50 CONTAINER
docker inspect --format='{{.State.OOMKilled}}' CONTAINER
docker inspect --format='{{.HostConfig.Memory}}' CONTAINER
docker run --rm -it --entrypoint /bin/sh IMAGE
Port Conflict
ss -tlnp | grep :PORT
lsof -i :PORT
docker ps --filter "publish=PORT"
Disk Full
docker system df -v
df -h /var/lib/docker
docker image prune
docker container prune
docker builder prune
docker image prune -a
Image Pull Failure
curl -I https://registry-1.docker.io/v2/
docker login
docker pull image@sha256:DIGEST
Crash Loop
docker inspect --format='{{.RestartCount}}' CONTAINER
docker logs --tail 100 CONTAINER
Network Issues
docker network create mynet
docker network connect mynet CONTAINER
docker exec CONTAINER cat /etc/resolv.conf
docker exec CONTAINER nslookup google.com
Compose Stacks 📦
Creating a Compose File
version: "3.8"
services:
app:
image: myapp:1.0
restart: unless-stopped
ports:
- "8080:8080"
environment:
- NODE_ENV=production
volumes:
- app-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
deploy:
resources:
limits:
memory: 512M
cpus: "0.5"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
app-data:
Compose Lifecycle
docker compose up -d
docker compose ps
docker compose logs -f --tail 50
docker compose restart app
docker compose pull && docker compose up -d
docker compose down
docker compose down -v
Compose Traps
depends_on waits for container start, NOT service ready — use condition: service_healthy
.env file must be next to docker-compose.yml — wrong directory = silently ignored
- Volume mounts overwrite container files — empty host dir = empty container dir
docker compose run does NOT start dependencies
- YAML anchors don't work across files — use multiple compose files instead
Security Hardening 🔒
Container Security
docker run --user 1000:1000 ...
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE ...
docker run --read-only --tmpfs /tmp ...
docker run -m 512m --cpus=0.5 ...
docker run --security-opt=no-new-privileges ...
Image Security
docker pull nginx:1.25.3-alpine
docker scout cves IMAGE
docker pull image@sha256:DIGEST
NEVER Do These
- ❌
docker run --privileged — disables ALL security
- ❌
-v /:/host — mounts entire host filesystem
- ❌
--pid=host — can see/kill host processes
- ❌
--network=host on non-DNS containers — unnecessary exposure
- ❌ Secrets in ENV or ARG — visible in
docker inspect and docker history
- ❌
docker rm $(docker ps -aq) — chained destructive command
- ❌
docker system prune -a without audit first
Resource Monitoring 📈
Quick Health Check
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}'
docker system df -v
df -h /var/lib/docker
free -h
Alert Thresholds
| Metric | Warning | Critical | Action |
|---|
| Disk usage | >80% | >90% | Run cleanup playbook |
| Memory | >80% | >95% | Add limits or restart heavy containers |
| Container restarts | >3/hour | >10/hour | Check logs, likely crash loop |
| Dangling images | >10 | >30 | Run image prune |
| Log file size | >100MB | >1GB | Add log rotation |
Dockerfile Patterns 📝
Layer Cache Optimization
# ✅ GOOD — requirements rarely change, code changes often
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# ❌ BAD — invalidates cache on every code change
COPY . .
RUN pip install -r requirements.txt
Multi-Stage Build
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
Image Size Traps
- Multi-stage: forgotten
--from=builder copies from wrong stage silently
COPY . . before RUN npm install = cache invalidated on every code change
ADD extracts archives automatically — use COPY unless you need extraction
rm -rf /var/lib/apt/lists in separate RUN = space not reclaimed (layers)
.git copied = megabytes of bloat — use .dockerignore
ARG vs ENV
ARG only available during build, visible in docker history — NEVER for secrets
ENV persists at runtime — use for configuration
ARG with empty override uses default, not empty string
ARG must be re-declared after each FROM in multi-stage
Telegram Formatting Guide 📱
When reporting Docker status in Telegram, use this format:
Fleet Status
🐳 **Docker Fleet** — 5 running
🟢 **adguardhome** — DNS/ad-blocking [PROTECTED]
Up 4 days · 43MB RAM · :53
🟢 **buck-dashboard** — Dashboard
Up 8 days · 120MB RAM · :8080
🟢 **verdaccio** — NPM registry
Up 21 days · 58MB RAM · :4873
🟡 **mockserver** — API mocking
Up 21 days · 42MB RAM · :1080
🟢 **gitbox** — Git server
Up 21 days · 35MB RAM · :8081
📦 37 dangling images (3GB reclaimable)
💾 68GB/233GB disk (31%)
Alert Format
⚠️ **Container Alert**
🔴 **mockserver** — Exited (1) 2min ago
Last log: `Connection refused on port 1080`
Restart? (3 restarts in last hour)
Cleanup Report
🧹 **Docker Cleanup**
Removed:
- 12 dangling images (450MB)
- 3 stopped containers
- 1 unused network
Reclaimed: **1.2GB**
Current disk: 62GB/233GB (27%)
Quick Reference Card
| Task | Command |
|---|
| Fleet status | docker ps --format 'table {{.Names}}\t{{.Status}}' |
| Resource usage | docker stats --no-stream |
| Disk usage | docker system df |
| Container logs | docker logs --tail 50 CONTAINER |
| Inspect JSON | docker inspect CONTAINER | python3 -m json.tool |
| Find dangling | docker images --filter "dangling=true" -q | wc -l |
| Safe cleanup | docker image prune && docker container prune && docker builder prune |
| Health check | docker inspect --format='{{.State.Health.Status}}' CONTAINER |
| Restart policy | docker update --restart=always CONTAINER |
| Compose up | docker compose up -d |
| Compose logs | docker compose logs -f --tail 50 |
First-Run Setup
When this skill is activated for the first time on a new machine:
- Check compose:
docker compose version — if missing, install it
- Scan fleet:
docker ps -a + docker system df — understand current state
- Set restart policies:
docker update --restart=unless-stopped for all running containers
- Configure log rotation: Add max-size/max-file to daemon.json or per-container
- Clean up: Run safe cleanup (image prune, container prune, builder prune)
- Build service map: Document what each container does
- Set up monitoring: Consider a cron to check fleet health periodically
Credits
Built on top of the docker skill by ivangdavila (v1.0.4). This skill adds:
- 🛡️ Safety architecture (READ/RISKY/DESTRUCTIVE classification with confirmation gates)
- 📊 Fleet status view with Telegram formatting
- 🔍 Troubleshooting runbooks (crash loops, disk full, port conflicts, DNS)
- 🧹 Step-by-step cleanup playbook
- 🩺 Health check and restart policy configuration
- 📋 Log management and rotation
- 🛡️ Protected services list (never stop AdGuard without DNS fallback)
- 📦 Compose setup guide and lifecycle management
- 🔒 Security hardening checklist
- 🚀 First-run setup guide