Regions
One box per region, chosen by where the caller is. There is no cluster.
There is no cluster. A region is one VM running one livekit-server, with its
own key pair and its own firewall, sharing nothing with any other region.
Adding a region is standing up a second box and adding a row to configuration. It is never a code change.
Why it is shaped this way#
A region going down should not be able to take another one with it. There is no coordination step to fail, no shared state to diverge, and no clustering software to operate. The cost is that a room lives on exactly one region — a caller in Singapore and a caller in Virginia joining the same room means one of them is crossing an ocean, and there is no mechanism that mends this.
Choosing by caller, not by capacity#
Selection is caller proximity. The VM's only job is moving packets, so the region that minimises distance to the caller is always correct — there is no scenario where routing a Singapore caller to Virginia is right for capacity reasons.
caller_country is an ISO 3166-1 alpha-2 code, taken from the country code of an
inbound phone number or from the client's own locale for a web call. Pass it to
start_call and list_live_calls.
The country-to-region map is deliberately coarse — good enough for two or three regions, not a geo-IP service. Extend the map before reaching for a routing provider; most of the value is "not obviously wrong", not "optimal".
A caller country that maps to nothing falls back to the first configured region rather than throwing. A misclassified caller on a working region beats a dropped call over a geo-routing edge case.
Configuring them#
REGIONS_JSON is an array of region specs. Key names, never key values — the
same rule as env/.env.example, so a region table can be committed and read
without carrying a secret:
[
{ "id": "sg", "label": "Singapore", "url": "wss://sg.example.com",
"apiKeyEnv": "LK_SG_KEY", "apiSecretEnv": "LK_SG_SECRET" },
{ "id": "us-east", "label": "Ashburn", "url": "wss://use.example.com",
"apiKeyEnv": "LK_USE_KEY", "apiSecretEnv": "LK_USE_SECRET" }
]
A region whose named key or secret is missing from the environment is dropped
from the list, not reported. That is a deliberate fail-quiet: a half-configured
region that mints tokens nothing can use is worse than one that is not offered.
Check what the control plane actually loaded with
describe_call_config rather than by reading the JSON.
One region needs none of this#
With REGIONS_JSON unset, the control plane falls back to the single
LIVEKIT_URL / LIVEKIT_API_KEY / LIVEKIT_API_SECRET triple and
caller_country is ignored. A one-region deployment never needs to know the
region system exists.
Costs, measured#
| Region id | Location | Type | Price/month |
|---|---|---|---|
eu | Falkenstein | cx23 2c/4GB | $5.99 |
sg | Singapore | cpx12 1c/2GB | $17.99 |
us-east | Ashburn | cpx11 2c/2GB | $20.49 |
Server types are location-locked and the provider's API misreports it — cx23
exists only in the EU, and cpx11 publishes a Singapore price for a type that is
not available there. The runbook at The media plane
has the working combinations.