The Silent Resource Leak
If you use Docker-based Model Context Protocol (MCP) servers with Claude Code, you might be accumulating dozens of "zombie" containers that never get cleaned up. This happens because of how Claude Code's process lifecycle interacts with Docker's design.
When you start a Claude Code session, it spins up fresh Docker containers for each configured MCP tool (like a database browser or filesystem tool). The problem occurs when the session ends. If you close the IDE or terminal, Claude Code exits by closing the standard input (stdin) pipe to the docker run process. A broken pipe kills the process, but that process never gets a chance to signal the Docker daemon to stop the container. The container is orphaned and keeps running indefinitely.
The common fix of adding the --rm flag to docker run doesn't solve this. That flag only removes a container after it stops. Since these containers never receive a stop command, they never get removed.
How To Check For Leaks
Run this command in your terminal to see if you have any leaked containers:
docker ps | grep mcp
The original poster found 66 containers running from old Claude Code sessions. Each container consumes memory and CPU resources.
The Recommended Fix: Use uvx
The solution is to avoid Docker for MCP servers when possible. Instead, use tools that run as normal child processes, which get properly cleaned up when the parent process (Claude Code) exits.
For Python-based MCP servers, use uvx from the uv project. uvx runs tools directly in isolated environments without container overhead, and more importantly, it allows for proper process lifecycle management.
Example claude_desktop_config.json change:
Instead of:
"mcpServers": {
"my-tool": {
"command": "docker",
"args": ["run", "--rm", "-i", "my-mcp-image"]
}
}
Use:
"mcpServers": {
"my-tool": {
"command": "uvx",
"args": ["mcp-my-tool"]
}
}
Cleaning Up Existing Leaks
First, stop all the orphaned containers:
docker stop $(docker ps -q --filter "name=mcp")
Then remove them:
docker rm $(docker ps -a -q --filter "name=mcp")
Be careful if you have other containers with "mcp" in their names that you want to keep. You can manually review the list from docker ps | grep mcp first.
When Docker Is Still Necessary
Some MCP servers might require Docker due to complex dependencies or system isolation needs. For these cases, consider:
- Using Docker Compose with explicit cleanup: Create a
docker-compose.ymland usedocker-compose downin a cleanup script. - Adding a health check and auto-remove: Configure containers to stop themselves after inactivity.
- Regular maintenance: Add a cron job to clean up old containers weekly.
Why This Matters Beyond Resources
Beyond wasting memory and CPU, these zombie containers can cause port conflicts (if servers bind to specific ports) and make your development environment unpredictable. They also represent a security concern—unattended services running that you might have forgotten about.
The core issue is that MCP tooling currently treats docker run like a regular subprocess, but Docker's architecture requires explicit lifecycle management. Until Anthropic adds better container lifecycle handling to Claude Code, the uvx approach is the most reliable fix.
Check your system today—you might be surprised at what's still running from last week's coding sessions.


