459 lines
11 KiB
Markdown
459 lines
11 KiB
Markdown
# Docker Isolation - Complete Guide
|
|
|
|
**When to use this reference:** Docker isolation is enabled and servers are failing, or you see "not a TTY" errors.
|
|
|
|
This reference explains how MCPProxy's Docker isolation feature works and how to debug issues with it.
|
|
|
|
## What is Docker Isolation?
|
|
|
|
MCPProxy can run stdio MCP servers inside Docker containers for security isolation. This prevents untrusted servers from accessing your filesystem or network.
|
|
|
|
**Key concept:** MCPProxy wraps the server command in `docker run`, manages the container lifecycle, and handles stdio communication between Claude Code and the containerized server.
|
|
|
|
## How It Works
|
|
|
|
### Normal Flow (No Isolation)
|
|
|
|
```bash
|
|
# User configures:
|
|
command: "uvx"
|
|
args: ["mcp-server-sqlite", "--db-path", "/path/to/db"]
|
|
|
|
# MCPProxy runs directly:
|
|
uvx mcp-server-sqlite --db-path /path/to/db
|
|
```
|
|
|
|
### With Docker Isolation Enabled
|
|
|
|
```bash
|
|
# User configures same as above
|
|
# MCPProxy detects runtime (uvx → Python) and wraps in Docker:
|
|
|
|
docker run -i --rm \
|
|
--memory="512m" \
|
|
--cpus="1.0" \
|
|
--network=bridge \
|
|
python:3.11 \
|
|
uvx mcp-server-sqlite --db-path /path/to/db
|
|
```
|
|
|
|
**MCPProxy automatically:**
|
|
1. Detects the runtime from the command (uvx→Python, npx→Node.js)
|
|
2. Selects appropriate Docker image
|
|
3. Wraps command in `docker run` with resource limits
|
|
4. Tracks container lifecycle
|
|
5. Cleans up container on exit
|
|
|
|
## Configuration
|
|
|
|
### Global Docker Isolation Settings
|
|
|
|
Located in `~/.mcpproxy/mcp_config.json`:
|
|
|
|
```json
|
|
{
|
|
"docker_isolation": {
|
|
"enabled": true, // Master switch
|
|
"memory_limit": "512m", // Per-container memory limit
|
|
"cpu_limit": "1.0", // CPU cores (1.0 = 1 core)
|
|
"timeout": "30s", // Container startup timeout
|
|
"network_mode": "bridge", // Docker network mode
|
|
"default_images": {
|
|
"uvx": "python:3.11", // Image for uvx commands
|
|
"npx": "node:20", // Image for npx commands
|
|
"python": "python:3.11", // Image for python commands
|
|
"node": "node:20" // Image for node commands
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Per-Server Isolation Override
|
|
|
|
You can override isolation settings for specific servers:
|
|
|
|
```json
|
|
{
|
|
"name": "my-server",
|
|
"command": "uvx",
|
|
"args": ["mcp-server-name"],
|
|
"isolation": {
|
|
"enabled": false, // Disable isolation for this server
|
|
// OR customize settings:
|
|
"enabled": true,
|
|
"image": "python:3.12-slim", // Custom image
|
|
"memory_limit": "1g", // More memory
|
|
"cpu_limit": "2.0", // More CPU
|
|
"network_mode": "none", // No network access
|
|
"working_dir": "/workspace" // Working directory in container
|
|
}
|
|
}
|
|
```
|
|
|
|
## Checking Docker Isolation Status
|
|
|
|
### Is Isolation Enabled for a Server?
|
|
|
|
```bash
|
|
# Check server log for isolation setup
|
|
grep -i "docker isolation enabled\|docker isolation setup" \
|
|
~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
|
|
|
# Look for:
|
|
# "Docker isolation enabled for server: SERVER_NAME"
|
|
# "Docker isolation setup successful"
|
|
```
|
|
|
|
### View the Docker Command Being Used
|
|
|
|
```bash
|
|
# See actual docker run command
|
|
grep "docker_run_args" ~/Library/Logs/mcpproxy/main.log | tail -3
|
|
|
|
# Example output:
|
|
# docker_run_args: ["run", "-i", "--rm", "--memory=512m", "--cpus=1.0",
|
|
# "--network=bridge", "python:3.11", "uvx", "mcp-server-name"]
|
|
```
|
|
|
|
### List Running Containers
|
|
|
|
```bash
|
|
# List all MCPProxy containers
|
|
docker ps | grep mcpproxy
|
|
|
|
# Check specific server container
|
|
docker ps | grep mcpproxy-SERVER_NAME
|
|
|
|
# Check container health
|
|
docker inspect --format='{{.State.Status}}' CONTAINER_ID
|
|
```
|
|
|
|
### View Container Logs
|
|
|
|
```bash
|
|
# If container is running
|
|
docker logs CONTAINER_NAME
|
|
|
|
# If container exited, find it in history
|
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
|
docker logs CONTAINER_ID
|
|
```
|
|
|
|
## Common Docker Isolation Issues
|
|
|
|
### Issue 1: Docker-in-Docker (Most Common)
|
|
|
|
**Symptom:** "the input device is not a TTY" error for Docker-based servers
|
|
|
|
**Root cause:** MCPProxy's Docker isolation wraps a Docker command in Docker, creating nested containers.
|
|
|
|
```json
|
|
// Server config uses Docker directly
|
|
{
|
|
"command": "docker",
|
|
"args": ["run", "-i", "--rm", "mcp-image:latest"]
|
|
}
|
|
|
|
// MCPProxy wraps it in another Docker (if isolation enabled):
|
|
docker run ... python:3.11 docker run -i --rm mcp-image:latest
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
This fails: Docker-in-Docker
|
|
```
|
|
|
|
**Solution:** Explicitly disable isolation for Docker-based servers:
|
|
|
|
```json
|
|
{
|
|
"name": "docker-server",
|
|
"command": "docker",
|
|
"args": ["run", "-i", "--rm", "mcp-image:latest"],
|
|
"isolation": {
|
|
"enabled": false // CRITICAL: Always disable for Docker commands
|
|
}
|
|
}
|
|
```
|
|
|
|
**Why explicit is better:** MCPProxy should auto-detect Docker commands and skip isolation, but explicit configuration is more reliable and prevents edge cases.
|
|
|
|
### Issue 2: File Access Problems
|
|
|
|
**Symptom:** Server can't access local files, permission errors
|
|
|
|
**Root cause:** Container can't access host filesystem by default.
|
|
|
|
**Solution 1: Disable isolation** (if file access is required):
|
|
```json
|
|
{
|
|
"isolation": {
|
|
"enabled": false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Solution 2: Mount volumes** (more secure):
|
|
```json
|
|
{
|
|
"isolation": {
|
|
"enabled": true,
|
|
"volumes": [
|
|
"/host/path:/container/path:ro" // Read-only mount
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
**Note:** Volume mounting requires MCPProxy support. Check documentation for current status.
|
|
|
|
### Issue 3: Network Access Blocked
|
|
|
|
**Symptom:** Server can't make API calls, network timeouts
|
|
|
|
**Root cause:** Container network mode blocks external access.
|
|
|
|
**Check current network mode:**
|
|
```bash
|
|
grep "network_mode" ~/.mcpproxy/mcp_config.json
|
|
```
|
|
|
|
**Solution:** Change network mode:
|
|
```json
|
|
{
|
|
"isolation": {
|
|
"enabled": true,
|
|
"network_mode": "bridge" // Default, allows external access
|
|
// OR
|
|
"network_mode": "host" // Full host network access
|
|
// OR
|
|
"network_mode": "none" // No network (most secure)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Issue 4: Container Image Not Found
|
|
|
|
**Symptom:** "context deadline exceeded", stderr shows "pull" or "not found"
|
|
|
|
**Investigation:**
|
|
```bash
|
|
# Check stderr for pull errors
|
|
grep -i "pull\|not found" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
|
|
|
# Try pulling image manually
|
|
docker pull python:3.11
|
|
```
|
|
|
|
**Solution:** Ensure Docker image exists locally or can be pulled:
|
|
```bash
|
|
# Pull image before starting mcpproxy
|
|
docker pull python:3.11
|
|
|
|
# Or specify a custom image that exists
|
|
{
|
|
"isolation": {
|
|
"image": "python:3.12-slim" // Different image
|
|
}
|
|
}
|
|
```
|
|
|
|
### Issue 5: Resource Limits Too Low
|
|
|
|
**Symptom:** Container exits unexpectedly, out of memory errors
|
|
|
|
**Investigation:**
|
|
```bash
|
|
# Check container exit status
|
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
|
|
|
# Look for OOMKilled status
|
|
docker inspect CONTAINER_ID | grep -i "oom"
|
|
```
|
|
|
|
**Solution:** Increase resource limits:
|
|
```json
|
|
{
|
|
"isolation": {
|
|
"enabled": true,
|
|
"memory_limit": "2g", // Increase from default 512m
|
|
"cpu_limit": "2.0" // Increase from default 1.0
|
|
}
|
|
}
|
|
```
|
|
|
|
## Docker Context Support (Colima)
|
|
|
|
MCPProxy uses your system's default Docker context. For Colima users:
|
|
|
|
```bash
|
|
# Check current Docker context
|
|
docker context show
|
|
|
|
# List all contexts
|
|
docker context ls
|
|
|
|
# Example output:
|
|
# NAME DESCRIPTION DOCKER ENDPOINT
|
|
# default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
|
|
# colima * colima unix:///Users/.../.colima/docker.sock
|
|
|
|
# MCPProxy uses whichever has the asterisk (*)
|
|
```
|
|
|
|
**To change context:**
|
|
```bash
|
|
docker context use colima
|
|
docker context use default
|
|
```
|
|
|
|
**After changing context:** Restart mcpproxy for changes to take effect.
|
|
|
|
## Disabling Docker Isolation
|
|
|
|
### Globally (All Servers)
|
|
|
|
Edit `~/.mcpproxy/mcp_config.json`:
|
|
|
|
```json
|
|
{
|
|
"docker_isolation": {
|
|
"enabled": false // Disables for all servers
|
|
}
|
|
}
|
|
```
|
|
|
|
### Per-Server (Recommended)
|
|
|
|
Only disable for servers that need it:
|
|
|
|
```json
|
|
{
|
|
"name": "special-server",
|
|
"command": "docker", // Or any command that needs no isolation
|
|
"args": ["..."],
|
|
"isolation": {
|
|
"enabled": false // Only this server runs without isolation
|
|
}
|
|
}
|
|
```
|
|
|
|
**Best practice:** Keep isolation enabled globally, disable per-server only when necessary.
|
|
|
|
## Debugging Workflow for Docker Isolation Issues
|
|
|
|
Use this systematic workflow:
|
|
|
|
### Step 1: Verify Isolation Status
|
|
```bash
|
|
# Is isolation enabled for this server?
|
|
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
|
```
|
|
|
|
### Step 2: Check Container Status
|
|
```bash
|
|
# Is container running?
|
|
docker ps | grep mcpproxy-SERVER_NAME
|
|
|
|
# Did container exit?
|
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
|
```
|
|
|
|
### Step 3: View Container Logs
|
|
```bash
|
|
# Get container ID
|
|
CONTAINER_ID=$(docker ps -a | grep mcpproxy-SERVER_NAME | awk '{print $1}')
|
|
|
|
# View logs
|
|
docker logs $CONTAINER_ID
|
|
```
|
|
|
|
### Step 4: Check Docker Command
|
|
```bash
|
|
# See exact command being used
|
|
grep "docker_run_args" ~/Library/Logs/mcpproxy/main.log | tail -3
|
|
```
|
|
|
|
### Step 5: Test Manually
|
|
```bash
|
|
# Extract docker run command from logs and test it manually
|
|
# Example:
|
|
docker run -i --rm --memory=512m --cpus=1.0 python:3.11 uvx mcp-server-name
|
|
```
|
|
|
|
### Step 6: Apply Fix
|
|
|
|
Based on the issue:
|
|
- Docker-in-Docker → Disable isolation
|
|
- File access → Disable isolation or mount volumes
|
|
- Network issues → Change network_mode
|
|
- Resource limits → Increase memory/CPU
|
|
- Image not found → Pull image or change image
|
|
|
|
### Step 7: Reload and Verify
|
|
```bash
|
|
# Trigger config reload
|
|
touch ~/.mcpproxy/mcp_config.json
|
|
|
|
# Or restart
|
|
pkill mcpproxy && open /Applications/mcpproxy.app
|
|
|
|
# Verify fix
|
|
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | \
|
|
python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
|
|
```
|
|
|
|
## When to Use Docker Isolation
|
|
|
|
### ✅ Use Docker Isolation When:
|
|
- Running untrusted third-party MCP servers
|
|
- Need security boundaries between servers
|
|
- Want resource limits per server
|
|
- Servers don't need host filesystem access
|
|
- Servers don't need special network configurations
|
|
|
|
### ❌ Disable Docker Isolation When:
|
|
- Server needs to access local files
|
|
- Server command is already Docker (prevents Docker-in-Docker)
|
|
- Server needs host network access
|
|
- Troubleshooting connection issues
|
|
- Performance is critical (isolation adds overhead)
|
|
|
|
## Quick Reference
|
|
|
|
### Check if isolation is active
|
|
```bash
|
|
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-NAME.log | tail -5
|
|
```
|
|
|
|
### List containers
|
|
```bash
|
|
docker ps | grep mcpproxy
|
|
```
|
|
|
|
### View container logs
|
|
```bash
|
|
docker logs CONTAINER_NAME
|
|
```
|
|
|
|
### Disable isolation for server
|
|
```json
|
|
{"isolation": {"enabled": false}}
|
|
```
|
|
|
|
### Increase resources
|
|
```json
|
|
{"isolation": {"memory_limit": "2g", "cpu_limit": "2.0"}}
|
|
```
|
|
|
|
### Change network mode
|
|
```json
|
|
{"isolation": {"network_mode": "host"}}
|
|
```
|
|
|
|
### Use custom image
|
|
```json
|
|
{"isolation": {"image": "python:3.12-slim"}}
|
|
```
|
|
|
|
## Summary
|
|
|
|
Docker isolation provides security but adds complexity. Start with isolation disabled while getting servers working, then enable it once everything is stable. Always disable isolation explicitly for Docker-based servers to prevent Docker-in-Docker issues.
|