Limits

Every Lakebed deploy runs with resource limits. This page lists the values a default deploy gets, the error you see when you cross one, and what to change in your capsule.

The numbers below are free-plan defaults. Claiming a deploy does not raise the free-plan limits. Paid plans can have higher values.

Default limits

LimitDefaultWhen you hit itWhat to do
Requests per deploy per day10000HTTP 429 with Retry-After and X-RateLimit-* headers. Body has code: "lakebed_quota_exceeded" and bucket: "requests".Wait for the reset at 00:00 UTC, or cut round trips. One subscription that stays open beats polling.
Mutations per deploy per day1000HTTP 429, bucket: "mutations". Mutations and actions use this quota and also count as requests.Batch writes into one mutation instead of one call per row.
Rows returned per handler1000Error is collect() exceeded the 1000 row return limit. Use paginate(). for one collection, or Database rowsReturned limit exceeded (1000). for combined reads.Use paginate({ cursor, numItems }) instead of collect(), or take(n) with a small n.
Rows read per handler5000Handler throws Database rowsRead limit exceeded (5000).Add an index and use withIndex so the query stops scanning rows it throws away.
Bytes read per handler4 MiBHandler throws Database bytesRead limit exceeded (4194304).Return fewer rows, or move large text into object storage.
Direct get calls per handler1000Handler throws Database directGets limit exceeded (1000).Replace per-row get loops with one indexed query.
Index scans per handler100Handler throws Database scanCalls limit exceeded (100).Fold repeated queries into a single indexed range.
Writes per handler1000Handler throws Database writes limit exceeded (1000).Split the work across several mutations.
Bytes per field value64 KiBWrite throws Value for <table>.<field> exceeds 65536 bytes.Upload the payload with client.storage.upload and keep the returned key in the row.
Rows stored per deploy16384Mutation commit fails with a 429, bucket: "state_rows".Delete rows you no longer need.
Bytes stored per deploy1 MiBMutation commit fails with a 429, bucket: "state_bytes".Delete rows, or shorten stored values.
Transaction time5000 msThe transaction is cancelled and rolls back.Do less per handler. Move slow work to an action.
Single statement time4500 msThe statement is cancelled and the transaction rolls back.Add an index for the query.
Row lock wait1000 msThe write fails and rolls back when another write holds the row.Retry, and avoid many clients writing the same row at once.
Action arguments16 KiBDeploy log shows Action arguments exceed 16384 bytes.Pass an id and read the data inside the action.
Action result48 KiBDeploy log shows Action result exceeds 49152 bytes.Return only the fields the client needs.
Action runtime5000 msThe action is cancelled. Deploy log shows Action exceeded 5000ms.Keep outbound calls short. Split long work into several actions.
Stored file size5 MiBUpload fails with HTTP 413 and code: "too_large".Compress or resize before upload.
Stored bytes per developer100 MiBUpload fails with HTTP 413 and code: "quota_exceeded".Delete objects you no longer serve.
Log entries kept1000The oldest entries are dropped. No error.Log less per request, or read logs sooner.
Log bytes kept256 KiBThe oldest entries are dropped. No error.Log less per request.
Bytes per log entry16 KiBLong messages are cut. If the entry is still too large, data is replaced with an object that has truncated: true.Log a summary, not the whole object.
Artifact size1 MiBnpx lakebed build fails and reports the artifact's byte size and the 1048576 byte deploy limit.Remove unused code and large inline data from the capsule.
Server env keys64Deploy fails with serverEnv.values may include at most 64 keys.Combine related values into one JSON key.
Server env key length128 bytesDeploy fails with serverEnv.values.<key> exceeds 128 bytes.Use a shorter name.
Server env value length16 KiBDeploy fails with serverEnv.values.<key> exceeds 16384 bytes.Keep long payloads out of env.
Server env total size64 KiBDeploy fails with serverEnv.values exceeds 65536 bytes. Keys and values both count.Remove keys you do not read.

Reading a quota error

HTTP quota errors use this body:

{
  "code": "lakebed_quota_exceeded",
  "bucket": "requests",
  "current": 10001,
  "error": "requests quota exceeded. Limit: 10000.",
  "limit": 10000,
  "resetAt": "2026-01-02T00:00:00.000Z",
  "retryAfterSeconds": 41230,
  "suggestion": "Retry after reset or reduce request frequency."
}

App WebSocket errors put this body in quota. The outer message has op: "error", ok: false, the request id, and the same code and error. Read message.quota.bucket, message.quota.current, and message.quota.retryAfterSeconds for WebSocket quota details.

HTTP daily quota responses also carry Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Read bucket to find the row in the table above. Do not retry a daily quota in a tight loop. Wait for retryAfterSeconds. Stored row and byte quotas have no timed reset. Their resetAt and retryAfterSeconds are null.

Per-handler database limits fail the handler and roll back its transaction. Stored row and byte limits use the quota errors in the table.

Action size and time limits fail the action. Hosted clients receive Lakebed action failed.. Read the deploy log for the detailed limit message. Local calls include the detailed message. An action failure does not undo outbound side effects. For example, Lakebed checks the result size after the action runs. Do not assume a failed action is safe to retry.

Checking usage

npx lakebed deploy prints the artifact, state, request, and mutation limits for the deploy it just published.

Run npx lakebed inspect <deploy-id-or-url> --usage to see stored rows and bytes, and today's request and mutation counts, beside their limits. Daily counts use UTC. Stored rows and bytes have no timed reset. Use --usage --json for the complete usage response, including recorded daily counters. For local development, omit the deploy target and use --port if needed. Local daily counts are not enforced.

npx lakebed inspect <deploy-id-or-url> prints per-read database metrics for the deployed capsule:

  read by_room: returned=20 read=20 bytes=4096
  read direct: returned=1 read=812 bytes=190334 full-scan

returned, read, and bytes are the counters behind the rowsReturned, rowsRead, and bytesRead rows in the table. A full-scan marker means the query had no usable index, and it is the usual cause of a rowsRead failure.

Notes

The hosted cloud also applies a short-window burst limit per client and a separate daily cap per client IP. Normal app traffic stays well under both. A load test does not.

npx lakebed dev does not apply the daily request and mutation quotas, so local iteration is not throttled. The per-handler database limits, action limits, value size limits, and stored row and byte limits apply locally, so local tests can catch these failures. Local wording differs: lakebed dev reports the stored limits as State row limit exceeded (16384). and State byte limit exceeded (1048576). instead of a 429.

See database.md for collect() and paginate(), storage.md for object storage errors, and reference.md for deploy behavior.