Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Free-plan limits

This page lists the Cloudflare Workers Free plan limits that matter to an Ocre app, as published by Cloudflare in September 2026, with the source of each value and what Ocre does about it.

Before you start

  • The values apply to an app deployed with ocre deploy on a Cloudflare account without Workers Paid. ocre dev (local workerd) enforces none of them.
  • Most limits are per account and per day (reset at 00:00 UTC), shared by every Worker of the account, not per app.
  • Cloudflare changes its limits. Each row links to the page the value was read from (September 2026); check it before relying on a number. For how an app spends these budgets and a worked daily example, see Cost model.

Workers

LimitFree plan (September 2026)SourceWhat Ocre does
Requests100,000 a day per account; past it, error 1027 until midnight UTCWorkers limitsFiles in public/ are served by Workers Static Assets, free and unlimited, without running the Worker. A WebSocket connection counts once, not per message.
Static asset requestsfree and unlimited; 20,000 files per version, 25 MiB per fileStatic assets billing, limitsassetsDirectory: "public" in wrangler.config.ts from ocre new. Turning on Workers Cache makes asset requests count (Workers Cache pricing); Ocre does not enable it.
CPU time10 ms per invocation (HTTP request, Cron Trigger, queue consumer batch); occasional overruns are tolerated per isolate, frequent ones end in error 1102CPU timeWaiting on D1, KV, R2 or fetch does not count. Passwords are hashed with WebCrypto PBKDF2 (native, about 5.5 ms) instead of WebAssembly; R2 downloads are streamed without passing through WebAssembly; GraphQL (20 to 60 ms at instance start) is opt-in.
Memory128 MB per isolate, shared by the concurrent requests it runsMemoryUploads are read into memory: generated attachment rules default to 10 MB per file and forms refuse bodies over the sum of their files’ limits plus 1 MB (413).
Request body size100 MB (Cloudflare Free plan); larger bodies get 413 before the Worker runsRequest limitsKeep max_bytes of attachment Rules in the tens of MB.
Worker size64 MiB uncompressedWorker sizeRelease builds use opt-level = "z" and LTO; the blog starter measured 420 KB of WebAssembly (130 KB gzipped). GraphQL adds about 1.1 MB.
Startup time1 second to evaluate the global scopeStartup timeThe blog starter measured 4 ms. Translations are parsed on first use, not at startup.
Subrequests50 per invocation; 1,000 to Cloudflare servicesSubrequestsGenerated pages run one D1 query (lists, show) or a few (create/update add one check per unique field and reference). find_many loads many rows in one query per 100 ids instead of one per row.
Variables and secrets64 per Worker, 5 KB eachEnvironment variablesOcre needs at most four: SECRET_KEY_BASE, MAIL_FROM, MAIL_ADAPTER, RESEND_API_KEY (plus ALLOWED_ORIGINS when used).
Cron Triggers5 per accountAccount plan limitsocre g schedule warns when the app has more than 5; run several tasks from one cron.
Workers Logs200,000 log events a day, kept 3 daysWorkers Logs pricingOcre logs only errors and job/cron outcomes ([ocre ...] lines); the log mail adapter prints whole emails, so do not use it in production.

D1 (database, binding DB)

LimitFree plan (September 2026)SourceWhat Ocre does
Rows read5 million a day (rows scanned, not rows returned)D1 pricingGenerated lists are paginated (Page: default 50, at most 100) and ordered by the primary key; every references column gets an index, every unique field a unique index.
Rows written100,000 a day; each index on a written column adds one row writtenD1 pricingSessions live in an encrypted cookie (no rows); API keys update last_used_at at most once an hour.
Storage5 GB per account; 500 MB per database; 10 databases per accountD1 limitsOne database per app (database_name = app name). Files go to R2, not D1.
Queries per invocation50D1 limitsfind_many instead of find in a loop.
Bound parameters100 per queryD1 limitsfind_many splits ids into chunks of 100.
Row or string size2,000,000 bytes (2 MB)D1 limitsKeep large text and files in R2 (attachment fields).
SQL statement length100 KBD1 limitsQueries use ?N placeholders, never inlined values.
NumbersD1 returns numbers as JavaScript numbers (exact up to 2^53 - 1)ocre::MAX_SAFE_INTEGERGenerated integer validations reject values beyond ±9,007,199,254,740,991.

Queues (background jobs, binding JOBS)

LimitFree plan (September 2026)SourceWhat Ocre does
Operations10,000 a day; a delivered message costs 3 (write, read, delete), each retry 1 more read, a dead-lettered message 1 more write; each 64 KB of a message counts as one operationQueues pricingOne message per job: about 3,300 jobs a day without retries.
Retention24 hours, not configurableQueues pricingRetries (30 s, 1 min, 3 min, 9 min, 27 min) end about 40 minutes after the first failure; the dead-letter queue <app>-jobs-failed keeps failures 24 hours.
Message size128 KB (1 KB = 1,000 bytes, including about 100 bytes of metadata)Queues limitsenqueue refuses messages over 127,000 bytes: cannot enqueue a <n> byte message: Cloudflare Queues messages hold 128 KB at most. Fix: store large data in D1 or R2 and put its id in the job.
Delay24 hours, on send and on retryDelay messagesenqueue_in refuses longer delays and names the fix (a scheduled task, or a due time in D1).
Batchesup to 100 messages, 60 s waitQueues limitsmax_batch_size = 10, max_batch_timeout = 5: each consumer run handles up to 10 jobs within one invocation’s 10 ms of CPU.
Retriesup to 100Queues limitsmax_retries = 5.
Queues10,000 per accountQueues limitsTwo per app: <app>-jobs and <app>-jobs-failed, created by ocre deploy.

