MCP Directory

Running an MCP Server in Docker: Config & Gotchas

Most MCP servers don't need Docker — here's when a container actually earns its keep, and the config that works the first time.

Hua·June 30, 2026·6 min read
Close-up of colorful patch cables connected to a network panel with labels, indoors.
Photo by cottonbro studio on Pexels

Running an MCP server in Docker is straightforward: your client launches docker run instead of npx or python, and the container talks to the client over stdio just like any local process. The catch is that most servers don't need a container at all, and the ones that do have a few sharp edges — stdin handling, secrets, and mounted paths — that break silently if you get them wrong.

This guide covers when to containerise, the minimal config that works, and Docker's own MCP catalog. Roughly 90% of MCP servers run locally over stdio, so Docker is an optimization for a specific set of problems, not the default. If you're new to the protocol, start with what an MCP server is and come back.

When to run an MCP server in Docker (and when to skip it)

Containerise when the server has messy dependencies, needs isolation, or runs untrusted code — otherwise plain stdio is simpler and faster. A Node server you install with npx starts in under a second; wrapping it in Docker adds image pulls, a daemon dependency, and a longer cold start for no benefit.

Here's the honest breakdown:

SituationDocker?Why
Simple Node/Python server (most cases)Skip itnpx/uvx is faster and has fewer moving parts
Server needs system libs or a specific runtimeYesPin the whole environment in the image
Running community/untrusted codeYesFilesystem and network isolation limits blast radius
Team wants one reproducible setupYesSame image everywhere, no "works on my machine"
Server holds secrets you don't want on diskMaybeEnv injection is cleaner, but so is a secrets manager

The isolation point is the strongest argument. An MCP server runs with your user's permissions by default, so a buggy or malicious one can read anything you can. A container narrows that down to explicitly mounted paths. That trade-off is covered in depth in what actually matters for MCP security.

The minimal Docker config that works

Point your client at docker as the command and pass run -i --rm plus the image. The -i (interactive) flag is the one people forget, and without it the server can't read from stdin — the MCP transport just hangs.

{
  "mcpServers": {
    "github": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "ghcr.io/github/github-mcp-server"],
      "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..." }
    }
  }
}

Three flags carry the weight. -i keeps stdin open so the JSON-RPC messages flow; --rm deletes the container on exit so you don't leak dead containers every time the client restarts; the image reference should be pinned to a tag or digest, not latest, so an upstream push can't change your tools underneath you.

Don't add -t (TTY). It's the reflex from running containers by hand, but a TTY mangles the stdio stream and breaks the protocol. If your config uses the config generator, it emits the correct flags for you.

Passing secrets and files without leaking them

Inject secrets as environment variables and mount only the directories the server truly needs — never bake a token into the image. The env block in the config above is read by your client and passed to the container at launch, so the token never lands in an image layer or a shell history file.

For file access, be surgical with -v:

{
  "command": "docker",
  "args": [
    "run", "-i", "--rm",
    "-v", "/Users/me/projects:/workspace",
    "mcp/filesystem", "/workspace"
  ]
}

That mounts one project tree read-write into the container, and the server's allowed directory is /workspace — nothing above it is visible. This is the containerised version of the Filesystem reference server, which already restricts writes to allowed directories; the mount adds a second wall. Mount as read-only with :ro when the server only needs to read.

Two failure modes to expect. Paths inside the container are the container's paths, not your host's, so pass /workspace, not /Users/me/projects, as the server's argument. And on macOS or Windows, Docker Desktop must have the parent folder shared in its file-sharing settings or the mount silently comes up empty.

Docker's own MCP catalog and Gateway

Docker maintains an MCP Catalog and a Gateway that let you run vetted servers as containers behind a single endpoint. Instead of hand-writing a docker run block per server, you enable servers in Docker Desktop's MCP Toolkit and point one client entry at the Gateway, which multiplexes them.

The appeal is management, not raw capability. The Gateway handles credentials, per-server isolation, and gives you one place to see what's connected — useful once you're running more than three or four servers. The catalog images are pre-built and signed, so you skip the "is this npm package safe" question for anything listed there.

The GitHub MCP Server is a good example: it ships as an official Docker image and also as a hosted remote, so you can run it locally in a container for full control or point at GitHub's endpoint to skip infra entirely. Not everything is in the catalog, though — a doc-injection server like Context7 is typically wired in directly. For a broader shortlist of what's worth installing, see the best MCP servers roundup.

Remote MCP over HTTP instead of a local container

If the goal is running the server somewhere other than your laptop, a remote HTTP server often beats shipping a container to each developer. Docker gets a local server off your machine's dependency list, but it still runs on your machine. A remote server over streamable HTTP runs once, centrally, and every client just connects.

The trade-off is real: remote means auth, network latency, and a service you now operate. Local stdio (containerised or not) means zero network setup but no sharing. Most teams end up with a mix — local stdio for personal tools, remote HTTP for shared ones. The local vs remote MCP breakdown walks through picking per server.

One budget note that applies regardless of transport: coding clients cap out around 40 active tools before the model starts picking the wrong one. A container doesn't change that math — every server you add, Dockerised or not, spends from the same budget. Enable servers deliberately.

The bottom line

Use Docker for MCP servers that carry heavy dependencies, run untrusted code, or need to be identical across a team. For everything else, npx or uvx over stdio is the faster path and one fewer daemon to keep running. Get -i --rm right, pin your image, mount narrowly, and the container disappears into the background where it belongs. If a Dockerised server won't connect, the troubleshooting guide covers the usual suspects, and adding an MCP server has the client-by-client steps.

FAQ

Is running an MCP server in Docker free?

Yes. Docker Engine and Docker Desktop for personal use are free, and the MCP images in Docker's catalog are free to pull. The only cost is the extra startup time and memory a container uses versus running the server directly with npx or uvx.

Do I need Docker to run an MCP server?

No. Around 90% of MCP servers run locally over stdio launched with npx, uvx, or python — Docker is optional. Reach for a container only when you need dependency isolation, want to sandbox untrusted code, or need one reproducible setup across a team.

Why does my Dockerised MCP server hang or fail to connect?

The most common cause is a missing -i flag: without interactive mode the container can't read stdin and the MCP transport stalls. Also check that you did not pass -t (a TTY corrupts the stdio stream) and that any mounted folder is shared in Docker Desktop's file-sharing settings.

How do I pass an API token to a containerised MCP server?

Put it in the env block of your client's MCP config so it's injected as an environment variable at launch — never bake a token into the image, where it would persist in a layer. Mount only the directories the server needs with -v, and use :ro for read-only access.

Should I use Docker or a remote HTTP server?

Use Docker when you want the server isolated but still running on your own machine with no auth or network to manage. Use a remote HTTP server when a team needs to share one instance — it runs centrally, but you take on auth, latency, and operating a service.

Put this into practice

Browse MCP servers by capability, or check your own setup's tool budget and security.

More essays