Scaling
Each app has three knobs:
| Knob | Default | Notes |
|---|---|---|
| Replicas | 1 | Number of containers nginx load-balances over. |
| CPU millis | 500 | 1000m = 1 CPU. Hard cap. |
| Memory MB | 256 | Hard cap. |
These are stored on the App row as defaults, snapshotted into a Release
each time you deploy, and reconciled by the Orchestrator.
Changing them
Dashboard: not in v1 UI yet — set via API:
curl -X PATCH https://paas.example.com/api/orgs/acme/apps/web/scale \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"replicas":3,"cpuMillis":1000,"memoryMb":512}'
The change marks the App proxy-dirty. On the next reconciliation tick the Orchestrator brings the desired replica count up (or down), and the ProxyController rewrites the upstream block.
How rolling updates work
When a new Release is promoted (after a successful build):
- Orchestrator computes the desired set:
replicascontainers of the new Release. - It starts new containers in parallel (one per available replica slot) and waits for each one to pass its health probe.
- As soon as any new container is healthy, it begins draining old
containers — one per loop tick (every
LoopIntervalSeconds, default 5s) to keep churn bounded. - ProxyController re-renders the upstream block on every change, only
reloading nginx if
nginx -tsucceeds and the rendered config changed.
Why nginx and not a smarter LB?
For single-host with horizontal replicas, nginx's upstream { server …; server …; } round-robin is fine. The Orchestrator only adds a server to the
block once it's healthy, and nginx's max_fails/fail_timeout will stop
sending traffic to a member that crashes between health probes. Replace this
with HAProxy / Envoy / Traefik when you have multi-region affinity needs.
Limits
- Replicas are capped only by your host's RAM/CPU. Paas does not enforce cluster-level quotas in v1.
MemoryMb < 32andCpuMillis < 50are rejected at the API.- A replica that fails its health probe is left as
Unhealthyand never enters the upstream pool. The reconciler will not "heal" it by replacing the container in v1 — you have to redeploy.
"Zero downtime" caveats
Rolling updates assume your app:
- Returns
< 500toGET /quickly (soHealthyflips on the first probe). - Tolerates running side-by-side with the old version for one tick.
- Doesn't take long-lived connections that span the swap (your old replicas
get a
SIGTERMwith a 10s grace period beforeSIGKILL).
For workers / migrations / one-shots that don't fit this model: deploy them
as a separate app (web-migrate) with replicas=0 and trigger them from
your app's startup with a feature-flag gate. v1.1 will add explicit
"release" / "pre-deploy" hooks.