---
title: "Deployment profiles and capacity"
---

> Documentation Index
> Fetch the complete documentation index at: https://ermahgerd-docs.t-bd7662a5eeb4.workers.ermahgerd.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment profiles and capacity

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:

```sh
make down
make up

# Start the optional capacity profile later:
make multi-up
```

Run `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](/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.

Source: https://ermahgerd-docs.t-bd7662a5eeb4.workers.ermahgerd.io/fleet/index.mdx
