Getting started
This guide gets the runtime running locally and serves a tiny app from a
worker. You’ll need Bun 1.3+ installed.
Run the monorepo
Section titled “Run the monorepo”-
Clone and install:
Terminal window git clone https://github.com/djalmajr/buntime.gitcd buntimebun install -
Start the full dev stack — runtime, the core plugins, and the operator panel (cpanel) — from the repo root:
Terminal window bun devThis runs the runtime with
--watch. Usebun --watch, neverbun --hot: hot mode breaks timers/cron and leaks port bindings. -
Check it’s alive:
Terminal window curl http://localhost:8000/api/health
Deploy your first worker
Section titled “Deploy your first worker”A “worker app” is just a directory with an entrypoint. The runtime discovers it
from the directories listed in RUNTIME_WORKER_DIRS (PATH-style, separated by
:).
-
Create an app directory. Versioned, flat layout is the simplest:
Directoryapps/
Directoryhello@1.0.0/
- index.ts
- manifest.yaml
-
Write the entrypoint. A worker exports a default object — a
fetchhandler is the simplest form:// apps/hello@1.0.0/index.tsexport default {fetch(req: Request) {const url = new URL(req.url);return Response.json({ hello: "world", path: url.pathname });},}; -
(Optional) Describe how the worker runs. Without a manifest the defaults apply (ephemeral discovery, 30s timeout):
# apps/hello@1.0.0/manifest.yamlentrypoint: index.tsttl: 0 # 0 = ephemeral (spawned per request); >0 keeps it warmtimeout: 30smaxRequests: 1000 -
Point the runtime at the directory and start it:
Terminal window RUNTIME_WORKER_DIRS=./apps bun --watch apps/runtime/src/index.ts -
Call your app. An unscoped worker named
hellois served at/hello/:Terminal window curl http://localhost:8000/hello/# {"hello":"world","path":"/"}
Choose how the worker runs
Section titled “Choose how the worker runs”The ttl field defines the worker’s personality:
Spawned for each request and discarded afterward. Higher per-request
latency, zero idle cost. Best for stateless, lambda-style handlers.
Concurrency is bounded by RUNTIME_EPHEMERAL_CONCURRENCY (default 2);
overflow queues up to RUNTIME_EPHEMERAL_QUEUE_LIMIT (default 100),
then returns 503.
Kept warm and reused. The TTL is sliding — it resets on every request,
so the worker lives as long as traffic keeps arriving (or until
maxRequests). Best for apps with state, database connections, SSE, or
WebSocket.
Other entrypoint shapes
Section titled “Other entrypoint shapes”A worker’s default export can be a fetch handler, a routes object, or you
can ship an index.html for a static SPA:
export default { fetch(req: Request) { return new Response("ok"); },};export default { routes: { "/": new Response("Home"), "/api/posts/:id": { GET: (req) => new Response(`Post ${req.params.id}`), }, },};Set entrypoint: index.html in the manifest. The runtime serves the file
statically and injects <base href> so client-side routing works under the
app’s subpath. index.ts is not executed in this mode.
Where to next
Section titled “Where to next”- Understand what just happened: the worker pool and the runtime request pipeline.
- Add a capability to the runtime: write a plugin.
- Tune the runtime for an environment: environment variables.