stacklok / stacklok/toolhive

[BUG] Improve MCPServer Scaling Coordination

Open
#4,484 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug go kubernetes
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Scale-to-Zero Leaves Orphaned Backend StatefulSet

Description

When the MCPServer Proxy Deployment is scaled to 0, the backend StatefulSet continues running, resulting in a zombie state with no proxy managing or routing traffic to it.

This happens because the Proxy (proxyrunner) is currently responsible for provisioning and managing the backend StatefulSet. When the Proxy is terminated, no component remains to reconcile or clean up the backend unless the entire MCPServer CR is deleted.

Additionally, the current model implicitly couples Proxy and Backend scaling. In practice, this is suboptimal because the Proxy behaves like a control-plane component, while the backend StatefulSet behaves like a data-plane component. These two should not scale identically.

Environment

  • Operator Version: v0.14.1
  • CRD Version: v0.14.1

Current Behavior

  • Scaling Proxy Deployment to 0 terminates Proxy pods
  • Backend StatefulSet remains running
  • Backend is only cleaned up when the MCPServer CR is deleted
  • Scaling behavior implicitly ties Proxy and Backend replica counts

Expected Behavior

  • Scaling to 0 should result in both:
    • Proxy Deployment scaled to 0
    • Backend StatefulSet scaled to 0
  • Scaling above 1 replica should primarily affect the backend StatefulSet, not the Proxy Deployment
  • Proxy should remain singleton by default (control-plane role)

Current Workaround

  • Scale Proxy to 0 via MCPServer CR
  • Manually scale StatefulSet to 0 using kubectl

Note: This is brittle because proxyrunner uses server-side apply and may overwrite manual scaling on restart.

Proposed Solutions

1. Enable Scale-to-Zero in CRD

Update CRD validation:

  • Change replicas minimum from 1 to 0
2. Decouple Proxy and Backend Scaling

Introduce clear scaling semantics:

  • replicas = 0

    • Proxy = 0
    • Backend = 0
  • replicas = 1

    • Proxy = 1
    • Backend = 1
  • replicas > 1

    • Proxy = 1 (default)
    • Backend = N

Optional:

  • Allow explicit configuration for Proxy replicas for advanced use cases
3. Operator-Coordinated Scaling

Enhance MCPServerReconciler:

  • Handle scaling logic instead of relying solely on proxyrunner
  • Ensure StatefulSet is scaled down when replicas = 0
  • Prevent drift between desired state and actual backend replicas

Note: This may require revisiting ownership boundaries between operator and proxyrunner.

4. Proxy Runner Compatibility

Ensure proxyrunner:

  • Supports replicas = 0
  • Does not override operator-managed scaling
  • Respects decoupled scaling model

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the MCPServer CRD replica validation and the MCPServerReconciler, then trace how proxyrunner provisions and scales the backend StatefulSet. Review the ownership boundary and current server-side apply behavior before implementing the stated replica semantics. Done means replicas 0 scales both resources to zero, replicas above 1 keep the proxy singleton by default, and reconciliation prevents backend drift.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kubernetes
Domain
backend, infrastructure
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.