Appearance
Operating plexsphere across multiple regions
This guide is the operator-facing walkthrough for running plexsphere with tenants spread across more than one region. It explains the two supported topologies, how to pin a tenant to a region, how the control plane schedules and migrates placements, and the DNS records each region needs.
For the domain model behind the placement decision, see the Management Fleet bounded-context reference and the Identity and Tenancy bounded-context reference. For the wire shape of the surfaces you inspect here, see the Domains HTTP API and the Management Fleet HTTP API.
The two topologies
A multi-region plexsphere deployment combines two independent overlays. You can adopt either on its own, but together they give a tenant a region-local control surface and a region-local data plane.
1. One control plane, regional management clusters
A single plexsphere control plane owns the durable tenancy records and the fleet inventory, while the provisioning substrate lives on one or more regional management clusters. The control plane pins each tenant (a Domain) to a region; the management fleet then places every schedulable Project under that Domain onto a management cluster whose region matches the pin. Tenant records and the assignment ledger stay in the single control-plane database, so the placement decision is centralised and auditable while the per-Project namespaces, RBAC, and quota live close to the workloads on the regional clusters.
2. Per-region ingress overlays
Each region also gets its own ingress. Region-scoped Gateway and HTTPRoute overlays publish distinct hostnames per region — api.<region>.plexsphere.example for the API and web.<region>.plexsphere.example for the dashboard — so a caller in a region reaches a region-local entry point rather than crossing region boundaries on every request. The overlays are additive: the base Gateway and routes are unchanged, and each region overlay layers its own hostnames on top.
Pinning a tenant to a region
Region placement is driven from the Domain aggregate. A Domain carries an optional region handle:
- Shape. A region is a lowercase kebab-case handle matching
^[a-z0-9]+(-[a-z0-9]+)*$(for exampleeu-central-1), at most 64 bytes. The pattern is the same kebab-case rule a slug uses, but the length ceiling is one byte larger because a region is a free-standing locality label and is never folded into a single DNS label. - Unpinned is the default. An empty or absent region means the Domain is unpinned — it has not committed to a region, and its Projects are placed freely (the scheduler does not constrain them to a region). A region-pinned Domain, by contrast, constrains every Project beneath it.
- Re-pinnable. Unlike the Domain slug — which is immutable once the Domain is created — the region is set on create and can be changed later with a
PATCH /v1/domains/{id}. Re-pinning a Domain is the trigger for the migration runbook below.
The Project region is derived from its Domain; it is never stored on the Project. Re-pinning the Domain therefore re-targets every Project underneath it at once.
To pin a Domain on create, include region in the create body; to re-pin an existing Domain, send a region in the patch body. See the Domains HTTP API for the request and response schemas.
How placement is scheduled
A RegionAssignmentResolver runs on every management-fleet reconcile sweep. Its scheduling half — Schedule() — places each schedulable Project onto a management cluster:
- A Project is schedulable when it owns at least one resource and does not yet have a management-cluster assignment.
- For each schedulable Project, the resolver looks for a management cluster whose region exactly string-matches the Project's Domain region: the contract is
ManagementCluster.Region() == Domain.Region(). There is no fuzzy matching, no nearest-region fallback, and no normalisation beyond the kebab-case validation already applied at construction. - When a matching cluster exists, the resolver creates a
Pendingassignment on it and emits aProjectClusterAssignedevent. - When no cluster matches the region, the resolver logs a
WARNand skips the Project. No assignment row is created; the Project stays unplaced until a cluster for its region is registered. A missing region match is a per-Project skip, not a sweep failure.
Register a management cluster for a region before you pin Domains to it, otherwise their Projects log the skip warning and never place.
Migrating a tenant to another region
When you re-pin a Domain to a different region, the existing placements no longer match. The resolver's migration half — MigrateOutOfRegion() — reconciles this one step per sweep. It handles any assignment whose Domain region no longer matches the region of the cluster currently hosting it.
The migration path depends on whether the Project owns resources.
Zero-resource Projects migrate automatically
For a Project that owns no resources, the resolver drives the move itself, one step per sweep:
- It advances the current assignment's namespace phase through the teardown arm — from
Pending,Provisioning,Ready, orDegradedintoTerminating. - While the namespace is
Terminating, the resolver waits; the reconcile loop drains the per-Project namespace and its RBAC and quota objects. - Once the old assignment reaches
Deleted, the resolver re-points the Project to a freshPendingassignment on a cluster in the correct region, emitting aProjectClusterAssignedevent.
Because each sweep advances the state machine by one step, a migration spans several sweeps; this is expected and safe to leave running.
Resource-owning Projects are blocked
For a Project that owns resources, the assignment is immutable. The resolver does not move it. Instead it logs a WARN and skips the Project, leaving the existing assignment untouched, and the sweep still returns success — per-Project conditions like a blocked migration are skip-and-WARN; only infrastructure errors fail a sweep.
Moving such a Project across regions would orphan its live Composite Resources, ProviderConfigs, and Secrets in the old region's namespace. To migrate it deliberately, drain its resources on the old cluster first (drive its namespace through Terminating to Deleted and unassign it), then let the next sweep place it in the new region.
Inspecting a placement
To see where a Project currently sits and which lifecycle phase its namespace is in, read its assignment:
text
GET /v1/projects/{project_id}/management-cluster-assignmentThe response carries the management_cluster_id, the region the placement landed in, the namespace_name, and the namespace_phase. During a migration the phase walks the teardown arm (Terminating → Deleted) on the old assignment and starts at Pending on the new one. If a Project never gains an assignment after you pinned its Domain, check that a management cluster is registered for that region — the resolver skips with a WARN when no cluster matches.
Region DNS model
Each region publishes two hostnames, and each hostname needs exactly one DNS record pointing at that region's ingress:
| Hostname | Points at | Record |
|---|---|---|
api.<region>.plexsphere.example | the region's API ingress address | one A or CNAME |
web.<region>.plexsphere.example | the region's dashboard ingress address | one A or CNAME |
Use an A record when the region ingress exposes a stable address and a CNAME when it exposes a hostname (for example a load-balancer DNS name). There is one record per hostname per region — adding a region is two records (api.<region>… and web.<region>…) pointed at that region's ingress, in lockstep with registering the region's management cluster and pinning the tenants that should land there.
Cross-references
- Management Fleet bounded-context reference — the
ManagementClusterandProjectClusterAssignmentaggregates, the namespace-phase state machine, the region-assignment resolver, and the recovery runbook. - Identity and Tenancy bounded-context reference — the Domain region pin, its invariants, and the Domain → Project → Resource → Node aggregate model.
- Domains HTTP API — the
regionfield on the Domain create, patch, and response schemas. - Management Fleet HTTP API — the assignment-inspection and terminate endpoints.