Workers
Run JavaScript on a request, a schedule, or an object event.
A worker is an ES module. You deploy it from the console and it runs on AnythingCloud's nodes, not on your servers. Each worker has its own address, versions, environment, bucket bindings, and triggers. A trigger decides when the code runs: an HTTP request, a timer, or a change to an object in a bucket.
The worker page has six tabs: Overview (the default), Metrics, Code, Triggers, Logs, and Settings. Each worker runs in its own process and stays warm between deploys and node restarts. A stopped or daily-recycled worker can return a 503 while it starts. New workers are placed on the least-loaded configured node.
Create and deploy
- Open Workers and choose Create worker.
- Enter a name and a worker id. The id is fixed and is used for the
default
ID.workers.anything-cloud.comaddress. You can point the worker at a different label under the same suffix later in Settings → Address. - Choose an address: AnythingCloud address, No address, or Your domain for a root domain you have already verified.
- Pick a template, then edit the code. A worker exports
fetch,scheduled, orevent. - Choose Create worker. The first version is stored and the deploy is queued for a node.
The Code tab is the editor, with live syntax highlighting.
Paste code, choose Upload .js, or drop a .js /
.mjs file onto the editor to load it. Press
Ctrl + Enter or Cmd + Enter to open the
deploy dialog, add a changelog line, and save. A deploy of new code stores
an immutable version; deploying code identical to an existing version reuses
that version. The last 20 are kept. The Versions page
lists every kept version; roll back to the previous one from the page
header, or pick any older version from its row, without re-uploading
code.
A deploy starts the new version beside the one that is serving, waits for it to pass a health check, and then sends new requests to it. Requests that are already running finish on the old version. If the new version does not become healthy, the old version keeps serving.
Addresses and custom domains
Every worker can have one generated address:
ID.workers.anything-cloud.com. Choose No
address when the worker only runs on schedules or object events;
you can add a host later.
To use your own hostname, verify the root domain under
Networking → Domains, then open
Settings → Address or Triggers → Add
host. Pick the domain and enter a label such as www,
api, or @ for the root. The host is added to the
worker's trigger list and appears in Networking → Hosts
next to bucket hosts. The Settings picker can also change which domain and
subdomain the worker advertises.
Point a CNAME at edge.anything-cloud.com and set
it to DNS only. The edge controller issues the certificate
after DNS resolves to it, so there is no separate TLS step. A custom host
that shows Setting up is waiting for DNS or the
certificate.
When a custom host becomes active it becomes the worker's primary address, and the console shows it instead of the generated address. An existing generated host stays in the trigger list as a fallback. You can remove it once a custom host exists; when you remove the last custom host, the generated address is created again if it is missing.
Requests reach the worker with their method, path, query string, and body.
The edge sets X-Forwarded-For, X-Forwarded-Proto,
X-Forwarded-Host, and X-Real-IP, preserves the
Host header, and strips internal invocation headers. The
worker's status code, headers, and body are returned to the caller.
export default {
async fetch(request) {
const url = new URL(request.url);
const agent = request.headers.get("user-agent") || "unknown";
return Response.json({
path: url.pathname,
query: Object.fromEntries(url.searchParams),
agent,
});
},
};
Triggers
Open Triggers to see everything that invokes the worker in one list. Each row is one of three types: Request host, Schedule, or Object event. Schedules and object events have an on/off switch; request hosts can be copied or removed.
Request hosts
A request host sends HTTP requests to the worker's fetch
handler. The generated address and each custom hostname are request
triggers. A host shows Active once it is routed and
Setting up while DNS or TLS is pending. Copy its URL from
the row, or remove it when you no longer need it.
Schedule triggers
A schedule runs the scheduled handler. Choose an interval
from five minutes to one day, or enter a five-field cron expression
(minute hour day month weekday) evaluated in the time zone you
select.
export default {
async scheduled(event) {
// event.scheduledTime is the run time as an ISO 8601 string.
const response = await fetch("https://example.com/ping");
console.log("ping", response.status);
},
};
Schedules are best-effort and can start a few seconds late under load. Runs do not overlap: the next run starts after the current one finishes, so keep the handler idempotent and short.
Object event triggers
An object event runs the event handler when an object changes
through the console in a bucket you own. Choose the bucket, one or more
event types (created, updated, deleted,
renamed, or restored), and an optional key prefix.
Reads never fire an event. Writes made through the S3 API, a presigned URL,
or env.BUCKET.put do not fire console object events.
The handler receives one event object:
export default {
async event(event, env) {
// event.type "created", "updated", "deleted", "renamed", or "restored"
// event.bucket the bucket name
// event.key the object key
// event.size bytes, currently 0 for console events
// event.etag the object ETag, currently empty for console events
// event.oldKey the previous key, for a rename
// event.eventTime when the change happened, as an ISO 8601 string
console.log(event.type, event.bucket, event.key);
},
};
A prefix such as uploads/ keeps a busy bucket from waking the
worker for every file. The event is delivered after the bucket change
completes, so a slow handler never delays the upload or delete that
produced it.
Environment
Open Settings → Environment and add key/value rows.
Values arrive in the handler as env.KEY:
export default {
async fetch(request, env) {
const key = env.API_KEY;
if (!key) return new Response("Not configured", { status: 500 });
// use the key
},
};
Keys use uppercase letters, digits, and underscores, and start with a letter. Values are encrypted at rest and injected into the worker process; saving a variable applies to the worker without a new code deploy. Secret values are hidden after creation and shown once in the response that creates them. Turn off the secret option for a value you want to see in the list.
For values that change at runtime, create a
KV namespace and bind it in
Settings → KV namespaces. The alias you choose becomes
env.KV or another name.
Bucket helpers
Bind a bucket under Settings → Buckets and it appears
as env.ALIAS (the default alias is BUCKET). The
binding is tied to one bucket, and the runtime signs requests for it, so no
endpoint, access key, or private address is exposed to your code.
| Call | Does |
|---|---|
env.BUCKET.get(key) | Reads an object. Returns null when the key is missing. The result has size, etag, text(), json(), arrayBuffer(), and body. |
env.BUCKET.head(key) | Metadata only: key, size, and etag. Returns null when the key is missing. |
env.BUCKET.put(key, body, options) | Writes an object. Pass a string, stream, or buffer, and set the type with { httpMetadata: { contentType } }. |
env.BUCKET.delete(key) | Deletes an object. Deleting a missing key is not an error. |
env.BUCKET.list({ prefix, limit, cursor }) | Lists objects under a prefix, up to 1,000 per page. Returns { objects, truncated, cursor }, where each object has key, size, and etag. |
env.BUCKET.url(key) | The public URL for a key on the bucket's CDN host. |
Returning a file
For a public bucket, do not stream object bytes through the worker. Redirect to the bucket URL so the download is served from the edge cache, with range requests and caching handled for you. Public bucket reads are edge-cached for up to five minutes, so an overwrite or delete can take that long to appear:
export default {
async fetch(request, env) {
const key = new URL(request.url).pathname.slice(1);
if (!key) return new Response("Missing key", { status: 400 });
const object = await env.BUCKET.head(key);
if (!object) return new Response("Not found", { status: 404 });
return Response.redirect(env.BUCKET.url(key), 302);
},
};
The redirect hands the download to the bucket host; the browser address bar
changes to that host. env.BUCKET.url(key) returns the public URL; it
does not grant access to a private bucket. For a private object, read it
with get and return a Response.
Receiving an upload
A worker can be the upload endpoint for a bucket: accept a multipart form, write the file into the bucket, and answer with the public URL.
export default {
async fetch(request, env) {
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const form = await request.formData();
const file = form.get("file");
if (!file || typeof file === "string") {
return new Response("Missing file", { status: 400 });
}
const key = `uploads/${crypto.randomUUID()}-${file.name}`;
await env.BUCKET.put(key, file.stream(), {
httpMetadata: { contentType: file.type || "application/octet-stream" },
});
return Response.json({ key, url: env.BUCKET.url(key) }, { status: 201 });
},
};
Route it to a hostname such as upload.example.com and the form
posts straight to the worker. The bridge caps a single put at
10 MB, and the bucket's public/private setting still applies. Plan quotas
are enforced on console uploads; S3 API and worker writes are not capped by
the plan, so keep them within the plan you are paying for.
Outbound requests
Workers can call public HTTP and HTTPS endpoints with fetch,
which is useful for sending a webhook or calling another API:
export default {
async fetch(request, env) {
const response = await fetch("https://example.com/hooks/orders", {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${env.WEBHOOK_TOKEN}`,
},
body: JSON.stringify({ event: "order.created" }),
});
return Response.json({ delivered: response.ok });
},
};
Public hosts on ports 80 and 443 are reachable. Private, loopback, link-local, and LAN addresses are blocked by the node firewall, including cloud metadata endpoints. Use a bucket binding for object storage; S3 credentials are not available to worker code.
Logs and metrics
console.log output and uncaught errors appear on the worker's
Logs tab. Filter messages, select a level, then copy or
download the result. Each worker keeps the newest 2,000 lines for 7 days;
older lines are deleted automatically. Turn off Save logs
to discard new output without clearing what is already stored, or use
Clear to remove the stored lines. An uncaught error in a
request handler returns a 500 to the caller and is recorded in the log.
Metrics
The Metrics tab covers 24 hours, 7 days, or 30 days. It shows invocations, errors and error rate, average duration, and egress, each compared with the previous window of the same length. A source breakdown separates requests, schedules, and object events, including each source's error rate and average duration.
The edge map shows where traffic enters. The edge currently runs in Columbus, US, so entries appear there on the map.
Billing
Workers are billed by running time, not per request. The base hourly rate applies to a 128 MB worker, and the price scales with the Memory you choose in Settings:
| Memory | Hourly price |
|---|---|
| 64 MB | 0.5x the base rate |
| 128 MB (default) | The base rate |
| 256 MB | 2x the base rate |
| 512 MB | 4x the base rate |
The default base rate is $0.0025 per hour for 128 MB. A 64 MB worker costs half that, while 256 MB doubles it and 512 MB quadruples it. The operator can change the base rate. Bucket storage and KV namespace storage are billed separately. The Billing page has a monthly budget with an optional hard cap.
Paused workers do not accrue runtime. Pausing an AnythingCloud account stops its workers and triggers until the account is resumed. The Billing page shows running workers, hours this month, and the worker share of the current bill.
Limits
| Limit | Value |
|---|---|
| Code format | ES module exporting fetch, scheduled, and/or event |
| Bundle size | 1 MiB |
| Request body | 10 MB |
| HTTP edge timeout | 30 seconds |
| Environment value | 8 KB per variable |
| Environment variables | 50 per worker |
| Request hosts | 20 per worker |
| Memory | 64, 128, 256, or 512 MB (default 128 MB) |
| Request timeout | 1, 5, 10, or 30 seconds for scheduled and event runs (default 10 seconds); HTTP requests use the 30-second edge timeout |
| Versions kept | 20 |
| Schedules and object events per worker | 20; request hosts are not counted |
| Workers per member | 10 by default |
| Log lines kept | 2,000 per worker |
A run that exceeds its memory cap is stopped and counted as an error.