Docker stack ops
Reached over SSH to the docker host(s). Read infra/docker-stacks.md for host
aliases, the compose layout, and stack membership before acting. Docker needs
root: if the host connects as a non-root user (e.g. TrueNAS truenas_admin),
prefix docker / docker compose with sudo -n (works when that user has
passwordless sudo; the host-ops and truenas-ops skills carry the one-time fix).
Root hosts run it bare. capabilities/linux.md has the full Docker and host
command surface; it is not loaded up front.
Where compose files live depends on the setup and is recorded in
infra/docker-stacks.md: it may be a single /mnt/<pool>/docker/docker-compose.yml
(the TrueNAS layout), per-stack /opt/stacks/<stack>/compose.yaml (Dockge), or
stacks defined in Portainer. This skill is for troubleshooting/operating existing
stacks; to stand up a NEW app or stack, use the deploy-app skill.
Diagnose first
hh run <dockerhost> "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'" hh run <dockerhost> "docker ps -a --filter status=exited --format '{{.Names}}\t{{.Status}}'" hh run <dockerhost> "docker logs --tail 120 <container>" hh run <dockerhost> "docker inspect -f '{{.State.Health.Status}}' <container>"
The gluetun rule (most common arr failure)
If downloads are dead but the rest of the stack is fine, suspect the VPN sidecar before touching qBittorrent.
hh run <dockerhost> "docker logs --tail 60 gluetun" hh run <dockerhost> "docker exec gluetun wget -qO- https://ipinfo.io/ip"
If that IP is the home IP or the call fails, the tunnel is down. Fix order: restart gluetun, wait for healthy, confirm the egress IP is the VPN's, then restart qBittorrent. Never let qBittorrent run with a leaked (home) IP.
Common cases
- Container restart loop: read the tail of its logs for the fatal line; check a bad config volume, a missing dependency container, or a port clash.
- App reachable internally but not externally: this is usually network-diag territory (Cloudflare Tunnel or reverse proxy), not the container.
- Everything in a stack down after boot: order sensitivity. Bring up dependencies first (VPN, db), then the rest.
State-changing (confirm first)
# from the compose dir: Dockge => /opt/stacks/<stack>, single-file => /mnt/<pool>/docker hh run <dockerhost> "cd <composedir> && docker compose restart <service>" hh run <dockerhost> "cd <composedir> && docker compose up -d" hh run <dockerhost> "cd <composedir> && docker compose pull && docker compose up -d"
Do not docker compose down -v or prune volumes without an explicit go-ahead;
the -v and prune paths delete app data. Image pruning is fine, volume pruning
is not.