R2 (files, binding STORAGE)

LimitFree plan (September 2026, per month)SourceWhat Ocre does
Storage10 GB-month (Standard storage class)R2 pricingFiles of deleted and replaced records are deleted; files of rows removed by ON DELETE CASCADE are not.
Class A operations1 million a month (PutObject, ListObjects, …)R2 pricingOne per upload.
Class B operations10 million a month (GetObject, HeadObject, …)R2 pricingOne per download or 304.
Deletes, egressfreeR2 pricing
ActivationR2 must be enabled once in the dashboard, which asks for a payment method even for the free tierR2 pricingocre deploy stops with a hint when the account lacks it (API code 10042).

Past the free tier, R2 bills (October 2026, R2 pricing): Standard storage $0.015 per GB-month, Class A operations $4.50 per million, Class B $0.36 per million; egress stays free. Rounding is up to the next unit (1.1 GB-month bills 2). Video is where this shows: a 4K video is several GB, so a few of them pass the 10 GB. Examples for Standard storage: 100 GB kept a month is (100 - 10) × $0.015 = $1.35; 1 TB is $14.85. A 5 GB file sent with storage::multipart_uploads is 514 Class A operations (512 parts, the create and the complete), well within the free million. Keep storage in check by deleting what users no longer need (a schedule that deletes files older than N days, storage::purge_unattached for abandoned uploads).

Beyond the Worker: programs and heavy compute

A Worker runs WebAssembly in a V8 isolate: it cannot start a program (ffmpeg, ffprobe, ImageMagick, Python), has no filesystem, and gets 10 ms of CPU per invocation on the free plan. Work of that kind runs elsewhere and reports back, with ocre g external_job (Run work on another service):

  • a GPU service billed per second (RunPod serverless, Modal, Replicate…) for AI inference and video encoding;
  • Cloudflare Containers, which run any image (ffmpeg included) next to the Worker, on the Workers Paid plan;
  • any server or function of your own that answers HTTP.

Probing a video (duration, resolution) to price it belongs there too: the service reports it in an event’s output. Cloudflare’s Media Transformations and Stream (paid) cover some video work without a container.

Workers KV (cache, binding CACHE)

LimitFree plan (September 2026)SourceWhat Ocre does
Reads100,000 a dayKV limitsocre::cache::fetch reads once per call.
Writes1,000 a day (to different keys); 1 per second to the same keyKV limitsThe scarcest free resource: each miss of fetch, each write and each delete is one. Past a limit, fetch logs [ocre cache] ... failed and computes the value instead of failing.
Deletes, lists1,000 a day eachWorkers pricing, KVocre::cache::delete is one delete; Ocre never lists.
Storage1 GB per account and per namespaceKV limitsValues are JSON; keep them small.
Key size512 bytesKV limitsLonger or empty keys are refused with an error naming the fix.
Value size25 MiBKV limits
Minimum TTL60 seconds (expirationTtl)Write key-value pairsfetch and write refuse shorter TTLs (ocre::cache::MIN_TTL).
Consistencywrites can take up to 60 seconds to be visible elsewhereWrite key-value pairsNot for data that must be read back immediately.

Durable Objects (realtime, binding CHANNELS)

LimitFree plan (September 2026)SourceWhat Ocre does
Requests100,000 a day; incoming WebSocket messages count 1 per 20; outgoing messages are freeDurable Objects pricingOne request per connection (and reconnection) and one per broadcast. Browsers only listen, so they send no messages.
Duration13,000 GB-s a day (at 128 MB, about 28 hours awake)Durable Objects pricingOcreChannel uses the WebSocket Hibernation API: objects are only billed while handling a connection or a broadcast.
Storage backendSQLite-backed classes onlyDurable Objects pricingnew_sqlite_classes = ["OcreChannel"]; the channel stores nothing.
Classes, storage100 classes per account, 5 GB storage per accountDurable Objects limitsOne class, no storage.
Received WebSocket message size32 MiBDurable Objects limitsClient messages are ignored.

Email

LimitFree plan (September 2026)SourceWhat Ocre does
Email Routing (receiving)free and unlimited; 200 routing rules per domain, 200 destination addresses per account, 25 MiB per incoming messageEmail Service pricing, limitsocre g mailbox wires the Worker’s email event; each received message is one invocation with 10 ms of CPU.
Email Service (sending, MAIL_ADAPTER = "cloudflare")on Workers Free, only to verified destination addresses of the account (free); any recipient needs Workers Paid (3,000 a month included, then $0.35 per 1,000); 5 MiB per message, 50 recipientsEmail Service pricing, limitsFine for mail to yourself; use Resend for sign-up and password-reset mail on the free plan.
Resend (sending, MAIL_ADAPTER = "resend")100 emails a day, 3,000 a month (each recipient counts; received emails too); up to 3 verified domainsResend quotasdeliver_later retries provider failures through the jobs queue.

What happens at a limit

ResourceBehavior past the limitOcre’s handling
Worker requestsError 1027 page (or the request bypasses the Worker if its route fails open) until 00:00 UTCnone possible inside the app
CPUError 1102 “Worker exceeded resource limits” when overruns become frequentDesign: I/O in handlers, heavy work in jobs
D1 rowsQueries failErrors become 500 responses, logged with [ocre]
KVOperations of that type fail until 00:00 UTCfetch falls back to computing the value
QueuesSends failenqueue returns the error; the handler decides
Durable ObjectsRequests failBroadcast failures are logged ([ocre realtime] broadcast to <channel> failed: ...) and the request still succeeds

See also