| name | docker-ops |
| description | Docker Compose service management over SSH. Subcommands: logs (view service logs with filtering), restart (graceful service restart), telemetry (CPU/memory/disk/container stats), cleanup (prune Docker resources, dry-run by default). Use for day-to-day container operations. |
| disable-model-invocation | false |
| user-invocable | true |
| argument-hint | [logs|restart|telemetry|cleanup] [service] [options] |
| allowed-tools | Read, Bash, AskUserQuestion |
| metadata | {"version":"1.0","created":"2026-03-16T00:00:00.000Z","author":"Ability.ai","changelog":["1.0: Initial version — logs, restart, telemetry, and cleanup subcommands over SSH"]} |
Docker Ops
ℹ️ First, set expectations: before anything else, print one short line with this skill's version and its most recent change — the top entry of metadata.changelog above — e.g. docker-ops vX.Y — recent: <summary>. Then proceed.
Day-to-day Docker Compose service management over SSH. Four subcommands for the most common operations.
Prerequisites
Configure via environment variables or a local .env file:
SSH_HOST=<ip or hostname>
SSH_USER=<username, default: ubuntu>
SSH_KEY=<path to key, default: ~/.ssh/id_rsa>
APP_PATH=<remote app directory, default: ~/app>
COMPOSE_FILE=<compose file, default: docker-compose.yml>
Connection setup:
source .env 2>/dev/null || true
SSH_HOST=${SSH_HOST:-""}
SSH_USER=${SSH_USER:-"ubuntu"}
SSH_KEY=${SSH_KEY:-"~/.ssh/id_rsa"}
APP_PATH=${APP_PATH:-"~/app"}
COMPOSE=${COMPOSE_FILE:-"docker-compose.yml"}
RUN="ssh -i $SSH_KEY $SSH_USER@$SSH_HOST"
Parse $ARGUMENTS to extract the subcommand and options.
Subcommand: logs [service] [lines] [--errors]
View logs from one or all services. Supports filtering to errors only.
Argument Parsing
From $ARGUMENTS:
- First word after
logs: service name (or all / omitted = all services)
- Second word: number of lines (default: 100)
--errors flag: filter to error lines only
Execution
Single service:
SERVICE=$1
LINES=${2:-100}
$RUN "cd $APP_PATH && docker compose -f $COMPOSE logs --tail=$LINES --timestamps $SERVICE 2>&1"
All services:
$RUN "cd $APP_PATH && docker compose -f $COMPOSE logs --tail=$LINES --timestamps 2>&1"
With --errors flag:
$RUN "cd $APP_PATH && docker compose -f $COMPOSE logs --tail=$LINES $SERVICE 2>&1 | grep -iE 'error|exception|traceback|critical|fatal|warning'"
Time-based filter (e.g. logs backend --since=1h):
SINCE=${SINCE:-"1h"}
$RUN "cd $APP_PATH && docker compose -f $COMPOSE logs --since=$SINCE $SERVICE 2>&1"
Named Container Logs (non-compose)
If the service name doesn't match a compose service, try as a Docker container name directly:
$RUN "docker logs --tail=$LINES --timestamps $SERVICE 2>&1"
Subcommand: restart [service|all]
Graceful restart of one or all services.
Execution
Single service:
SERVICE=$1
$RUN "cd $APP_PATH && docker compose -f $COMPOSE restart $SERVICE && echo 'restarted: $SERVICE'"
All services (ordered stop/start to respect dependencies):
$RUN "cd $APP_PATH && docker compose -f $COMPOSE down && docker compose -f $COMPOSE up -d"
Post-restart Check
Wait 5 seconds, then verify:
sleep 5
$RUN "cd $APP_PATH && docker compose -f $COMPOSE ps"
Flag any containers not in Up state after restart.
Named Container (non-compose)
$RUN "docker restart $SERVICE && sleep 3 && docker inspect $SERVICE --format '{{.State.Status}}'"
Subcommand: telemetry
Collect resource usage statistics.
System Resources
$RUN "df -h / /var /tmp 2>/dev/null | head -6"
$RUN "free -h"
$RUN "uptime && nproc"
$RUN "ps aux --sort=-%mem | head -10"
Docker Resources
$RUN "docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}' 2>/dev/null"
$RUN "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' 2>/dev/null"
$RUN "docker system df 2>/dev/null"
Compose Service Status
$RUN "cd $APP_PATH && docker compose -f $COMPOSE ps"
Output Summary
Present as a clean table:
RESOURCE TELEMETRY
─────────────────────────────────
Host: $SSH_HOST
Time: [timestamp]
DISK
/ XX% used (XG of XG)
MEMORY
Used: XG / Total: XG (XX%)
LOAD
1m: X.XX 5m: X.XX 15m: X.XX (N CPUs)
CONTAINERS
[name] CPU: X% MEM: XM / XM (XX%)
...
DOCKER DISK
Images: X.XGB
Containers: XMB
Volumes: X.XGB
Build cache: XMB
Highlight any metric above warning thresholds:
- Disk >85% — WARNING
- Memory >80% — WARNING
- Any container CPU sustained >80% — WARNING
- Docker disk >10GB — NOTE
Subcommand: cleanup [--execute]
Prune Docker resources. Dry-run by default — requires --execute to make changes.
Dry Run (default)
Estimate reclaimable space:
$RUN "docker system df 2>/dev/null"
$RUN "docker images -f dangling=true --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}' 2>/dev/null | head -20"
$RUN "docker ps -a -f status=exited --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' 2>/dev/null | head -20"
$RUN "docker volume ls -f dangling=true 2>/dev/null | head -20"
Report estimated reclaim:
DRY RUN — No changes made
─────────────────────────────────
Dangling images: N items (~XGB reclaimable)
Stopped containers: N items
Unused volumes: N items
Build cache: ~XGB
To execute cleanup, run: /docker-ops cleanup --execute
Execute
Only when --execute is explicitly passed:
$RUN "docker system prune -f 2>&1"
Show before/after disk usage:
$RUN "df -h / | tail -1"
$RUN "docker system df 2>/dev/null"
Do not prune volumes without explicit user confirmation — volumes may contain persistent data.
Related Skills