Docker Resource Diagnostics and Cleanup
Use this workflow when Docker builds, worktrees, agents, dev stacks, or services have been running for a while and the machine needs a resource check. It is useful on small EC2 or VPS servers with limited disk and RAM, and also on local Linux development machines where Docker cache and unused containers can grow over time.
When to use it
Section titled “When to use it”- Before moving a service such as MySQL from managed hosting to local Docker.
- After many Docker builds, worktrees, agents, or dev stacks.
- When an EC2 or VPS machine feels slow or has a small root disk.
- When local Docker development starts consuming unexpected disk or CPU.
- When htop and Docker show high CPU but a whole-system monitor shows a lower total percentage.
Quick reference
Section titled “Quick reference”# memory and diskfree -hdf -h
# live Docker usagedocker stats --no-streamdocker system df
# find large root, var, Docker, and containerd pathssudo du -x -h --max-depth=1 / | sort -hsudo du -h --max-depth=1 /var | sort -hsudo du -h --max-depth=1 /var/lib | sort -hsudo du -h --max-depth=1 /var/lib/docker | sort -hsudo du -h --max-depth=1 /var/lib/containerd | sort -h
# conservative Docker cleanupdocker builder prunedocker container prunedocker image prunedocker system prune
# re-checkdf -hdocker system dfCheck memory and disk
Section titled “Check memory and disk”For memory, the important value is available. Linux uses unused memory for buff/cache, which is usually normal. On a small server, available memory comfortably above 1 GB is a good sign.
free -hCheck mounted filesystems next. On small EC2 or VPS disks, disk can become the bottleneck before RAM. Keeping the root filesystem below about 70 to 80 percent leaves room for logs, Docker layers, package cache, and temporary files.
df -hCheck Docker usage
Section titled “Check Docker usage”docker stats shows live CPU and memory usage per container. Docker CPU percentage is per logical CPU core, not always the percentage of the whole machine.
| Docker CPU | Meaning |
|---|---|
| 50% | Half of one logical CPU core |
| 100% | One full logical CPU core |
| 200% | Two full logical CPU cores |
| 100% on 16 threads | About 1/16 of total CPU capacity |
docker stats --no-streamdocker system df summarizes images, containers, volumes, and build cache. SIZE is current usage. RECLAIMABLE is what Docker thinks can be removed. Build cache is often safe to clean. Volumes can contain databases and should be treated carefully.
docker system dfFind disk growth
Section titled “Find disk growth”Start from the root filesystem, then inspect /var and /var/lib. Use -x when scanning / so du stays on the same filesystem and avoids mounted pseudo-filesystems. Some /proc or /run warnings are normal if / is scanned without -x.
sudo du -x -h --max-depth=1 / | sort -h
sudo du -h --max-depth=1 /var | sort -hsudo du -h --max-depth=1 /var/lib | sort -h- /var/lib/docker contains Docker images, containers, overlay layers, volumes, and build-related data.
- /var/lib/containerd can also grow, especially from downloaded image blobs and unpacked filesystem layers.
- /var/log, /var/cache, /var/lib/mysql, /var/lib/postgresql, and /var/lib/snapd can also be large depending on the machine.
sudo du -h --max-depth=1 /var/lib/docker | sort -hsudo du -h --max-depth=1 /var/lib/containerd | sort -h| containerd path | What it usually holds |
|---|---|
| io.containerd.content.v1.content | Downloaded image blobs and layers |
| io.containerd.snapshotter.v1.overlayfs | Unpacked container and image filesystem layers |
Clean Docker safely
Section titled “Clean Docker safely”Start with the safest cleanup. Build cache is usually the first thing to remove because it does not delete running containers, active images, Docker volumes, databases, env files, or source code. The tradeoff is that the next Docker build may be slower.
docker builder prune
# non-interactivedocker builder prune -f| Command | What it removes |
|---|---|
| docker container prune | Stopped containers only |
| docker image prune | Dangling images only |
| docker system prune | Stopped containers, unused networks, dangling images, and build cache |
| docker image prune -a | All unused images, not images used by running containers |
docker container prunedocker image prunedocker system prune
# more aggressivedocker image prune -aAvoid volume cleanup by default. Unused Docker volumes may contain old local databases, Qdrant data, Redis data, or experiment state.
# Do not run this unless the volumes are definitely disposable.# docker system prune -a --volumesRe-check after cleanup
Section titled “Re-check after cleanup”df -hdocker system dfsudo du -h --max-depth=1 /var/lib/containerd | sort -hsudo du -h --max-depth=1 /var/lib/docker | sort -hRead CPU correctly
Section titled “Read CPU correctly”htop process CPU is usually per logical CPU core. Whole-system monitors such as Mission Center usually show total CPU across all CPU threads. Both views can be correct at the same time.
| Process CPU | Meaning |
|---|---|
| 70% | 0.7 of one logical CPU |
| 100% | One full logical CPU |
| 200% | Two full logical CPUs |
| Two processes at 70% on 16 threads | About 1.4 logical CPUs, or about 8.75 percent of total CPU capacity |
# Ubuntu/Debiansudo apt install htop
# Archsudo pacman -S htop
htopMap CPU to a container
Section titled “Map CPU to a container”When htop shows a busy process, inspect the PID and check whether it belongs to a container. Replace PID_HERE with the process ID from htop.
ps -fp PID_HEREcat /proc/PID_HERE/cgroupfor c in $(docker ps --format '{{.Names}}')do echo "=== $c ===" docker top "$c" | grep -E 'PID_HERE' || truedoneThen check the suspected container logs.
docker logs --tail=100 CONTAINER_NAME
docker logs --tail=100 project-wt1-backenddocker logs --tail=100 project-wt2-backenddocker logs --tail=100 automation-serviceWatch reload CPU
Section titled “Watch reload CPU”uvicorn –reload can cause steady CPU usage because it watches files. Inside Docker this can be heavier if the watched directory includes mounted source code, .git, .venv, node_modules, generated files, cache folders, or large worktrees.
- Use –reload only for the backend you are actively editing.
- Run other worktree or backend containers without reload.
- Never use –reload in production.
docker inspect CONTAINER_NAME --format '{{.Config.Cmd}}'# production-style FastAPI commandgunicorn -k uvicorn.workers.UvicornWorker -w 2 -b 0.0.0.0:8000 main:app
# plain non-reload Uvicornuvicorn main:app --host 0.0.0.0 --port 8000
# dev reload with common exclusionsuvicorn main:app \ --host 0.0.0.0 \ --port 8000 \ --reload \ --reload-exclude .git \ --reload-exclude .venv \ --reload-exclude node_modules \ --reload-exclude __pycache__ \ --reload-exclude .pytest_cacheStop unused stacks
Section titled “Stop unused stacks”Stop containers that are not actively needed. For local development, this is often better than trying to optimize every idle worktree service.
docker stop CONTAINER_NAME
docker stop project-wt1-backend project-wt1-frontenddocker stop project-wt2-backend project-wt2-frontendOr stop a Compose stack from the relevant project directory.
docker compose downCheck Node service CPU
Section titled “Check Node service CPU”If htop shows a long-running node process, a workflow service or other Node-based container may be the active CPU user. A process at 100 percent means about one logical CPU core. On a 16-thread machine that is about 6.25 percent of total CPU capacity.
docker stats --no-stream | grep -i service-namedocker logs --tail=100 automation-service# if not neededdocker stop automation-service
# if it should be running but seems busydocker restart automation-servicedocker logs --tail=100 automation-service