ermahgerd provides a native, organization-metered image transformation capability to Workers. A deployment declares an Images binding in the same place it declares R2, D1, KV, queues, and assets:
{
"images": {"binding": "IMAGES"}
}Wrangler includes the binding in the immutable Worker version. At invocation
time the trusted runtime injects the public builder interface as env.IMAGES
and retains the private service credential. Image bytes and credentials are
never placed in deployment metadata.
Transform an image
The binding follows Cloudflare’s stream-oriented Images API:
const source = await env.MEDIA.get("originals/photo.jpg");
if (!source) return new Response("Not found", {status: 404});
const result = await env.IMAGES
.input(source.body)
.transform({width: 1200, height: 800, fit: "scale-down"})
.output({format: "image/webp", quality: 86});
return result.response();The current surface accepts scale-down, contain, cover/crop, and pad
fit modes and emits AVIF, GIF, JPEG, PNG, or WebP. Width and height are optional.
The service preserves aspect ratio where the fit
mode requires it and re-encodes output without source metadata.
Input is bounded at 128 MiB, decoded images at 100 megapixels, and either output dimension at 12,000 pixels. These are input ceilings, not a promise that every combination fits the execution budget described below. Rejected headers, invalid options, and admission refusals do not consume a transformation allowance. The photo publishing example applies its own tighter 64-megapixel editorial policy before invoking Images.
Shared capacity and failure isolation
Images accepts parallel transformations without decoding them in its API process. Each upload first reserves an admission ticket, then streams to private disk staging. Header inspection runs in a limited child process; its dimensions and requested output determine the memory reservation for decoding and encoding. The same reservation becomes the executor’s Linux address-space limit. A child that cannot allocate memory or finishes too slowly fails its own operation; the API does not need to restart. Executors are fresh processes drawn from a fixed number of execution slots, not per-Worker services.
The compact and public profiles use the following defaults:
| Control | Default |
|---|---|
| Simultaneous executors | 2, at most 1 per organization |
| Total executor memory reservations | 1 GiB, inside the existing 1.5 GiB service ceiling |
| Admitted requests, including upload and response | 12 total, at most 4 per organization |
| Capacity wait | 30 seconds per stage |
| Header inspection / transform deadline | 5 / 20 seconds |
| Upload / response transfer deadline | 30 / 30 seconds |
| Encoded output ceiling | 64 MiB |
Eligible tenants take execution slots in arrival order. A smaller operation from another organization may run alongside a large one when its reservation fits; one organization cannot occupy both executors. This is bounded local fairness, not dedicated CPU or memory per tenant and not a hard latency guarantee.
Temporary saturation returns 503 capacity_busy with Retry-After: 2 on the
private binding transport. Use bounded retries with backoff. A transform that
exceeds its individual memory or time budget returns 422 execution_budget or
422 execution_deadline; replaying the same operation is not a capacity fix.
The Worker binding surfaces these failures as exceptions. Accepted execution
attempts consume an operation even if the codec later fails. No per-image CPU
charge is inferred from shared service telemetry.
The photo application’s object-upload path has a separate known compatibility boundary: a fully buffered value larger than workerd’s 32 MiB RPC-message limit can fail before Images is called. Images admission does not remove that transport limit; large-object binding streaming needs separate work.
Quotas and operations
Open Media → Images in the tenant console to inspect the capability:
- Overview shows transformation rate, observed p95 duration, unsuccessful operations, and the authoritative monthly allowance. Rejected inputs and quota enforcement are included in unsuccessful operations; these are not all platform failures.
- Workers lists the active Images bindings and links to each Worker and deployed version. Add or remove bindings through Wrangler.
- Activity shows the latest 200 organization-scoped transformations in the selected window, paginated in the browser, with outcome, duration, and trace identity. Image content is never included.
Charts and activity are derived from retained operation logs, not the monthly quota ledger. A quiet window has a zero operation rate; duration is only shown when observations exist. Telemetry unavailability is reported as an error, not a healthy zero. The time range, selected tab, filters, and pagination can be bookmarked or shared within the organization.
Every accepted transformation reserves one idempotent monthly operation at the trusted Images service boundary. Free, Standard, and Enterprise tiers include 1,000, 25,000, and 100,000 transformations per month respectively. The tenant console reports this under Usage & limits → Images; platform operators can apply an explicit support override without changing the organization’s tier.
This is an image transform primitive, not durable image storage. Store source
and derived objects in R2, and use versioned keys when queue retries or concurrent
edits can race. photo-gallery shows
that composition.