Skip to content

Docker & containers

Pull the official image

docker pull ghcr.io/kartoza/kartoza-mcp:latest

Tags:

Tag Meaning
latest Most recent stable release.
vX.Y.Z Pin to a specific release.
vX.Y Pin to a minor train (gets patch updates).
edge HEAD of main. Not for production.

Arches: linux/amd64, linux/arm64. Built via docker buildx.

Run it

docker run --rm -i ghcr.io/kartoza/kartoza-mcp:latest < request.json

The image is FROM scratch + a single static binary. There is no shell, no apt, no /etc. It does one thing: read MCP frames on stdin, write them on stdout.

In Claude Desktop config

{
  "mcpServers": {
    "kartoza-mcp": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "ghcr.io/kartoza/kartoza-mcp:latest"]
    }
  }
}

In Kubernetes (as a sidecar)

MCP servers don't bind ports, so they live happily as sidecars in a pod with a bridge container:

apiVersion: v1
kind: Pod
metadata:
  name: mcp-bridge
spec:
  containers:
    - name: bridge
      image: ghcr.io/kartoza/mcp-bridge:latest
      # talks websocket out, stdio to sidecar in
    - name: kartoza-mcp
      image: ghcr.io/kartoza/kartoza-mcp:latest
      stdin: true
      tty: false

Build it yourself

# Dockerfile shipped in the repo
FROM scratch
COPY spatial-cluster-static /spatial-cluster
ENTRYPOINT ["/spatial-cluster"]
nix run .#build
docker buildx build --platform linux/amd64,linux/arm64 -t my/kartoza-mcp .

Image hardening

Because the image is FROM scratch:

  • No CVEs from a userland (there isn't one).
  • No shell to escalate into.
  • No package manager to call out to.

You still get the usual Kubernetes / Docker controls — read-only root FS, non-root user (set runAsUser: 65532), drop all capabilities.