The platform’s default demo appliance is a compact single node. An optional six-node capacity profile uses the same Ansible roles, APIs, runtime model, and application deployment path when additional placement and compute headroom are useful.
Compact single-node profile
make up provisions every platform capability on single-01, a
4 vCPU / 16 GiB / 64 GiB Ubuntu VM. It includes four shared workerd members,
two native Durable Object shards, all data services, ingress and control APIs,
the product portals, the complete observability stack, and trusted local HTTPS
for Worker ingress.
The single node is intended for evaluation, personal self-hosting, and small design-partner installations. It has no host fault isolation: losing the VM loses every service until that VM is recovered. Service memory values are ceilings rather than reservations; the compact inventory lowers the aggregate observability and high-volume service ceilings to keep the host recoverable.
Only one local profile can own ports 8080–8083 and 9090 at a time. Switch without deleting disks:
make down
make up
# Start the optional capacity profile later:
make multi-upRun make capacity-check to create a temporary Standard-tier organization,
deploy 32 distinct sum Workers, invoke each repeatedly through HAProxy, report
guest memory/disk/load evidence, and delete the temporary organization and its
Workers afterward.
The reference 4 vCPU / 16 GiB / 64 GiB profile has been exercised with the complete platform, all curated examples, 32 additional logical Workers, and 128 concurrent verification requests. That run completed without a failed service or request while using 3.01 GiB of memory, 6.9 GiB of root disk, and a 0.58 one-minute load average. These figures establish a practical low-traffic baseline, not a universal capacity promise: Worker code, object volume, queue backlogs, telemetry retention, and request concurrency determine the real ceiling. An 8 vCPU / 32 GiB host is a straightforward way to buy operating headroom without changing the topology.
Six-node capacity profile
Run make multi-up to create the six-node profile. Its additional nodes are
capacity and placement targets, not HA replicas. No database, queue, object, or
telemetry state is mirrored, and the profile makes no quorum or automatic
state-failover claim.
Default placement
| Node | Capacity | Disk | Placed capabilities |
|---|---|---|---|
control-01 |
4 vCPU / 6 GiB | 32 GiB | HAProxy, Data Plane API, control, identity, quotas, Access, tenant, admin, and status portals |
compute-01 |
6 vCPU / 8 GiB | 24 GiB | Four shared workerd pool members |
compute-02 |
6 vCPU / 8 GiB | 24 GiB | Four additional shared workerd pool members |
data-01 |
4 vCPU / 8 GiB | 96 GiB | Object storage, static assets, and Images |
data-02 |
4 vCPU / 12 GiB | 64 GiB | Queues, key-value, databases, vector search, and two native Durable Object shard members with Cron coordination |
observe-01 |
4 vCPU / 10 GiB | 64 GiB | Prometheus, Loki, Tempo, and Grafana |
Lima disks are sparse maximums rather than immediately allocated host usage. The multi-node profile requests 28 virtual CPUs and 52 GiB of memory, leaving room on the intended 40-core, 128 GiB development host for load generators and normal desktop work.
Compute scheduling
runtime_members in ansible/inventory/group_vars/all.yml is the schedulable
runtime set. HAProxy uses consistent hashing on the organization and Worker name
for ordinary requests and health-checks every member. Both compute nodes can
load any deployed immutable Worker version from the control plane, so adding
compute-02 expands the same pool rather than creating a standby copy.
The control plane considers the runtime dependency ready when at least
runtime_min_available members respond. That setting is a readiness floor, not
a replication factor. HAProxy removes unhealthy members from request selection.
Deployments remain fleet-wide logical releases: Wrangler stores one immutable version and publishes one route. It does not select a host or create a per-Worker unit. A healthy runtime member loads the selected version on demand.
Data placement
Data capabilities are assigned by inventory groups (storage, assets, images,
queues, key_value, and databases). The default inventory spreads those
services over data-01 and data-02 to provide resource headroom and reduce
contention. This is explicit placement, not data replication or transparent
failover. Moving a stateful capability requires an intentional data migration;
adding a host alone does not copy its state.
durable_runtime_members is the fixed native Durable Objects pool. Each
stateful application is assigned one stable member and keeps that assignment as
capacity is added. Members can be declared on both data nodes, but an
application’s namespaces are not distributed or replicated between them. See
Durable Objects for update and migration boundaries.
Organization Cron coordinators use the same placement mechanism and native
alarm storage; the control node does not poll a schedule table.
Private fleet network
Every node joins a full-mesh WireGuard network at 10.77.0.0/24. Keys are
generated on the nodes during provisioning. Private service listeners bind to
their WireGuard address, and UFW admits their ports only on wg0. Lima exposes
the public and management endpoints from control-01 and the operator
observability endpoints from observe-01, always onto host loopback.
The Lima user-v2 network is only the WireGuard underlay. Application services
do not trust it directly.
Platform operations views
The administrator portal presents declared topology and live host pressure under Platform → Fleet. Selecting a host opens a focused detail view for its resource pressure and assigned service health; service placement is not repeated as a second fleet-wide inventory. These native views are backed by Prometheus data; Grafana remains an operator troubleshooting tool rather than the platform’s primary interface.
The unauthenticated status surface at http://status.ermahgerd.localhost:8083 uses the same platform-owned health and SLO signals. It intentionally excludes customer Worker response codes, so a faulty tenant deployment cannot report the platform itself as unhealthy.
Changing capacity
Resource envelopes live in lima/single/*.yaml and lima/fleet/*.yaml.
Placement and private addresses live in ansible/inventory/hosts*.yml and
ansible/inventory/host_vars/.
Compute capacity additionally requires entries in runtime_members; each entry
must have a unique name and endpoint. Run make infra-check after changing the
topology, then recreate nodes whose Lima CPU, memory, or disk configuration
changed and run make site or make multi-site for the matching profile.
tools/fleet.py intentionally manages one declared profile at a time. Ordinary
lifecycle targets select single; multi-* targets select fleet. Capacity
membership remains declarative and reviewable rather than being discovered from
whatever Lima instances happen to exist.