Sidecar patterns
Run Spanly alongside your MCP server in docker-compose or Kubernetes.
The recommended deployment shape for the Spanly container is as a sidecar that proxies traffic to your MCP server. The MCP server stays internal, the sidecar takes the public port.
docker-compose
A typical setup: your MCP server container plus a Spanly sidecar in the same compose service group.
services:
mcp:
image: my-org/my-mcp:1.0.0
expose:
- '3000' # reachable from the sidecar only, not the host
environment:
MCP_PORT: 3000
spanly:
image: spanly/spanly:latest
command: ['proxy', 'mcp:3000', '0.0.0.0:3001']
ports:
- '3001:3001' # this is what your MCP client connects to
environment:
SPANLY_API_KEY: ${SPANLY_API_KEY}
depends_on:
- mcpPoint your MCP client at http://localhost:3001. The MCP server
itself is no longer exposed externally.
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-mcp
spec:
replicas: 1
selector:
matchLabels:
app: my-mcp
template:
metadata:
labels:
app: my-mcp
spec:
containers:
- name: mcp
image: my-org/my-mcp:1.0.0
ports:
- containerPort: 3000
- name: spanly
image: spanly/spanly:latest
args: ['proxy', 'localhost:3000', '0.0.0.0:3001']
ports:
- containerPort: 3001
env:
- name: SPANLY_API_KEY
valueFrom:
secretKeyRef:
name: spanly
key: api-keyThe Service exposes port 3001 (the Spanly proxy), not 3000.
Session grouping
If your MCP server runs sessionless, the sidecar assigns a synthetic
Mcp-Session-Id on initialize responses so Spanly can still group
requests into sessions. The ID is stripped before requests are
forwarded to your server. Add --inject-session-id=false to the
proxy arguments to turn this off. See
Session tracking.
A maintained Kustomize component is at
kustomize/spanly-sidecar.
For a standalone Pod + Service pattern, see the
Helm chart.