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

# Register a service on a VM

`POST /v1/vms/{id}/services`

Registers an HTTP service on the VM under `name`, listening on
`port`. The service immediately becomes addressable at
`https://<name>--<vmIdHexNoHyphens>.proxy.<stack-domain>` once
the firewall is applied (synchronous).

Idempotent: a POST with a name that already exists at the same
`(port, h2c)` returns 201 with the existing entry. POST with a
name that already exists at a different port OR different `h2c`
returns 409 — use PUT to update an existing service.

Per-VM cap: currently 16 services per VM (configurable via
`MAX_SERVICES_PER_VM` on the scheduler).

## Request body

- RegisterServiceRequest
  - `name` string, required
  - `port` integer, required
  - `h2c` boolean — Optional. When true, the proxy uses HTTP/2 cleartext to the backend (required for gRPC). Defaults to false (HTTP/1.1).

## Response `201`

Service registered (or idempotent same-`(port, h2c)` re-register)

- 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, invalid port, or per-VM cap exceeded. The body is a `QuotaExceededError` for the cap case (carries the structured `vm_service_quota_exceeded` reason + numeric count) and an `Error` otherwise.
- `401` — Missing or invalid credentials
- `404` — Resource not found
- `409` — The service name is already registered at a different port or different `h2c` (use PUT to update), or the 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)
