Skip to content

bug(operator): VirtualMCPServer statusReportingInterval requeue Updates the Deployment every interval (no rollout) #6340

Description

@RaviTharuma

Bug description

On operator/vmcp v0.43.0, a VirtualMCPServer with config.operational.failureHandling.statusReportingInterval: 1m produces a DeploymentUpdated event pair every minute, and the operator logs Updating Deployment on the same cadence.

The backing Deployment’s pod template does not change (same ReplicaSet for days, no new rollout). The event text still says “rolling out new configuration”.

This is the same symptom class as #5819, but it happens with no external admission webhook / sidecar injector writing pod-template labels or annotations. The live pod template only has operator-owned keys (toolhive.stacklok.dev/runconfig-checksum + the usual toolhive labels).

Sibling VirtualMCPServers in the same namespace without statusReportingInterval set do not emit these events.

Steps to reproduce

  1. Install ToolHive operator v0.43.0 on Kubernetes.
  2. Create a VirtualMCPServer that already has a Ready Deployment, and set:
spec:
  config:
    operational:
      failureHandling:
        statusReportingInterval: 1m
        healthCheckInterval: 5m
        healthCheckTimeout: 15s
        unhealthyThreshold: 3
        partialFailureMode: fail
  1. Watch:
kubectl get events -n <ns> --field-selector involvedObject.kind=VirtualMCPServer
kubectl logs -n <ns> deploy/toolhive-operator | grep 'Updating Deployment'
kubectl get deploy <vmcp-name> -n <ns> \
  -o custom-columns='GEN:.metadata.generation,REV:.metadata.annotations.deployment\.kubernetes\.io/revision'
kubectl get rs -l toolhive-name=<vmcp-name> -n <ns>

Expected behavior

  • Status reporting updates VirtualMCPServer.status (backend health / phase) only.
  • ensureDeployment is a no-op when the desired pod template is unchanged.
  • DeploymentUpdated / “rolling out new configuration” is emitted only when a new ReplicaSet is actually created.

Actual behavior

  • Every ~60s (exactly the configured statusReportingInterval): two DeploymentUpdated events and two Updating Deployment log lines (two reconcile IDs at the same timestamp).
  • Deployment metadata.generation can move; deployment.kubernetes.io/revision and the active ReplicaSet stay put.
  • Event stream is unusable for real config changes. Extra apiserver write load on every status tick.

Observed on one tenant (11 backends, unique podTemplateSpec + telemetryConfigRef). Three other vMCPs in the same cluster, same operator, no statusReportingInterval, stay quiet.

Environment (if relevant)

  • OS/version: Talos Linux, single-node Kubernetes
  • ToolHive version: operator + proxyrunner + vmcp images v0.43.0

Additional context

Docs say statusReportingInterval “controls how often the vMCP runtime reports backend health and phase changes” and that lower values increase API server load. That should mean status patches, not Deployment writes.

Likely: a VMCP status update re-enqueues the VirtualMCPServer reconciler, and ensureDeployment treats something as drift (hash / map equality / defaulting) and calls Update + DeploymentUpdated even when spec.template is identical.

Related: #5819 (hot-loop when an external controller annotates the pod template — different trigger, same event). #5817 / #5818 (other ensureDeployment hot-loops, already closed).

No credentials or private hostnames in this report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue needs initial triage by a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions