● CLI + REST APIDocker SDK for Python·GPLv3

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.

The X factor

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."

Preview

What it looks like

Real output against a container running an infinite loop: two restarts, then the monitor stops trying.

zsh
$ 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.
Under the hood

Key concepts & stack

Python 3Docker SDKargparseFastAPIUvicornThreadPoolExecutoruvpytestruff

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.

Run it

Install and run

Needs a running Docker daemon: Docker Desktop, OrbStack, or Docker Engine on Linux.

CLI

  1. 1
    Clone and install:
    git clone https://github.com/akshatgcodes/ContainerCraft
    cd ContainerCraft && uv sync
  2. 2
    List containers with live CPU and memory:
    uv run python craft.py list --stats
  3. 3
    Watch them, restarting when stuck:
    uv run python craft.py monitor --threshold 80 --restart

REST API

  1. 1
    Start it with a key (docs at /docs):
    CRAFT_API_KEY=change-me \
      uv run uvicorn containercraft.api:app
  2. 2
    Call it:
    curl -H "X-API-Key: change-me" \
      localhost:8000/containers