Sandbox Execution Lifecycle
Compute runs request handlers in WebAssembly sandboxes.
In the default configuration, each request starts a new sandbox. This provides fast startup and per-request isolation. Compute also supports an opt-in mode in which a sandbox may process multiple requests. This can reduce repeated initialization overhead for applications with expensive startup work.
Default behavior
By default, Compute starts a new WebAssembly sandbox for each request. This means request handling begins in a fresh execution environment rather than reusing sandbox state from a prior request.
This execution model is a good fit for most applications because it provides:
- Fast startup
- Per-request isolation
- A simple mental model for request handling
Reusable sandboxes (opt-in)
Some applications perform expensive initialization when a sandbox starts. For example, an application may:
- parse a large JSON configuration blob
- build in-memory lookup tables
- initialize other data structures needed to serve requests
With the default behavior described above, this initialization work is repeated for each request because each request starts a new sandbox.
If repeated initialization is a significant performance cost for your workload, you can enable an opt-in mode in which a sandbox may process multiple requests. When reusable sandboxes are enabled, a single sandbox can process more than one incoming request. This can improve performance for workloads that benefit from reusing initialized in-memory state across requests.
IMPORTANT: Enabling reusable sandboxes changes the request execution lifecycle. Applications that enable this mode should be written with the expectation that sandbox state may persist across requests handled by the same sandbox.
Enabling reusable sandboxes is recommended only when you need to reduce repeated initialization overhead.
Configuring reusable sandboxes
When reusable sandboxes are enabled, you can configure limits that control how long a reusable sandbox remains available and when it is stopped.
You can configure the following limits (desired maximums):
- the maximum lifetime of the sandbox
- the maximum amount of time to wait for another request before stopping
- the maximum amount of memory the sandbox may use before stopping
- the maximum number of requests to serve before stopping
These settings are not guarantees that a sandbox will run until a given limit is reached; a sandbox may stop earlier depending on factors like high memory use or whether other sandboxes for the service are idling while waiting for further requests. These limits help you balance efficiency gains from reusable sandboxes against resource usage and lifecycle control.
Lifecycle and waiting for the next request
A Compute sandbox is not a general-purpose server and is designed not to wait indefinitely for new requests.
When reusable sandboxes are enabled, the sandbox's lifetime progresses as follows:
A WebAssembly sandbox is started in response to an incoming request.
The request handler runs against the current request.
IMPORTANT: When reusable sandboxes are enabled, request execution limits (such as CPU time and runtime limits) apply to each incoming request. For current request execution limits, refer to Limitations and constraints.
After the request handler has run to completion, the sandbox is checked for whether any of the following limits have been met:
- the "maximum lifetime of the sandbox" limit, if set
- the "maximum memory the sandbox may use" limit, if set
- the "maximum number of requests to serve" limit, if set
- a platform-defined maximum number of requests a sandbox can execute (this value is not publicly disclosed and may change)
If any of the limits have been met, the sandbox exits.
The sandbox waits for an additional request. The time a sandbox spends waiting for another request is bounded by the lesser of the following values:
- the "amount of time to wait for another request" limit, if set
- a platform-defined maximum amount of time a sandbox is willing to wait for a next request (this value is not publicly disclosed and may change)
If no request arrives in this time, the sandbox exits.
Repeat from step 2 against the newly-arrived request.
IMPORTANT: Sandbox lifecycle checks are only performed between requests, not during active request handling.
Concurrency
When reusable sandboxes are enabled, a single sandbox does not process multiple requests concurrently.
A sandbox is not reused while it is handling a request. Even if the request is temporarily idle (for example, waiting for asynchronous work), the sandbox remains dedicated to that request until execution completes. A sandbox becomes eligible for reuse only after the request handler has finished. If another request arrives while a sandbox is still handling a request, Compute starts a new sandbox to handle the incoming request.
For example, in JavaScript, the addEventListener() syntax and event-loop-based code can make it appear that another request might be handled in the same sandbox while an earlier request is awaiting asynchronous work. In practice, this does not occur. Each request runs to completion before that sandbox can be reused.
Resource usage and billing considerations
While a reusable sandbox waits for the next request, it continues to consume memory and wall-clock time. Memory used by the WebAssembly instance is freed when the instance exits, as usual.
If your service is billed by WebAssembly memory × wall-clock time, usage accrued while waiting for the next request is billable, including cases where the sandbox eventually times out. You can limit this usage by setting your own timeout for how long a sandbox waits for another request.
A WebAssembly instance that is blocked while waiting for the next request does not consume vCPU time.
Environment variables
Environment variables are associated with a sandbox rather than an individual request.
For example, FASTLY_TRACE_ID is generated for each sandbox. If a sandbox processes multiple incoming requests, use the provided per-request identifier when you need request-level correlation (e.g., telemetry or logging).
How to enable reusable sandboxes
Reusable sandboxes are enabled using language-specific configuration or APIs.
- Rust
- JavaScript
- Go
To enable reusable sandboxes with the Rust SDK for Compute, use a main() function rather than the #[fastly::main] macro. Create an instance of Serve from the fastly::experimental::reusable_sessions module with the required options set for your reusable sandbox configuration, call .run() to configure it with your request handler, and return the result from your main() function.
The following example initializes the sandbox to handle at most 10 requests before shutting down the sandbox.
use fastly::{Error, Request, Response};use fastly::experimental::reusable_sessions::*;
fn main() -> Result<(), Error> { Serve::new() .with_max_requests(10) .run(handler) .into_result()}
fn handler(_req: Request) -> Result<Response, Error> { Ok(Response::from_body("hello") .with_header("hello", "world!") .with_status(200))}For more details, refer to the SDK documentation for fastly::experimental::reusable_sessions.
Per-request ID
As described above, FASTLY_TRACE_ID is generated for each sandbox. If a sandbox processes multiple incoming requests, use the provided per-request identifier when you need request-level correlation (telemetry, logging).
- Rust
- JavaScript
- Go
The per-request identifier is available as get_client_request_id() on the Request struct.
let request_id = req.get_client_request_id().unwrap_or("");println!("Request ID: {}", request_id);