Diagnosing Docker Container High CPU Usage on Linux and Windows
Learn to diagnose and stop a single Docker container from consuming excessive CPU on Linux and Windows hosts, using process-level inspection and resource limits.
Rao Aadil, India 5 min read
Confirm High CPU Usage at the Host Level
Start at the host: is it actually under CPU pressure? On Linux, run top or htop. On Windows, open Task Manager with Ctrl+Shift+Esc and check the CPU column, or use PowerShell:
Get-Counter '\Processor(_Total)\% Processor Time'
A misbehaving container on Linux shows up as a process such as dockerd or the container's main process consuming CPU, because containers use cgroups. Docker Desktop on Windows runs containers in a VM (WSL2 or Hyper-V), so you might see vmmem or Docker Desktop.exe spike instead. That's expected for that architecture.
For a per-container breakdown, docker stats works on both Linux and Windows hosts:
docker stats --no-stream
Look for a container with a high CPU% value. --no-stream prints one snapshot instead of a live updating stream.
Inspect containers, read logs, check stats, restart services and run Compose stacks, just by describing what you want.
“why does this container keep restarting”
It reads the real state of the server before it says anything, shows you the exact command, and waits for your approval. Windows and Linux, over the SSH access you already have.
Identify the Container and Process Responsible
By default, docker stats output is not sorted by CPU. On Linux, pipe it to sort after stripping the percent sign:
docker stats --no-stream --format "{{.CPUPerc}} {{.Name}}" | sed 's/%//' | sort -nr
On Windows PowerShell, the equivalent is:
docker stats --no-stream --format "{{.CPUPerc}} {{.Name}}" | ForEach-Object {
$cpu, $name = $_ -split ' ', 2
[PSCustomObject]@{ CPU = [double]$cpu.Trim('%'); Name = $name }
} | Sort-Object CPU -Descending | Format-Table
You get the container name and CPU percentage in descending order.
With the suspect container identified, inspect the processes inside it. For Linux containers, run either:
docker top <container>
or
docker exec <container> ps aux
docker top shows host PIDs, which helps with mapping to the host later. For Windows containers, run:
docker exec <container> cmd /c tasklist
or
docker exec <container> powershell Get-Process
These commands assume the container OS matches. If you're running a Linux container on Docker Desktop with WSL2, use the Linux commands, not Windows commands.
To map a container process to a host process on Linux, first get the container's main process host PID:
docker inspect --format '{{.State.Pid}}' <container>
Then use ps -p <pid> -o pid,ppid,%cpu,%mem,cmd on the host to see that process. For child processes, docker top already lists host PIDs. On Windows with Hyper-V isolation, mapping is less direct; the container runs in a separate VM. For WSL2, use wsl --list --verbose to find the distribution name, then wsl -d <distro> top to view processes inside the WSL2 VM.
An AI agent for Docker operations can accelerate this step: ask it 'which container is eating all the memory' or 'why does this container keep restarting' to get a breakdown of resource usage and logs without manually running each command.
Immediate Mitigation: Stop the CPU Spike
When the container is causing an outage, restart it to stop the runaway process. The command is the same on Linux and Windows:
docker restart <container>
A restart terminates the current container and starts a new one. It causes brief downtime and loses any state not stored in a volume or external service. If the container must stay stopped while you investigate, use:
docker stop <container>
docker pause <container> pauses processes without terminating them on Linux, but it is not supported for Windows containers.
You can apply a CPU limit to a running container without a restart. This works on Linux containers and on Windows containers with Hyper-V isolation (Docker Engine 19.03 or newer):
docker update --cpus 2 <container>
That limits the container to two CPU cores. For Docker Desktop with WSL2 running Linux containers, the limit applies to the Linux VM's CPU allocation to that container. docker update takes effect immediately without a restart.
Find and Fix the Root Cause Inside the Container
Stopping the spike is only the first step. Check recent logs for errors or abnormal activity:
docker logs --tail 200 <container>
Logs work on both Linux and Windows hosts. If they don't show enough, run diagnostics inside the container. For Linux containers:
docker exec -it <container> top
docker exec <container> ps aux
If the container has strace or perf installed, attach to a specific process ID from inside:
docker exec <container> sh -c 'strace -p <pid>'
docker exec <container> perf top
For Windows containers, use PowerShell performance counters:
docker exec <container> powershell Get-Counter '\Process(*)\% Processor Time'
For .NET applications, dotnet-trace can collect a trace from inside the container if it's installed.
Common culprits include infinite loops, garbage collection thrashing in Java or .NET applications, busy-waiting on locks or I/O, misconfigured thread pools, and runaway background jobs. If the high CPU comes from application code, fix the bug or optimize the algorithm. If it comes from configuration, adjust environment variables or configuration files to reduce worker count or thread pool size. Automated diagnostics can summarize findings, but code fixes need human judgment.
Prevent Future High CPU Occurrences
Set resource limits before a container starts. In a Docker Compose file (Compose v2), add a cpus key under the service:
services:
myapp:
image: myapp:latest
cpus: 2
Swarm mode uses deploy.resources.limits.cpus instead:
services:
myapp:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
When running a container manually, use --cpus to cap CPU and --cpu-shares to set a relative weight (not a hard limit) among containers:
docker run --cpus 2 --cpu-shares 512 myimage
Both flags work on Linux containers and Windows containers with Hyper-V isolation.
Health checks catch unhealthy containers early. In a Dockerfile:
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/ || exit 1
Or in Compose:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 3s
Continuous monitoring closes the loop. cAdvisor and Prometheus can scrape Docker stats and alert on sustained high CPU. Docker Desktop includes built-in resource usage graphs for containers on Windows and macOS.