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 elixirTo 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 loginSee also running untrusted LLM code, grading student code and per-user dev environments.
Sources
Checked 25 September 2026.
- Install Elixir:
install.sh, Elixir 1.20.4 and OTP 28.4 - Ubuntu 24.04 (noble) packages: elixir, erlang-nox, erlang-dev
Facts on this page were checked on 25 September 2026.