From 2fca3a0bd591a75a7ef311b004f3d958f61c5819 Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:48:11 -0400 Subject: [PATCH 1/2] Document sessionAffinity in MCPServer scaling Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/toolhive/guides-k8s/run-mcp-k8s.mdx | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx index 8ff20df6..31d8edfc 100644 --- a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx +++ b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -639,6 +639,34 @@ backends. ::: +### Session affinity for proxy replicas + +Clients connect to the proxy runner through its Service. When you run more than +one proxy replica (`spec.replicas > 1`), the `spec.sessionAffinity` field +controls how that Service routes repeated connections from the same client: + +- `ClientIP` (default) - pins a client to the same proxy pod. Because MCP + transports (SSE and streamable HTTP) are stateful, this keeps a client on the + replica that holds its in-memory session. +- `None` - load-balances each connection freely across proxy pods. + +```yaml title="MCPServer resource" +spec: + replicas: 2 + # highlight-next-line + sessionAffinity: None # default is ClientIP +``` + +Affinity only influences routing; it does not move session state between pods. +`ClientIP` is also unreliable behind NAT or shared egress IPs, where many +clients share a source address. For reliable scaling, configure +[Redis session storage](./redis-session-storage.mdx#horizontal-scaling-session-storage) +so any proxy pod can serve any client rather than depending on affinity alone. + +This client-facing affinity is separate from the backend session routing +described next, which controls how the proxy reaches backend pods when +`spec.backendReplicas > 1`. + ### Session routing for backend replicas MCP connections are stateful: once a client establishes a session with a From eb1d2878a916dc69953c192f63f1c56b274c143a Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:53:17 -0400 Subject: [PATCH 2/2] Clarify source-IP basis of sessionAffinity Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/toolhive/guides-k8s/run-mcp-k8s.mdx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx index 31d8edfc..297f7c48 100644 --- a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx +++ b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -643,11 +643,12 @@ backends. Clients connect to the proxy runner through its Service. When you run more than one proxy replica (`spec.replicas > 1`), the `spec.sessionAffinity` field -controls how that Service routes repeated connections from the same client: +controls whether that Service routes repeated connections from the same source +IP to the same proxy pod: -- `ClientIP` (default) - pins a client to the same proxy pod. Because MCP - transports (SSE and streamable HTTP) are stateful, this keeps a client on the - replica that holds its in-memory session. +- `ClientIP` (default) - routes connections from the same source IP to the same + proxy pod. Because MCP transports (SSE and Streamable HTTP) are stateful, this + keeps a client's connections on the replica that holds its in-memory session. - `None` - load-balances each connection freely across proxy pods. ```yaml title="MCPServer resource"