Skip to content

Repository files navigation

OpenWorkers runner

OpenWorkers is a runtime for running javascript code in a serverless environment.

This runner manages instances of OpenWorkers Runtime.

Usage

Build

One JavaScript engine per build, and wasm on top of it if the runner should also serve components. Selecting no backend, or two JavaScript engines, is a compile error.

cargo build --release --features v8,wasm   # recommended for production
cargo build --release --features jsc
cargo build --release --features quickjs
cargo build --release --features boa
cargo build --release --features wasm

A worker goes to the backend its code type names, so a build carrying both serves JavaScript workers and components from the same process.

Every backend serves fetch and, through Event::Task with a schedule source, scheduled. Bindings are supported per type: the runner refuses a worker that declares a binding its backend cannot serve, naming the types, rather than handing the guest an undefined env.ASSETS.

Backend Feature Code type Snapshot / code cache env Bindings Known limitations
V8 v8 javascript, snapshot yes yes all but images no images handler on any backend; the only backend with an isolate pool, warm reuse and websockets
JSC jsc javascript no yes none links the system JavaScriptCore; no websockets; a fresh context per request
QuickJS quickjs javascript no no none no env, no websockets; a fresh runtime per request
Boa boa javascript no no none no env, no websockets; a fresh context per request
WASM wasm wasm no yes kv, database, storage wasi:http/proxy components only; env arrives as WASI vars, not env; no assets or worker bindings

The wasm guest reaches its bindings through the openworkers:bindings WIT package rather than an env object: every call names its binding, and the runner resolves that name against the worker's bindings.

Nova is not selectable: it wires no operations handler, so a guest has no fetch, and nova_vm pulls a temporal_rs that does not build against the ICU version v8 152 forces in a shared lockfile.

Optional on top of a backend: database (default), telemetry, and multiplexing (v8 only, ignored elsewhere).

Snapshot the runtime (V8 only)

cargo run --features v8 --bin snapshot

Prepare the database

CREATE USER openworkers WITH PASSWORD 'password';
CREATE DATABASE openworkers WITH OWNER openworkers;

Create .env file

DATABASE_URL='postgres://openworkers:password@localhost:5432/openworkers'
NATS_SERVERS='nats://localhost:4222'

Environment Variables

Required

Variable Description
DATABASE_URL PostgreSQL connection string
NATS_SERVERS NATS server URL

Networking

Variable Default Description
WORKER_DOMAINS workers.rocks Comma-separated list of worker domains for internal routing
HTTP_POOL_MAX_IDLE_PER_HOST 100 Max idle HTTP connections per host (for worker fetch())

Code cache

Holds V8 code caches and precompiled wasm components, so a worker version is compiled once instead of on every cold start.

Variable Default Description
CODE_CACHE_MAX 5000 Max entries in the in-memory LRU
CODE_CACHE_MAX_BYTES 536870912 Max total bytes in that LRU, whichever binds first

SNAPSHOT_CACHE_MAX and SNAPSHOT_CACHE_MAX_BYTES are still read when the CODE_CACHE_* name is unset, with a warning.

V8 Runtime

Variable Default Description
V8_EXECUTE PINNED Execution mode: PINNED, POOLED, or ONESHOT
WORKER_POOL_SIZE CPU cores Number of V8 worker threads
MAX_QUEUED_WORKERS pool × 10 Max queued tasks before backpressure
WORKER_WAIT_TIMEOUT_MS 10000 Timeout (ms) waiting for a worker slot

V8_EXECUTE modes:

PINNED (default)

Thread-local isolate pools — each thread maintains its own pool of V8 isolates, keyed by tenant (user_id). Zero cross-thread contention. Multiple isolates can exist per tenant for concurrent requests. Includes backpressure via per-thread queue with configurable size and timeout.

A new V8 context is created per request, so no JS state leaks between requests. The isolate (engine, heap, GC) is reused to avoid the allocation cost.

POOLED

Single global LRU pool shared across all threads, protected by a mutex. Isolates are keyed by worker_id. Simpler model but higher contention under load since all threads compete for the same lock.

ONESHOT

Fresh V8 isolate per request, destroyed after each response. No reuse, no pooling. Slower (~1-2ms overhead per request) but useful for debugging. Also serves as a workaround for a V8 SIGSEGV (SEGV_PKUERR) that affects PINNED and POOLED modes in some containerized environments (see #2).

Telemetry (OpenTelemetry)

Variable Default Description
OTLP_ENDPOINT - OTLP exporter endpoint (enables telemetry)
OTLP_SERVICE_NAME openworkers-runner Service name reported to OTLP
OTLP_HEADERS - Extra headers for OTLP exporter

NATS Authentication

Variable Default Description
NATS_CREDENTIALS - Path to NATS credentials file

Internal Routing (WORKER_DOMAINS)

When a worker calls fetch() to a URL matching *.{domain}, the request is routed internally instead of going through DNS and external network. This improves latency and avoids external bandwidth costs.

// These are routed internally (no DNS lookup):
fetch("https://my-api.workers.rocks/endpoint");

// This goes through external network:
fetch("https://example.com/api");

Configure for your environment:

# Production (default)
WORKER_DOMAINS=workers.rocks

# Local development
WORKER_DOMAINS=workers.dev.localhost

# Both
WORKER_DOMAINS=workers.rocks,workers.dev.localhost

Run

export RUST_LOG=openworkers_runtime=debug,openworkers_runner=debug # Optional

cargo run --features v8

Install sqlx-cli (optional - only for development)

cargo install sqlx-cli --no-default-features --features rustls,postgres

Prepare the database

cargo sqlx prepare

Known Issues

temporal_rs build failure with Deno runtime

When building with the deno feature (default), you may encounter a build error with temporal_rs:

error: unexpected end of macro invocation
  --> temporal_rs-0.0.11/src/tzdb.rs:60:1
   |
60 | timezone_provider::iana_normalizer_singleton!();
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments

Workaround: Pin timezone_provider to version 0.0.13:

cargo update -p timezone_provider@0.0.16 --precise 0.0.13

This is a known upstream issue with temporal_rs and newer versions of timezone_provider. The Cargo.lock file should preserve this fix for subsequent builds.

About

No description, website, or topics provided.

Resources

Stars

222 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages