Limits
The limits that apply on the free plan. We notify you before you approach or exceed one.
Every organization is on the Free plan today; the Pro column shows what the plan changes once self-serve billing opens. Every limit below is actually enforced by the service, with one exception: database size is measured and notified, but nothing rejects a write at the cap — what happens instead is described below.
| Item | Free | Pro | When exceeded |
|---|---|---|---|
| Requests | 100,000 a day | 10 million a month included | 429 budget_exhausted until 00:00 UTC. |
| CPU time | 1,000 seconds a day | 30 million ms a month included | 429 budget_exhausted until 00:00 UTC. |
| Worker memory | 512 MiB | 1 GiB | The worker is restarted and a line appears in runlot logs. |
| Projects per organization | 3 | 30 | 409 project_limit |
| Custom domains per project | 1 | 50 | 409 hostname_limit |
| Preview deployments per project | 2 | 10 | 409 preview_limit |
| Preview lifetime after the last request | 24 hours | 7 days | The preview and its database branch are deleted. |
| Database size | 500 MiB | 8 GiB | Writes keep working. We notify you at 90 %. |
| Backup retention | 7 days | 30 days | The oldest backups are deleted first. |
| File storage | 10 GiB | 100 GiB included | Free: 507 quota_exceeded. Pro: uploads keep working and the overage is billed. |
| File storage overage (Pro) | — | $0.015 per GiB-month above the included 100 GiB, on the month's average | Counts toward the spending limit. At the limit, uploads are refused with 507 quota_exceeded until the first of next month. |
Outgoing email (login emails and env.email.send) | 30 per hour per project | 300 per hour | Delivery is delayed. |
| Incoming email | 600 per hour per project | Same | The sending server is told to try again later. |
| Email log retention | 30 days | 365 days | The oldest entries are deleted first. |
| Log buffer | 2,000 lines / 1 MiB per project | Same | The oldest logs are dropped first. |
| Bundle upload | 64 MiB compressed | Same | bundle_too_large |
| Scheduled execution | 5 per organization | 250 | 400 cron_limit |
| Queues | 5 per organization, 5 per project; 10,000 operations a day per project | 25 per organization; 1,000,000 a day | 400 queue_limit at deploy; send throws QueueLimitError until 00:00 UTC. See Queues. |
| Vectors | 5,000 per project, up to 3,072 dimensions | 25,000 per project | upsert throws VectorsError (vector_limit) and writes nothing. A namespace grown past the bound is refused at query time. See Embeddings and vectors. |
| Build minutes (push to deploy) | 300 a month per organization | 3,000 a month | The build fails with minutes_exhausted; the previous deployment keeps serving. Resets on the first of the month, UTC. |
| Request rate | 60 per second per client (burst 120), 2,000 per second per project | Same | 429 with Retry-After. See Rate limits and DDoS. |
The database size limit is an operational limit, not an engine limit. Backups only carry the commits made since the previous one, so the cost of a backup follows how much you write, not how big the database is — that is what let the cap move from 150 MB to 500 MiB on the free plan and 8 GiB on Pro.
Nothing blocks a write at the cap today. At 90% we send a db_near_cap notification. See The size cap for details.
Scheduled execution also caps the wall-clock time of one run, and that cap comes from the schedule rather than the plan: 30 seconds under an hour, 5 minutes at an hour or longer. See Scheduled execution.
Even after you go over the file storage quota, reads, listings, and deletes keep working. Only writes are restricted, so that you can bring usage back down.
Worker memory is capped per project, and the cap covers everything that process holds at once — your code, the response bodies it is building, and the database engine's own working memory. Go past it and the process is stopped and started again; requests in flight at that moment fail, and the next request runs on a fresh process. runlot logs shows the line worker exceeded the memory cap (512 MiB) and was restarted, so a restart caused by memory is not something you have to infer from a crash. The usual cause is holding a whole file or a whole query result in memory — stream it instead.
When you hit the project limit
Error [project_limit] the free plan allows up to 3 projects (currently 3)The response body includes the current limit and usage.
{ "error": { "code": "project_limit", "message": "…", "details": { "limit": 3, "count": 3, "plan": "free" } } }Clean up projects you no longer use, or tell us at [email protected] if you need more.
Notifications
When you approach or exceed a limit, we notify the organization's admin.
| Notification | When |
|---|---|
project_limit | A project creation was rejected by the limit |
db_near_cap | The database reached 90 % of the cap |
storage_near_quota | File storage reached 90 % of the quota |
storage_quota_hit | An upload was refused at the file storage quota (Free only — on Pro the refusal is the spending limit, and budget_exhausted is its notification) |
backup_failed | A backup failed after retries |
cron_failed | A scheduled run did not complete |
build_failed | A push-to-deploy build did not produce a deployment (at most once an hour per repository) |
restore_done | A restore finished |
dns_publish_stalled | A DNS zone has not been published for five minutes |
The same kind of notification is sent at most once every 24 hours. This keeps repeated overruns from flooding your inbox.
You can review recent notifications on the organization page in the dashboard. For accounts with no email address on file, the dashboard is the only place notifications appear.
Requests and CPU time
Both are counted per organization, not per project, and both are enforced.
- On the free plan, passing the daily request or CPU budget stops serving for the rest of that UTC day — it never turns into a charge. Requests are answered with
429 budget_exhaustedand aRetry-Afterheader pointing at the next 00:00 UTC. - On Pro, usage past the included amount is billed, and a monthly spending limit is on by default. Reaching that limit produces the same
429.
Two things are deliberately left open. Database connections through psql, runlot pg shell, and drivers are not blocked by this limit, so you can always get your data out; and requests that serve static assets are counted like any other request — so the free allowance covers fewer page loads than the number alone suggests.
Enforcement is not instantaneous. Usage is reported once a minute and the decision reaches the traffic frontend on the next map poll, so serving can continue for two to three minutes past the limit. That grace is given, not billed.
runlot usage
runlot usage --hours 168The charts on the project overview
The project's first screen draws what the service stores rather than summarizing it into one number. Six plots, one measure each, over a window you pick: 24 hours, 7 days, or 30 days.
| Chart | What it is | Cap line |
|---|---|---|
| Requests | Round trips the frontend finished. Rejections — unknown hostname, suspended, not deployed — are not here, because none of them ran your code. | Free: 100,000 a day |
| CPU time | The same population as requests. | Free: 1,000 seconds a day |
| Database size | The last size the node observed, not a sum. | Your plan's cap |
| Storage operations | Object reads and writes. | — |
| 5xx errors | Requests that answered 5xx, with the rate underneath. A 404 is not here: that is the client's mistake, and an error chart that spikes when somebody mistypes a URL is not worth watching. | — |
| Response time | The median, with the slowest 1% named underneath. Waking a parked project is inside this number — the time you experienced is the time that is drawn. | — |
A few things about how they are drawn are worth knowing, because they are the difference between a chart and a chart you can trust.
- Over 7 days and 30 days the counting charts fold to whole days. The caps that exist are daily, so a dashed line for a per-day limit drawn across per-hour bars would be a line the bars could never touch. Database size folds by taking the last observation of the day, never by adding: adding a size per hour counts the same store twenty-four times.
- A missing hour stays a gap. The service does not write a row for an hour with no report, and the chart does not fill one in. A flat zero and "no report came in" are different facts, and a project whose node went quiet should look like it.
- Vertical dashed lines are deployments. Hover one to see which version. A step in the response-time chart that starts exactly at a marker is usually the release, and that is the question the marker exists to answer.
- Response time never folds. A percentile cannot be added, so a day's median is not a function of its hours' medians. That chart keeps hourly points at every range, and the headline number is computed by the server from the whole window at once.
- The error rate needs a denominator. With no requests in the window it shows a dash, not
0.00%.
Every plot has a "View data table" toggle underneath with the same numbers as text.
How far back you can restore
Backups are taken often and each one carries only what changed, so a restore can land on any point in the window: seven days on Free, thirty on Pro. Retention differs by plan because of the cost of keeping copies off-site.
A restore lands on any commit inside the window — a backup, a commit id, or a time. See Backup and restore.
Regions
There is one region today: Seoul. Your application and your database run on the same machine, so an env.db call needs no network round trip.