Open-Source Wikis

/

Neon

/

API

/

Storage controller API

neondatabase/neon

Storage controller API

The storage controller's HTTP API has four logically separate surfaces, all served from the same axum app in storage_controller/src/http.rs (~91 KB). They are partitioned by URL prefix.

The four prefixes

/v1/... — pageserver-compatible

Mirrors the pageserver's /v1/ API so that callers (e.g. the cloud control plane) can talk to the controller as if it were a pageserver. The controller fans the call out to the right set of pageservers (multiple shards, possibly on multiple nodes).

Examples:

Method Path Effect
POST /v1/tenant Create a tenant; the controller chooses pageservers to attach shards on.
DELETE /v1/tenant/{tenant_id} Delete a tenant on every pageserver that owns a shard.
POST /v1/tenant/{tenant_id}/timeline Create a timeline on every shard.

/control/v1/... — controller-specific

Operations that don't make sense on a single pageserver:

Method Path Effect
POST /control/v1/node Register a new pageserver.
PUT /control/v1/node/{id} Reconfigure a node (online/offline, drain, schedulable, ...).
POST /control/v1/tenant/{tenant_id}/shard_split Split shards.
POST /control/v1/tenant/{tenant_id}/migrate Migrate a shard.
GET /control/v1/tenant/{tenant_id} Describe a tenant: shards, generations, attached/secondary pageservers.

storcon-cli (in control_plane/storcon_cli/) is the operator-facing CLI for this surface.

/debug/v1/... — for tests and operators

Endpoints that are intentionally not part of the stable API. Used by integration tests to coerce the controller into specific states, and by operators during incidents. Examples include forcing a reconcile, replaying a placement decision, or dumping internal state machines.

/upcall/v1/... — called by pageservers

Pageservers call up to the controller, primarily for the generation handshake:

Method Path Purpose
POST /upcall/v1/re-attach At pageserver start, register and ask which tenants/generations to attach.
POST /upcall/v1/validate Periodically confirm that a tenant's generation is still current.

Authentication uses the same JWT scheme as the pageserver, with scope pageserverapi.

Compute notification

Although not a request flow per se, the controller also pushes attachment changes to compute nodes via the compute_hook (storage_controller/src/compute_hook.rs, ~46 KB). When the controller migrates a shard or splits, it issues an HTTP request to the affected compute's compute_ctl to update its routing table. The compute then re-establishes connections to the new pageserver.

Database persistence

Everything that survives a controller restart goes through storage_controller/src/persistence.rs (~108 KB) using diesel. Migrations under storage_controller/migrations/ are run automatically at startup.

The set of persisted things is intentionally minimal: tenants, nodes, and a few flags. Tenant shard placement (which node hosts which shard) is rebuilt from observation at startup by querying every registered pageserver. This avoids the complexity of keeping placement durable when the source of truth (the pageservers themselves) is what actually matters.

Reconciliation API contract

When you call /v1/tenant to create a tenant:

  1. The controller writes a row to the metadata DB (durable).
  2. It marks the tenant's intent state as "create on N pageservers."
  3. The reconciler picks this up and calls each pageserver's /v1/tenant route.
  4. The HTTP response to the original /v1/tenant call is delayed until the reconciler reports success — or returns immediately with a "pending" status, depending on the request mode.

Most write-side endpoints accept a mode=sync|async query parameter or default to a service-specific behavior. See the route handlers in storage_controller/src/http.rs for specifics.

Spec source

Unlike the pageserver and safekeeper, the storage controller does not check in a YAML OpenAPI spec — its routes are large and evolve more rapidly. The most reliable spec is storage_controller/src/http.rs itself plus the test fixtures in storage_controller/src/.

Authentication

pageserverapi JWT scope. Same key configuration as the pageserver. Multiple controller instances share the same JWT verification config.

Multiple instances

Multiple storage controllers can run; one is leader (elected via Postgres advisory locks in storage_controller/src/leadership.rs). Followers serve read-only requests and stand by to take over.

Clients

Crate Notes
storage_controller_client (storage_controller/client/) Rust client for the HTTP API. Used by the pageserver (for upcalls), storcon-cli, and tests.
storcon-cli (control_plane/storcon_cli/) CLI wrapping the client.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Storage controller API – Neon wiki | Factory