The setup hook
Owly builds a VM snapshot for each function ahead of time, then restores from that snapshot on a cold start instead of booting from scratch. The setup hook lets you run work while the snapshot is being built — so its results are captured in the snapshot and never recomputed on a real request.
Register it with the SDK:
app.onSetup(() => {
// runs once, while the snapshot is built
});
owly.OnSetup(func() {
// runs once, while the snapshot is built
})
Whatever you write to module- or package-level state inside the hook is baked into the snapshot. On every cold start that state is already there.
What to put in it
Idempotent, pure-compute warmups whose results live in module/package-level state:
- Pre-build lookup tables, parse static config, compile regexes.
- Decode and cache files read from the project's static files.
- Trigger any first-time-expensive initialization so the first real request doesn't pay for it.
let template = '';
app.onSetup(() => {
template = renderLayout();
});
app.get('/api/page', (c) => c.html(template));
What it must not do
The hook runs at build time — there is no real request and no client:
- No network calls. Outbound requests at build time won't reflect request-time conditions.
- No external writes. Don't touch databases or external state.
- No per-request data. There's no request context; the hook takes no arguments by design.
Semantics
- You can register multiple hooks; they run in registration order.
- Keep each hook idempotent — it represents "compute once, snapshot the result."
What it is not: cross-request caching
The setup hook is not a place to cache data across requests at runtime. Each request runs a fresh instantiation, so in-memory state written while handling one request does not survive to the next. The hook only bakes state computed at build time.
Durable, cross-request caching is a separate, future capability — it belongs in host-mediated storage (a KV layer), not the setup hook. Until then, treat the hook as "compute-once, snapshot-it."