open-context open-context
August 17, 2026 · 6 min read

Running an MCP Server in Docker: A Practical Guide

You can run open-context's MCP server in Docker with one command: docker run -i --rm -v opencontext-data:/root/.opencontext adityakarnam/open-context:latest node dist/mcp/index.js. That single image also serves the web UI and REST API — the difference is just which command you pass at the end, and a named volume that keeps your saved context across restarts and upgrades.

Key takeaways

  • open-context ships one Docker image (built on node:25-slim) that bundles the React UI, REST API, and MCP server together.
  • The default CMD starts the HTTP server on port 3000; override it with node dist/mcp/index.js to run the same image in MCP stdio mode instead.
  • Mount a named volume at /root/.opencontext — that's where contexts.json, preferences, and generated memory docs live, so they survive image upgrades.
  • On Linux, host.docker.internal isn't resolved automatically — add --add-host=host.docker.internal:host-gateway if the container needs to reach a host-side Ollama instance.
  • Connecting Claude Desktop or Claude Code just means pointing their mcpServers config at a docker run command — no local Node.js install required.

Why run an MCP server in a container at all

MCP servers are just local processes that speak a specific protocol over stdio or HTTP, so nothing forces you to containerize one. But Docker has been standardizing around MCP distribution — Docker's own MCP Catalog and Toolkit now ships a registry of MCP servers as container images under the mcp/ namespace on Docker Hub specifically so they can be pulled and run without a local language runtime. The same logic applies to open-context's MCP server: a container gives you a pinned Node.js version, no global npm install, and a clean way to isolate the process's filesystem access to one mounted volume.

How open-context packages its MCP server

The official image (adityakarnam/open-context:latest) is built in three stages: the React UI is compiled first, the TypeScript CLI/server/MCP code is compiled second, and both outputs are copied into a slim production stage with only production dependencies installed. The result is one image that can run three different ways depending on the command you pass it:

Both modes read and write the same /root/.opencontext/ directory, which is why mounting a persistent volume matters even if you only ever use MCP stdio mode.

Step-by-step: run the MCP server in Docker

1. Pull the image

No clone or build required — the image is published to Docker Hub:

docker pull adityakarnam/open-context:latest

2. Run it in MCP stdio mode

Override the default command and add -i so Docker keeps stdin open for the stdio transport:

docker run -i --rm \
  -v opencontext-data:/root/.opencontext \
  adityakarnam/open-context:latest \
  node dist/mcp/index.js

--rm is safe here because nothing important lives in the container itself — all state is in the opencontext-data volume, which Docker keeps around after the container exits.

3. Confirm what's in the volume

The mounted volume is the only place open-context writes to:

File in /root/.opencontext/Contents
contexts.jsonMCP context entries Claude has saved via save_context
preferences.jsonStructured preferences from the web UI form
preferences.mdGenerated Claude preferences doc
memory.mdGenerated Claude memory doc

Connect Claude Desktop or Claude Code

Both clients read an mcpServers block from a config file and launch the command you give them — pointing that command at docker run means Claude starts and stops the container for you on each session. Add this to ~/.claude/settings.json (Claude Code) or claude_desktop_config.json (Claude Desktop, under ~/Library/Application Support/Claude/ on macOS):

{
  "mcpServers": {
    "open-context": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "-v", "opencontext-data:/root/.opencontext",
               "adityakarnam/open-context:latest", "node", "dist/mcp/index.js"]
    }
  }
}

Restart the client after saving. Claude now has save_context, recall_context, list_contexts, search_contexts, update_context, and delete_context tools backed by the same volume every time it starts the container — see how MCP persistent memory works for what each tool does in practice.

Environment variables worth setting

VariableDefaultWhen to change it
OPENCONTEXT_STORE_PATH/root/.opencontext/contexts.jsonRarely — only if you're mounting the volume somewhere other than the default path
OLLAMA_HOSThttp://host.docker.internal:11434If Ollama runs on a different machine, e.g. a GPU box on your LAN
PORT3000Only relevant in HTTP server mode, not MCP stdio mode

Common Docker + MCP problems

Container can't reach Ollama on the host. host.docker.internal resolves automatically on Docker Desktop for Mac and Windows, but not on Linux. Add --add-host=host.docker.internal:host-gateway to your docker run command, or set OLLAMA_HOST directly to your host's LAN IP.

Container exits immediately with ERR_MODULE_NOT_FOUND. This was caused by missing .js extensions in older ESM builds. Pull the latest tag and it's resolved: docker pull adityakarnam/open-context:latest.

Claude doesn't see the MCP tools after editing the config. Both Claude Desktop and Claude Code only read mcpServers config on startup — fully quit and reopen the client rather than just closing the window.

Give Claude persistent memory with one Docker command.

Get started with open-context →

FAQ

Do I need to build the Docker image myself?

No. adityakarnam/open-context:latest is published on Docker Hub, so docker pull adityakarnam/open-context:latest gets you a working image without cloning the repo or running a build.

How do I switch the same image between the web UI and MCP stdio mode?

The image's default command runs the HTTP server (node dist/server.js) on port 3000. Override the command with node dist/mcp/index.js and pass -i to docker run to get MCP stdio mode instead — no separate image is needed.

Why can't my containerized MCP server reach Ollama on the host?

On Docker Desktop (Mac/Windows), host.docker.internal resolves automatically. On Linux it doesn't by default — add --add-host=host.docker.internal:host-gateway to your docker run command, or set OLLAMA_HOST to your host machine's LAN IP.

Will I lose my saved contexts if I update the image?

No, as long as you mount a named volume at /root/.opencontext. The contexts.json file, preferences, and generated memory docs live in that volume, not in the container's writable layer, so pulling a new image tag and restarting the container preserves them.

Does the MCP server need internet access to run in Docker?

No. The MCP server itself only reads and writes its local JSON store. The container makes no outbound calls unless you separately enable Ollama-based preference analysis, which is optional and stays on your own network.