---
title: "Set a serving deployment's desired replica count (platform admin)"
method: POST
path: "/v1/admin/inference/deployments/{id}/scale"
tags: ["Internal"]
---

# Set a serving deployment's desired replica count (platform admin)

`POST /v1/admin/inference/deployments/{id}/scale`

Sets replicasDesired. The value must lie within the deployment's replicasMin..replicasMax band; out-of-range values are rejected rather than silently clamped. The reconcile loop converges the fleet.

## Path parameters

- `id` string, required

## Request body

- ScaleServingDeploymentRequest
  - `desired` integer, required — Target replica count; must lie within replicasMin..replicasMax (as adjusted by this request)
  - `replicasMax` integer — Optional new upper bound for the replica band. Omitted = keep the current value. desired is validated against the adjusted band.
  - `replicasMin` integer — Optional new lower bound for the replica band. Omitted = keep the current value. desired is validated against the adjusted band.

## Response `200`

Updated

- ServingDeployment — A self-hosted serving deployment: the durable intent to run N replicas of an inference engine for one public catalog model, plus the runtime state the reconcile loop maintains while converging on it.
  - `backendCatalogWeight` integer — Live catalog routing weight of this deployment's backend. Only present when backendRegistered is true. May differ from backendWeight (the row mirror) while a rollout ramp is in flight.
  - `backendHealthy` boolean — Live catalog health of this deployment's backend (the kill switch). Only present when backendRegistered is true.
  - `backendRegistered` boolean — Whether this deployment's pool:// backend is currently present on the catalog model row. Absent when the live catalog state could not be read; false until the first replica passes readiness.
  - `backendVersion` string — The rollout tag this deployment's backend reports to the gateway as the `version` metric label on ig_model_requests_total and ig_stream_ttft_seconds. Use it to separate the two sides of a rollout in a dashboard query. Only present when backendRegistered is true.
  - `backendWeight` integer, required — Routing weight of the catalog backend. 0 = standby (blue/green starts here)
  - `createdAt` string, required
  - `dtype` string
  - `engine` string, required — Inference engine, e.g. vllm or sglang
  - `engineImage` string — Engine container image override; absent = the engine's default
  - `env` object
  - `extraArgs` string[]
  - `gpuCountPerReplica` integer, required
  - `gpuModelId` string, required
  - `id` string, required
  - `isolated` boolean, required — True when replicas and their private endpoint pool are for direct operator testing only. Isolated deployments never register a catalog backend, never participate in blue/green rollout, and cannot receive gateway traffic.
  - `maxConcurrencyPerReplica` integer
  - `maxModelLen` integer
  - `maxPromptBytes` integer — Exclusive text-request byte ceiling for routing eligibility; 0 or absent = unbounded. Routing hint only, not a context limit.
  - `minPromptBytes` integer — Inclusive text-request byte floor for routing eligibility; 0 or absent = unbounded. Routing hint only, not a context limit.
  - `nativeFamilies` string[]
  - `nodePool` string — Stable logical capacity lane within the public model. Blue/green only replaces versions in the same node pool; empty preserves legacy model-wide grouping.
  - `organizationId` string, required — Org that owns the managed replicas (and their metering)
  - `poolId` string — Endpoint pool the deployment's replicas register in
  - `publicModelId` string, required — Catalog model this deployment serves capacity for
  - `quantization` string
  - `readyReplicas` integer, required
  - `replicaVmIds` string[]
  - `replicasDesired` integer, required
  - `replicasMax` integer, required
  - `replicasMin` integer, required
  - `rollout` ServingRolloutState — DERIVED rollout state for this deployment's lane (public model + node pool). Computed by the same code that drives the rollout, so an operator surface can never narrate a different story than the controller will execute. Absent when the deployment is isolated, terminal, or the live catalog could not be read.
    - `blueSlots` integer — The blues' combined ready serving capacity, same unit
    - `cutoverReady` boolean — Whether green has the capacity parity cutover requires (>=95% of blue's slots)
    - `greenSlots` integer — Green's ready serving capacity: readyReplicas x maxConcurrencyPerReplica
    - `lane` string, required — The lane's label, publicModelId/nodePool
    - `laneMemberIds` string[] — Every live deployment in this lane, oldest first
    - `laneWeight` integer — The lane's full weight: the largest weight any blue carries
    - `nextAction` string — One sentence naming what the controller will do next and why, written by the deciding code itself.
    - `peerId` string — The other side of the roll: for a green, the newest blue it replaces; for a blue, the green replacing it. Absent when the lane is not rolling.
    - `phase` 'none' | 'waiting_for_replicas' | 'ramping' | 'capacity_hold' | 'at_full_weight' | 'cut_over' | 'rolled_back', required — Where the roll is. capacity_hold is the state the capacity guard produces: green is ramped as far as its serving capacity justifies and will not advance until it is scaled up.
    - `role` 'independent' | 'blue' | 'green', required — This deployment's part in its lane RIGHT NOW. independent = carries its own weight and is not in a rollout (also what every lane member is when no green exists). blue = an older member a green is ramping against. green = the opted-in successor.
    - `weightCeiling` integer — The highest weight green may carry right now under the capacity guard (its share of the lane's ready serving slots, scaled onto laneWeight).
  - `rolloutRole` '' | 'green' — Opt-in blue/green role. Empty (default) = an INDEPENDENT weighted capacity member of its lane, never blue or green. "green" = the lane's successor generation: the controller ramps it against the older members under a capacity guard and cuts them over once it reaches capacity parity. Rollouts are never implicit.
  - `servedModelId` string, required — Model id the engine serves; must equal the catalog backend ref
  - `status` 'pending' | 'deploying' | 'ready' | 'degraded' | 'scaling' | 'draining' | 'stopped' | 'failed', required
  - `statusReason` string — Human-readable cause for degraded/failed states
  - `updatedAt` string, required
  - `version` string — Generation marker for blue/green within one public model and node pool
  - `weightsRef` string — Staged-checkpoint reference; absent = the engine loads from its own source

## Other responses

- `400` — The request is invalid
- `401` — Missing or invalid API key
- `403` — API key lacks the required scope
- `404` — Resource not found
- `409` — The request conflicts with existing state
- `503` — A required integration (e.g. payments) is not configured

---

[API](https://skmtc.net/openrelay/apis/openrelay-api.md) · [All operations](https://skmtc.net/openrelay/apis/openrelay-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/openrelay/openrelay-api/versions/32905b8f44f8/schema)
