Skip to content

Scale-to-zero

AnvilBase runs a single shared Postgres with one isolated platform_<id> database per project (the project UUID with hyphens removed — 32 hex chars) — there is no per-project compute instance to spin down. So the Neon idea of “scale to zero when idle” maps onto the only per-project resource AnvilBase holds in memory: the pooled connections the control plane keeps open on a project’s behalf.

When a project goes idle, AnvilBase automatically releases its in-memory pooled resources, and lazily re-creates them on the very next request. This reclaims connections and memory for projects that aren’t being used right now, while keeping the first request after an idle period correct and transparent.

Your data is never affected

Scale-to-zero is purely connection/pool lifecycle — it drops cached, in- memory connection pools, nothing else. It never touches a project’s database contents, tables, rows, storage objects, secrets, or queues. A “scaled-to-zero” project loses nothing; the next request simply re-opens a fresh pool.

What scales to zero

Two per-project, in-memory resources are governed by the same idle window:

  • REST→SQL connection pools — the per-project deadpool Postgres pools the built-in REST→SQL engine uses. After the idle window the pool is dropped; the next REST request transparently re-creates it. (Read- replica pools, when a read replica is configured, use the same window.)
  • Valkey cache connections — the per-project authenticated Valkey connection the cache data-plane reuses. After the idle window the connection is released; the next cache operation re-establishes it.

Both behave the same way: drop on idle, restore on demand. Because the restore is lazy and automatic, callers never see a difference beyond a one-time reconnect cost on the first request after an idle period.

How it works

  • A lightweight background reaper runs every 60 seconds.
  • Each per-project pool/connection records when it was last used (refreshed on every request).
  • On each tick, any pool/connection idle longer than the configured window is evicted (its connections closed and the entry removed).
  • The next request for that project finds no warm pool and re-creates one on the spot — the lazy resume.

This is entirely additive and in-memory. No project state is read or written by the reaper.

Configuration

The idle window is controlled by a single environment variable on the control plane:

VariableDefaultMeaning
ANVILBASE_POOL_IDLE_TIMEOUT_SECS300Seconds a project’s pooled DB + cache resources may sit idle before they are released. Applies to REST pools, read-replica pools, and Valkey cache connections alike.
  • Default (300) — five minutes, unchanged from earlier releases. If you set nothing, behavior is byte-for-byte identical to before this feature existed.
  • Tune it — lower it (e.g. 60) to reclaim resources from idle projects more aggressively on a busy multi-tenant node; raise it (e.g. 1800) to keep pools warm longer and avoid reconnect latency for bursty workloads.
  • Disable it (0) — a value of 0 means never evict: pools and cache connections stay warm for the process lifetime (scale-to-zero off). The reaper still ticks but performs no eviction.
  • A missing or non-numeric value falls back to the safe 300 default — a typo can never silently disable scale-to-zero.
Terminal window
# Reclaim idle project pools after 60s instead of the 300s default
ANVILBASE_POOL_IDLE_TIMEOUT_SECS=60
# Or turn scale-to-zero off entirely (keep every pool warm)
ANVILBASE_POOL_IDLE_TIMEOUT_SECS=0

Observability

You can see scale-to-zero working — i.e. how many projects are currently warm (holding live pools/connections) — in two places:

  • Prometheus metrics (scraped at GET /metrics, see Monitoring):

    • db_project_pools — number of projects with a live REST→SQL pool.
    • cache_warm_connections — number of projects with a live Valkey cache connection.

    Both are single, bounded gauges (never a per-project series). They fall as idle projects are reaped and rise as projects resume on demand.

  • Admin statsGET /api/v1/admin/stats includes:

    • warm_project_pools — projects with a live REST→SQL pool.
    • warm_valkey_conns — projects with a live Valkey cache connection.

A large total project count with a small warm count is exactly the signal that scale-to-zero is doing its job: most projects are idle and consuming no pooled resources.

Notes & limits

  • Scale-to-zero is per process / per control-plane instance — each instance manages its own pools. There is no global coordination, and none is needed: a request lands on whichever instance, which warms its own pool.
  • The first request after an idle period pays a one-time pool/connection re-establishment cost (typically single-digit milliseconds on the local network). Subsequent requests reuse the warm pool until it goes idle again.
  • This feature does not pause Postgres itself or any project database — the shared Postgres is always running. It only governs the control plane’s in-memory connection lifecycle.