Topic: mcp server container security

MCP server container security — running MCP servers in Docker without giving them your host

Containerizing an MCP server is a sound security practice: it limits the server's access to your host filesystem, isolates its network, and gives you a reproducible environment. But containerization only provides isolation if you don't undo it with the configuration options that Docker provides to override those defaults. The five most common MCP server container misconfigurations in the SkillAudit corpus all involve explicitly opting out of Docker's default isolation. Understanding what each option removes is the first step to avoiding it.

Why container isolation matters for MCP servers specifically

MCP servers are compelling containerization targets for a reason that goes beyond general container security hygiene: they execute arbitrary code on behalf of an LLM. If a prompt-injection attack causes an MCP server to run unintended operations, the blast radius is limited by whatever the container process can access. A properly sandboxed container limits the attacker to the container's filesystem, network, and resource budget — not your entire laptop or cloud VM.

This means the security value of containerization scales directly with how tightly you've constrained the container. A container running as root with a Docker socket mount provides nearly zero isolation.

Misconfiguration 1: running as root

Docker containers run as root by default unless instructed otherwise. If the container process is compromised, root access inside the container maps to potential root access outside it — depending on how the container is configured and which kernel version is in use, container-to-host privilege escalation is a documented attack class.

# Vulnerable: no USER directive, runs as root
FROM node:20-slim
WORKDIR /app
COPY . .
RUN npm ci
CMD ["node", "server.js"]
# Fixed: explicit non-root user
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN addgroup --system mcpuser && adduser --system --ingroup mcpuser mcpuser
USER mcpuser
CMD ["node", "server.js"]

Misconfiguration 2: privileged mode

--privileged in a Docker run command (or privileged: true in a docker-compose.yml) disables nearly all container isolation. The container gains access to all devices on the host, can modify kernel parameters, and can mount host filesystems. SkillAudit flags any Dockerfile or docker-compose.yml in the repo that uses privileged mode as a HIGH finding.

Privileged mode is occasionally needed for specific development tooling (Docker-in-Docker, certain debuggers) but should never appear in an MCP server's production deployment configuration. If you need it for local development, gate it behind a DEV=1 override that's not in the committed configuration.

Misconfiguration 3: Docker socket mount

Mounting the Docker socket (-v /var/run/docker.sock:/var/run/docker.sock) inside an MCP server container is equivalent to giving the container root access to the host. Any process inside the container can spawn new containers with arbitrary volume mounts, including the host filesystem at /. This is documented as a full container escape.

SkillAudit flags Docker socket mounts in docker-compose.yml and in CMD/ENTRYPOINT patterns that reference docker.sock. No MCP server has a legitimate reason to mount the Docker socket unless it is specifically a Docker management server — in which case the security review scope is different.

Misconfiguration 4: unrestricted volume mounts

Mounting your entire home directory or entire project root into an MCP server container removes the filesystem isolation that containerization is supposed to provide. A prompt-injection attack that causes the server to read or write files can now reach anything the host user can access.

The safe pattern is to mount only the specific directory the MCP server needs, read-only where possible:

# Dangerous: mounts entire home directory
docker run -v ~/.:/home/user mcp-server
# Safe: mounts only the workspace, read-only
docker run -v ~/workspace/project:/app/workspace:ro mcp-server

Misconfiguration 5: no resource limits

An MCP server without CPU and memory limits can consume unbounded resources if an LLM instructs it to perform a resource-intensive operation — or if a prompt-injection causes it to enter a loop. In a shared team environment, one rogue session can degrade all other users.

# Safe: resource limits in docker-compose.yml
services:
  mcp-server:
    image: myorg/mcp-server:latest
    user: "1001:1001"
    read_only: true
    tmpfs:
      - /tmp:size=50m
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
    security_opt:
      - no-new-privileges:true

The minimal safe MCP server Dockerfile

Combining the above fixes, a production-ready MCP server container should look like:

FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20-slim
WORKDIR /app

# Non-root user
RUN addgroup --system --gid 1001 mcpuser \
  && adduser --system --uid 1001 --ingroup mcpuser mcpuser

COPY --from=build /app/node_modules ./node_modules
COPY --chown=mcpuser:mcpuser . .

USER mcpuser

# No writable filesystem at runtime (tmpfs handles temp writes)
HEALTHCHECK --interval=30s --timeout=3s \
  CMD node -e "require('./healthcheck')" || exit 1

CMD ["node", "--max-old-space-size=128", "server.js"]

The --max-old-space-size=128 flag on the Node process adds a second layer of memory limiting inside the container (in addition to Docker's external limit). It won't prevent all resource exhaustion attacks, but it adds an early-warning trip wire at the application layer.

What SkillAudit checks in container configurations

SkillAudit scans Dockerfiles, docker-compose.yml, and .github/workflows/ deployment configs for container security patterns. The Security axis checks for:

Container security checks are part of the same Security axis scan that checks source code. Authors who have both code vulnerabilities and container misconfigurations typically see compound findings — the container findings are listed alongside the code findings in the same report section.

For the network-level isolation controls that complement container security, see the MCP server network security page. For the access control layer that limits what operations a containerized server can perform even if compromised, see MCP server access control.

Scan your container configuration

SkillAudit checks Dockerfiles and docker-compose.yml alongside source code. Paste your GitHub URL to see the full breakdown.

Run a free audit