Runtime

How to run Elixir code in a sandbox

Install Elixir and Erlang in a Linux microVM, from apt or Elixir's own install script, and run the script there with elixir.

On Runtime Elixir installs with one command and each run gets a whole virtual machine, not just a BEAM process. Each sandbox is a Firecracker microVM running Ubuntu 24.04.5 with passwordless sudo, so apt-get install elixir or the script from elixir-lang.org works unchanged. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while it waits and $0.08 an hour with both cores busy, because Runtime bills the CPU the code uses (pricing).

Which Elixir to install

Elixir is not in the default image. There are two routes, both read on 25 September 2026:

Route Elixir Erlang/OTP Command
Ubuntu 24.04 (noble) packages 1.14.0 25.3.2.8 sudo apt-get install -y elixir
elixir-lang.org install.sh 1.20.4 28.4 sh install.sh elixir@1.20.4 otp@28.4

The apt route is one line and pulls in the Erlang runtime it needs (erlang-base and friends). The install script fetches the current release and installs it under $HOME/.elixir-install/installs, which in a sandbox is /workspace/.elixir-install/installs (install Elixir).

Run a script

.exs files run directly with elixir, with no project and no compile step you have to manage.

TypeScriptimport { Sandbox } from "withruntime";const script = `words = ~w(the quick brown fox jumps over the lazy dog)words|> Enum.frequencies_by(&String.length/1)|> Enum.sort()|> IO.inspect()`;await using sbx = await Sandbox.create({ timeoutSeconds: 600, onLeaseEnd: "stop" });await sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q elixir", {  check: true,  timeoutMs: 600_000,});await sbx.network.set({ internet: false });await sbx.files.write("/workspace/count.exs", script);const run = await sbx.exec(["elixir", "count.exs"], { timeoutMs: 30_000 });console.log(run.exitCode, run.timedOut, run.stdout);
Pythonfrom withruntime import Sandboxscript = """words = ~w(the quick brown fox jumps over the lazy dog)words|> Enum.frequencies_by(&String.length/1)|> Enum.sort()|> IO.inspect()"""with Sandbox.create(timeout_seconds=600, on_lease_end="stop") as sbx:    sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q elixir", check=True, timeout_ms=600_000)    sbx.network.set(internet=False)    sbx.files.write("/workspace/count.exs", script)    run = sbx.exec(["elixir", "count.exs"], timeout_ms=30_000)    print(run.exit_code, run.timed_out, run.stdout)

A script that raises exits non-zero with the error and stack trace on stderr, which is what a model needs to correct its code.

Why a sandbox and not a BEAM process

Elixir code can call System.cmd/2, :os.cmd/1, File.rm_rf!/1 or open a socket. Running a stranger's module inside your own node gives it all of your node's rights. In a sandbox the whole Erlang VM is inside a microVM with its own kernel, and the network, CPU, memory and cost limits are enforced on the host, where root in the sandbox cannot change them (security).

Run a Mix project's tests

Mix needs Hex to fetch dependencies. Fetch them with the network on, then cut the network before the tests run:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({  image: "elixir",  timeoutSeconds: 1800,  onLeaseEnd: "stop",});await sbx.files.upload("./my_app", "/workspace/my_app");const opts = { cwd: "/workspace/my_app", env: { MIX_ENV: "test" }, timeoutMs: 900_000 };await sbx.exec("mix local.hex --force && mix deps.get && mix compile", { ...opts, check: true });await sbx.network.set({ internet: false });const tests = await sbx.exec(["mix", "test"], {  ...opts,  onStdout: (t) => process.stdout.write(t),});process.exitCode = tests.exitCode ?? 1;

This uses the elixir image built below. Output streams while the tests run.

Use the newest Elixir

Elixir's install script puts OTP and Elixir side by side; add both bin folders to PATH for the command:

Pythonfrom withruntime import Sandboxhome = "/workspace/.elixir-install/installs"path = f"{home}/otp/28.4/bin:{home}/elixir/1.20.4-otp-28/bin:/usr/local/bin:/usr/bin:/bin"with Sandbox.create(timeout_seconds=900, on_lease_end="stop") as sbx:    sbx.exec("curl -fsSO https://elixir-lang.org/install.sh && sh install.sh elixir@1.20.4 otp@28.4",             check=True, timeout_ms=600_000)    print(sbx.exec(["elixir", "--version"], env={"PATH": path}).stdout)

Start every sandbox with Elixir

A recipe installs the Ubuntu packages once; erlang-dev adds the headers that dependencies with native code compile against. Building is free, and a stored image is charged on its size (custom images).

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "elixir", recipe: { apt: ["elixir", "erlang-dev"] } });await using sbx = await runtime.sandboxes.create({ image: "elixir" });console.log((await sbx.exec(["elixir", "--version"])).stdout);
Terminalruntime image build --apt elixir,erlang-dev --name elixir

To keep a node running between requests, start it with sbx.spawn, which returns at once and outlives your connection, and pause the sandbox when it is idle: a pause keeps memory and processes, and compute billing stops.

New accounts get 50 free sandbox hours, no card:

Terminalnpx withruntime login

See also running untrusted LLM code, grading student code and per-user dev environments.

Sources

Checked 25 September 2026.

Facts on this page were checked on 25 September 2026.