Skip to content

Maintenance mode

Close the API to clients while you migrate data, restore a backup or change the schema. Admins keep working.

Maintenance mode closes the API to clients without stopping the server. You keep the admin panel, so you can run a migration, restore a backup or fix data while nobody else writes to it.

Turn it on in Settings → Maintenance mode. A banner on every admin page shows while it’s on.

Restoring a backup leaves maintenance mode as it was (since 0.40.0), so you can turn it on, restore, check the result and then reopen. See Before you reopen.

Every refused request gets 503 Service Unavailable with your message:

{
"error": "Upgrading the database. Back at 14:00 UTC.",
"code": 503,
"maintenance": { "level": "full", "until": 1793552400, "retry_after": 1800 }
}
  • until and retry_after are there when you set Back at. The response also carries a Retry-After header. Browsers can’t read that header cross-origin, so the body carries it too.
  • The response isn’t cached (Cache-Control: no-store), and it has the usual CORS headers, so a browser app can read it.
  • Maintenance mode doesn’t end by itself at Back at. A migration that overruns isn’t opened up halfway; you switch it off.

The SDKs turn this response into a typed error, so an app can show a “back soon” screen:

// JavaScript
if (isVaultbaseError(e) && e.kind === "maintenance") {
showDownPage(e.data.message, e.data.until);
}
// Dart
on MaintenanceException catch (e) {
showDownPage(e.message, e.until);
}
Fully off (default)Read-only
Record lists, single records, files503work
Creates, updates, deletes, uploads503503
Sign-in, sign-up, token refresh503503
Realtimerefused; open connections closedworks
Custom routes503 unless left open503 unless left open

In read-only mode, signed-in users keep reading as long as their token is valid. Signing in and refreshing a token are writes, so they wait.

  • Admins. The admin panel, the admin API, admin API tokens (the CLI, scripts) and MCP. A request counts as an admin’s when it carries an admin session, or an API token that acts as admin for that request: one with the admin scope, or the read scope on a GET or HEAD. A token with an mcp:* scope passes on /api/mcp. Other tokens (a write token, a read token on a write) get the 503. The token’s scopes still apply.
  • /api/health. Monitoring keeps working, and the response says "maintenance": "full" (or "read_only", or null when off).
  • Custom routes you leave open. All custom routes are closed by default. Under Custom routes left open, open the ones that must keep answering, such as a status page or a payment webhook receiver.
  • Pages outside the API. /_/ (the admin), /_/health, /_/ready and the account pages under /auth/. The account pages call the API, so their forms get the 503 too.

While maintenance mode is on, background work is paused by default:

  • Cron jobs don’t run. A job that came due runs once when maintenance ends; missed slots aren’t run one by one.
  • Queue jobs stay queued. Jobs already running finish.
  • Webhook deliveries stay pending and go out when it ends.

Turn on Keep running to leave them alone.

The hourly sweep that deletes uploaded files no record refers to is paused either way: a migration can drop a file’s reference for a while.

Record hooks always run. They run inside the writes admins make, and may be what keeps your data valid.

In fully-off mode, new realtime connections from clients are refused, and open ones are closed within a couple of seconds. Before closing, the server sends this message:

{ "type": "error", "reason": "maintenance", "message": "Upgrading the database." }

The SDKs reconnect with backoff, so clients come back by themselves when maintenance ends. Connections that authenticated with an admin token (the auth message, or the token in the SSE subscription request) stay open.

Settings are shared through the database. Other servers using the same database follow a change within about two seconds.

The admin writes these keys; you can also set them with the settings API or MCP.

KeyValuesDefault
maintenance_mode.enabled0, 10
maintenance_mode.levelfull, read_onlyfull
maintenance_mode.messageup to 500 charactersa generic message
maintenance_mode.untilUnix time in seconds, or emptyempty
maintenance_mode.backgroundpause, runpause
maintenance_mode.routesJSON array, e.g. ["GET /status"][]