Deno logoDeno

Deno Deploy

Subhosting Private Preview

Isolates as a Service on Deno Deploy

Short term roadmap:

  • 🚧 Usage reporting
  • 🚧 New cache infrastructure

Key Ideas:

  1. Deno Deploy handles all scaling, sandboxing and isolate execution. You integrate against a simple HTTP API
  2. You implement a "relay" that forwards http requests (or events), e.g:
  3. You implement an "origin" that responds to RPCs (boot, ...), e.g:

Implementation outline:

  1. You treat Deno Deploy as a stateless isolate runner with seamless scaling & routing
    (deployments are created lazily, amortized by caching to fit typical short/long tail distributions)
  2. All authentication between you & Deno Deploy uses JWTs signed with a shared secret
  3. Your relay should inject an x-deno-subhost JWT header with the following claims:
    • deployment_id (unique upstream identifier) (possibly project_id/customer_id too)
    • subhoster_id: "acme" (JWT header kid for now, not required ATM)
    • An optional rpc_root override, default set to
    • You may also add an x-forwarded-host header encoding the original host header (allowing N domains to map to a single deployment)
  4. Your relay then forwards the request to or {hash/deployment_id} (for optimized load-balancing)
  5. Your origin may implement the following (webhook-like) RPCs, note that boot is required:
    • boot: GET
      • returns the JS Bundle or eszip payload in the body
      • returns metadata like envs, etc... in a special response header (x-deno-config)
    • read_blob: GET (file IDs are typically hashes are in manifest)
    • read_tree: GET
    • report_activity?: POST
      • Reports logs and usage info
  6. Deno Deploy sends the request through to the isolate, booting it if necessary
  7. Once the isolate responds, the http response is sent back to the relay (completing that request)


Technical specifics we may need to coordinate on


All JWTs must be signed using HS256 (HMAC sha-256) using your shared secret.

Control plane errors

If there’s an error routing a request to a subhosted isolate or an error spawning it, Deno Deploy will reply with a HTTP 502 with error info. Could potentially relay this to a reporting endpoint


ESZIPs are a custom bundle format built by the Deno team.

You can think of them as a mix between a lockfile and a tar-archive. Unlike regular scope-hoisted JS bundles, ESZIPs store JS module-graphs untransformed whilst also supporting JSON modules & WASM (in the future).

You can produce ESZIPs using our JS SDK, specifically using build(...), for example:

// import eszip
import { build } from "";

// root specifier(s) you want to bundle,
// these should valid URLs (file:/// or https://) or resolvable by your loader
const roots = [""];

// build the eszip from the roots
// NOTE: you can provide a custom loader func in the 2nd arg
const bytes = await build(roots);

// write the eszip to disk (or upload somewhere)
await Deno.writeFile("./out.eszip2", bytes);

Asset Tree Manifest

A JSON hashmap representing the file-tree available to the current deployment, it is used for readdir & stat operations. File entries in the manifest have (content addressable) IDs which are then used for (file) blob reads.

Deleting Projects

Given this stateless design we won’t persist any of your customer data/code (besides cache that will ultimately expire and usage info/logs). Inactive isolates will be automatically terminated by our hypervisors, so you do not need to send termination/delete signals given that will happen organically as a deployment stops receiving traffic.

Runtime APIs

End-users will be able to use regular Deno APIs and deno libraries in their new edge deployments.

Allowing for seamless local development using the deno CLI (potentially spawned via your own CLI)

Permissions & Limits

Isolates have 50ms CPU time per request, they can make outbound HTTP requests & open TCP/TLS connections (e.g: for databases). However Deploy will guard against circular calls to Deploy hosted apps.

x-deno-subhost example

A JWT signed with the shared secret:

header: { "kid": "acme_corp", "alg": "HS256", "typ": "JWT" }
claims: { "deployment_id": #opaque_id, exp, iat, rpc_root? }