---
title: "Register or update a service on a VM"
method: PUT
path: "/v1/vms/{id}/services/{serviceName}"
tags: ["vm-services"]
---

# Register or update a service on a VM

`PUT /v1/vms/{id}/services/{serviceName}`

Idempotent register-or-update: same name + new port updates the
port; same name + same port is a no-op. Returns the resulting
entry. Used to change the upstream port for an existing
service registration without dropping and re-creating it.

## Request body

- UpdateServiceRequest
  - `port` integer, required — New TCP port. Same value as the existing entry is a no-op.
  - `h2c` boolean — Optional. When true, the proxy uses HTTP/2 cleartext to the backend. Same value as the existing entry is a no-op; a different value updates the registered transport.

## Response `200`

Service updated (or no-op same-port re-issue)

- Service
  - `name` string, required — Service name (1–29 chars). Embedded in the public URL as `<name>--<vmIdHexNoHyphens>.proxy.<stack-domain>`.
  - `port` integer, required — TCP port the service listens on inside the VM. Privileged ports (<1024) are rejected.
  - `h2c` boolean, required — When true, the proxy speaks HTTP/2 cleartext (h2c) to the backend. Required for gRPC and h2c-only apps. When false (default), the proxy uses HTTP/1.1 — covers HTTP/1.1 apps, Server-Sent Events, and WebSocket pass-through.

## Other responses

- `400` — Invalid name or port, or per-VM cap exceeded. The body is a `QuotaExceededError` for the cap case and an `Error` otherwise.
- `401` — Missing or invalid credentials
- `404` — Resource not found
- `409` — VM is in `error` state and cannot be modified
- `500` — Internal server error

---

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