Playbook / updated 28 Sep 2026
Got hacked: single Docker host
Got hacked: single Docker host
For one Linux box running Docker Compose stacks. Budget: one evening to contain, one weekend to rebuild. The rule that matters most: you rebuild, you don't clean. Once someone had root or the Docker socket, you can't prove the box is clean.
Signs you're here
- CPU or GPU pinned at 100% with nothing of yours running (miners love homelab GPUs).
- Containers you didn't create, often
alpine,ubuntu, or random names, sometimes--privileged. - New SSH keys in
authorized_keys, new users, new cron entries. - Outbound traffic to mining pools or odd IPs. Your ISP or a Shadowserver report emails you.
- Your Ollama / GPU box suddenly slow and your power bill up.
1. Contain (tonight, 15 minutes)
Cut it off from the internet, but don't power it off yet. Running processes are evidence.
# Pull the WAN side: unplug the uplink, or block the host at the router.
# If you only have SSH access, block everything except your own IP first:
sudo iptables -I INPUT -s <your-ip> -j ACCEPT
sudo iptables -I OUTPUT -d <your-ip> -j ACCEPT
sudo iptables -A INPUT -j DROP
sudo iptables -A OUTPUT -j DROP
- Kill tunnels and port-forwards: stop
cloudflared, remove router forwards to this box. - From a different, clean device, change passwords that were stored on or typed into this box.
2. Capture (30 minutes, before you change anything else)
Save this to a USB stick or another machine, not the compromised disk.
OUT=/mnt/usb/incident-$(date +%F); mkdir -p "$OUT"
docker ps -a --no-trunc > "$OUT/containers.txt"
docker images --no-trunc > "$OUT/images.txt"
for c in $(docker ps -aq); do docker inspect "$c"; done > "$OUT/inspect.json"
for c in $(docker ps -aq); do echo "== $c"; docker diff "$c"; done > "$OUT/diffs.txt"
docker events --since 72h --until 0s > "$OUT/docker-events.txt"
ps auxf > "$OUT/ps.txt"
sudo ss -tupan > "$OUT/sockets.txt"
sudo crontab -l; ls -la /etc/cron.* > "$OUT/cron.txt" 2>&1
sudo cat /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys > "$OUT/ssh-keys.txt" 2>&1
sudo journalctl --since "-7 days" > "$OUT/journal.txt"
sudo last -Faiw > "$OUT/logins.txt"
Export any suspicious container for later inspection:
docker export <container> | gzip > "$OUT/<container>.tar.gz"
3. Find the way in (1 hour)
You need this answer, or the rebuilt box gets owned the same way. Check, in order:
| Suspect | Look for |
|---|---|
| Docker API on TCP | -H tcp:// in /etc/docker/daemon.json or a systemd override; port 2375 in sockets.txt |
docker.sock mounted into a web-facing container |
"/var/run/docker.sock" in inspect.json |
| Exposed admin/AI port | Any 0.0.0.0 bind in sockets.txt for 3000, 8080, 9000, 9090, 11434, 4000 |
| Tunnel with no Access policy | Hostnames in your cloudflared config; open each in a private window |
| Weak SSH | Password auth on, root login on, unknown IPs in logins.txt |
| Vulnerable app | Versions in images.txt against the relevant cards in risks/ |
4. Rebuild (weekend)
- Wipe and reinstall the OS. Don't reuse the old root disk.
-
Rotate every secret that lived on the box:
.envfiles, API keys (OpenAI, Anthropic, Cloudflare), tunnel token, database passwords, SSH keys, and Tailscale auth keys. -
Restore data from backups taken before the first sign of trouble. Restore config by re-reading it, not copying it blindly; attackers leave things in compose files and cron.
-
Pull fresh images by digest or pinned tag. Don't reuse local images from the old box.
- Close the hole you found in step 3 before the box goes back online.
5. Afterwards
- Check other machines on the same LAN for the same SSH keys, cron entries, or container names.
- Check your email and accounts at https://haveibeenpwned.com/ if credentials were on the box.
- Set up an alert for new containers or high CPU. Uptime Kuma or a Grafana alert is enough.
- Write down what happened. If it's something others will hit, it's a labsec card.
Sources
- NIST SP 800-61 Rev. 3, incident response recommendations: https://csrc.nist.gov/pubs/sp/800/61/r3/final
docker diff: https://docs.docker.com/reference/cli/docker/container/diff/docker export: https://docs.docker.com/reference/cli/docker/container/export/docker events: https://docs.docker.com/reference/cli/docker/system/events/