# How to run Deno code in a sandbox Install Deno with its one-line installer in an isolated Linux microVM, then run the file with `deno run` and only the permissions it needs. **On Runtime, Deno's permission flags get a second wall behind them: a Firecracker microVM with its own kernel.** For untrusted code, Deno's own security guide recommends several layers, among them "a sandboxed environment like a VM or MicroVM (gVisor, Firecracker, etc)". Every Runtime sandbox is that layer, with a network switch the host enforces, and a 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while it waits ([pricing](/docs/pricing)). ## What Deno's permissions do and do not cover A program run with Deno has no access to files, the network, environment variables or subprocesses unless you grant it with flags such as `--allow-net`, `--allow-read` and `--allow-write`, each of which can be scoped (`--allow-net=example.com`, `--allow-read=./data`). Deno also "sets no limits on the execution of code at the same privilege level", and `-A` grants everything. Its guide recommends using all of these for untrusted code: limited permissions, a Web Worker with fewer permissions, OS sandboxing such as seccomp, and a VM or microVM ([Deno security](https://docs.deno.com/runtime/fundamentals/security/)). | Layer | What it stops | Where it is enforced | | ------------------------- | ---------------------------------------------------------------- | --------------------------- | | Deno permissions | File, network, env and subprocess access the flags did not grant | Inside the Deno process | | Firecracker microVM | Reaching your host through a kernel or runtime bug | Hardware virtualization | | Runtime network rules | Any outbound connection, or all but named hosts | On the host, outside the VM | | Command timeout and lease | Loops that never end; a sandbox left running | Runtime's API and host | Root inside the sandbox cannot change the last two ([security](/docs/security)). ## Install Deno and run a file Deno is not in the default image. Its Linux installer is one command and puts the binary in `$HOME/.deno/bin/deno` ([Deno installation](https://docs.deno.com/runtime/getting_started/installation/)), which in a sandbox is `/workspace/.deno/bin/deno`. The installer needs `unzip`, which the image has. v2.9.7 was the latest release on 25 September 2026. ```ts check import { Sandbox } from "withruntime"; const program = ` const text = await Deno.readTextFile("./input.txt"); console.log(text.trim().split(/\\s+/).length, "words"); `; await using sbx = await Sandbox.create({ network: { internet: true, allow: ["deno.land", "*.deno.land"] }, timeoutSeconds: 600, onLeaseEnd: "stop", }); await sbx.exec("curl -fsSL https://deno.land/install.sh | sh -s v2.9.7", { check: true, timeoutMs: 180_000, }); await sbx.network.set({ internet: false }); await sbx.files.write("/workspace/input.txt", "one two three four"); await sbx.files.write("/workspace/count.ts", program); const deno = "/workspace/.deno/bin/deno"; const run = await sbx.exec([deno, "run", "--allow-read=./input.txt", "count.ts"], { timeoutMs: 30_000, }); console.log(run.stdout); // 4 words ``` ```python check from withruntime import Sandbox program = """ const text = await Deno.readTextFile("./input.txt"); console.log(text.trim().split(/\\s+/).length, "words"); """ with Sandbox.create( network={"internet": True, "allow": ["deno.land", "*.deno.land"]}, timeout_seconds=600, on_lease_end="stop", ) as sbx: sbx.exec("curl -fsSL https://deno.land/install.sh | sh -s v2.9.7", check=True, timeout_ms=180_000) sbx.network.set(internet=False) sbx.files.write("/workspace/input.txt", "one two three four") sbx.files.write("/workspace/count.ts", program) deno = "/workspace/.deno/bin/deno" run = sbx.exec([deno, "run", "--allow-read=./input.txt", "count.ts"], timeout_ms=30_000) print(run.stdout) # 4 words ``` The installer script and the Deno download both come from `deno.land` names, so the allow list is two entries. The version argument pins the release; with none, the script fetches the latest. ## Type-check before running `deno run` strips types and does not check them. `deno check`, or `deno run --check`, runs the TypeScript compiler first ([Deno TypeScript](https://docs.deno.com/runtime/fundamentals/typescript/)): ```bash no-run /workspace/.deno/bin/deno check count.ts /workspace/.deno/bin/deno run --check --allow-read=./input.txt count.ts ``` ## Deno in every sandbox Install Deno once in a [custom image](/docs/images). The installer honours `DENO_INSTALL`, so pointing it at `/usr/local` puts `deno` in `/usr/local/bin`, on the sandbox user's `PATH`: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "deno", recipe: { commands: ["curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh -s v2.9.7"], }, }); await using sbx = await runtime.sandboxes.create({ image: "deno", network: { internet: false } }); await sbx.files.write("/workspace/hi.ts", "console.log(`deno ${Deno.version.deno}`);"); console.log((await sbx.exec(["deno", "run", "hi.ts"])).stdout); ``` ```python check from withruntime import Runtime runtime = Runtime() runtime.images.build(name="deno", recipe={ "commands": ["curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh -s v2.9.7"], }) with runtime.sandboxes.create(image="deno", network={"internet": False}) as sbx: sbx.files.write("/workspace/hi.ts", "console.log(`deno ${Deno.version.deno}`);") print(sbx.exec(["deno", "run", "hi.ts"]).stdout) ``` Recipe commands run as root in `/workspace`. Building the image is free, and a stored image is charged on its size. ## Deno or Node in a sandbox? - **Deno** when the code should run with no permissions by default, or when a project already uses `deno.json` and JSR packages. - **Node.js or Bun** when you want no install at all: both are in every sandbox ([Node.js](/languages/javascript), [Bun](/languages/bun)). Deno can also be installed from npm with `npm install -g deno`; its docs note this slows Deno's startup and recommend the shell installer. ## Related - [Run TypeScript code in a sandbox](/languages/typescript) - [How to run untrusted code from an LLM safely](/use-cases/run-untrusted-llm-code) - [Turn off a sandbox's internet](/how-to/turn-off-sandbox-internet) - [What is egress control?](/glossary/egress-control) New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- bash -c 'curl -fsSL https://deno.land/install.sh | sh && ~/.deno/bin/deno --version' ``` ## Sources - Deno security and permissions: https://docs.deno.com/runtime/fundamentals/security/ (read 25 September 2026) - Deno installation: https://docs.deno.com/runtime/getting_started/installation/ (read 25 September 2026) - Deno TypeScript support: https://docs.deno.com/runtime/fundamentals/typescript/ (read 25 September 2026) - Deno install script, including `DENO_INSTALL` and the `unzip` check: https://deno.land/install.sh (read 25 September 2026) - Latest Deno release, v2.9.7: https://dl.deno.land/release-latest.txt (read 25 September 2026) Facts on this page were checked on 25 September 2026.