Self-healing containers that know when to give up.
A wrapper around the Docker SDK: list, start, stop, restart, inspect and tail containers without remembering flags, plus an HTTP API for scripts. Its health monitor restarts containers stuck at high CPU or memory, a small version of what Kubernetes does for you.
A restart policy that won't make things worse.
A naive monitor restarts a broken container forever and hides the real problem. ContainerCraft's policy is the part that's actually designed.
N breaches, not one spike
Builds and cache warm-ups spike CPU legitimately. The monitor acts only after several consecutive checks over the threshold.
Cooldown after restart
A restarted container gets time to warm up before it can be judged again, so it isn't restarted twice for the same load.
Gives up, says why
Too many restarts in a 10-minute window and it stops, like Kubernetes' CrashLoopBackOff: "Restarting isn't fixing it; needs a human."
What it looks like
Real output against a container running an infinite loop: two restarts, then the monitor stops trying.
$ python craft.py monitor --threshold 50 --interval 3 --consecutive 2 --restart --max-restarts 2
Monitoring every 3s · CPU > 50% or memory > 90% for 2 checks · auto-restart ON
15:56:07 ⚠️ cc-burner: CPU 101% > 50% (1/2 checks)
15:56:09 🔄 cc-burner: CPU 101% > 50% for 2 checks, restarting
15:56:21 ✅ cc-burner: restart complete
15:56:23 ⚠️ cc-burner: CPU 100% > 50% (1/2 checks)
15:56:25 🔄 cc-burner: CPU 101% > 50% for 2 checks, restarting
15:56:36 ✅ cc-burner: restart complete
15:56:38 ⚠️ cc-burner: CPU 101% > 50% (1/2 checks)
15:56:40 ⛔ cc-burner: CPU 100% > 50%, but already restarted 2x in 10 min.
Restarting isn't fixing it; needs a human.Key concepts & stack
Pure restart policy
policy.py takes usage, state and time and returns a decision. Cooldown, window expiry and give-up are tested with a fake clock: no Docker, no sleeping.
Warn-only by default
The monitor never restarts anything unless you pass --restart (or set CRAFT_AUTO_RESTART=true for the API).
Read-only API until keyed
The REST API refuses changes until CRAFT_API_KEY is set; once set, every endpoint needs it.
Parallel sampling
Stats for every running container are sampled at once with a thread pool, so one slow container doesn't stall the loop.
Label scoping
--label app=api limits the monitor to the containers you opt in.
Tested against a real daemon
Unit tests use a fake Docker client; integration tests start real containers when a daemon is available.
Install and run
Needs a running Docker daemon: Docker Desktop, OrbStack, or Docker Engine on Linux.
CLI
- 1Clone and install:
git clone https://github.com/akshatgcodes/ContainerCraft cd ContainerCraft && uv sync - 2List containers with live CPU and memory:
uv run python craft.py list --stats - 3Watch them, restarting when stuck:
uv run python craft.py monitor --threshold 80 --restart
REST API
- 1Start it with a key (docs at
/docs):CRAFT_API_KEY=change-me \ uv run uvicorn containercraft.api:app - 2Call it:
curl -H "X-API-Key: change-me" \ localhost:8000/containers