Isolate Apps
Small handlers on absolutejs.ai that sleep when nobody is using them and wake on the next request. You pay for the requests and CPU they use, not for a server waiting around.
#When to use one
Isolate apps suit webhooks, small APIs and handlers that run for a moment and go quiet. They run with Web APIs only, so anything that needs Bun APIs, node: modules, native addons or the filesystem should stay a regular server. An app only runs this way if you declare it.
#Your first app
Declare the runtime in absolute.hosting.json:
{
"version": 1,
"runtime": {
"tier": "isolate",
"contract": "fetch-v1",
"entrypoint": "src/handler.ts"
}
}Then export a fetch handler from your entrypoint:
export default {
async fetch(request: Request, env: Env, ctx: Context) {
ctx.state.visits = (ctx.state.visits ?? 0) + 1;
return Response.json({ visits: ctx.state.visits });
}
};Publish as usual. The build bundles your app into one file. If it imports something an isolate can't run, the build fails and names each import.
#State and sleep
An app sleeps after hibernateAfterMs without a request, 5 minutes by default, and wakes on the next one. Health checks don't wake it.
- ctx.state is an object that survives sleep, restarts and new releases, up to 256 KiB as JSON. It is saved at most every 10 seconds while the app serves, so a crash can lose the last few seconds. Keep anything you can’t lose in a database.
- ctx.waitUntil(promise) lets work finish after the response is sent.
#Database and fetch
An isolate app has no network of its own, and the global fetch doesn't exist. It reaches the outside only through capabilities you declare. Each one appears on env only if it is declared:
{
"version": 1,
"egress": { "hosts": ["api.example.com"] },
"runtime": {
"tier": "isolate",
"contract": "fetch-v1",
"entrypoint": "src/handler.ts",
"timeoutMs": 5000,
"hibernateAfterMs": 60000,
"capabilities": {
"database": { "access": "read-write", "maxRows": 500, "timeoutMs": 2000 },
"fetch": {
"methods": ["GET", "POST"],
"requestsPerMinute": 120,
"maxResponseBytes": 524288
}
}
}
}export default {
async fetch(request: Request, env: Env) {
const { rows } = await env.database.execute(
'select id, name from products where id = $1',
[new URL(request.url).searchParams.get('id')]
);
const rates = await env.fetch('https://api.example.com/rates');
return Response.json({ rates: JSON.parse(rates.body), rows });
}
};- env.database.execute(statement, parameters) runs SQL and resolves to { rows }. Set up a database for the environment in the project’s Data settings first, or the release is refused. maxRows defaults to 100 (up to 1,000), and going over is an error rather than a cut-off result. timeoutMs defaults to 1 second.
- env.fetch(url, { method, headers, body }) resolves to { status, headers, body }, with body as text. It only reaches HTTPS on port 443, at hosts in egress.hosts, with the methods you listed. Private and internal addresses are refused. requestsPerMinute defaults to 60 (up to 1,000), and maxResponseBytes to 256 KiB (up to 1 MiB).
Storage and Sync capabilities aren't available to isolate apps yet, and a release that declares storage is refused.
#Limits
- Time: timeoutMs defaults to 1 second, from 25 ms up to 30 seconds per request.
- Memory: 64 MB for each app.
- Request and response bodies: 1 MiB each.
- Console: 100 lines a second, and 8 KiB a line. Extra lines are dropped and counted in your logs.
- Sleep: hibernateAfterMs can be set from 1 second to 1 hour.
#Logs and errors
Your project's logs show console output, starts and sleeps, failures, and any answer of 500 or above. The latest 500 lines are kept while the app sleeps. They don't survive a host restart or a move to another host, so send anything you need to keep somewhere of your own.
- 504 means the app didn’t answer within its time limit. The app is restarted.
- 503 means the app ran out of memory and was stopped. It starts again on the next request.
#Pricing
Isolate apps come with Pro and Scale, don't use your project slots, and aren't limited in number. Your billing page shows your use once you have any.
- Pro: 3 million requests and 25 CPU-hours a month.
- Scale: 20 million requests and 150 CPU-hours a month.
- Beyond that: $0.30 per million requests and $0.05 per CPU-hour. Apps keep serving, and the extra goes on your next bill.