Skip to content
Runtime

How to run Elixir code in a sandbox

Install Elixir and Erlang in a Linux microVM, either from apt or with Elixir's own install script. Then run your script 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 that runs Ubuntu 24.04.5 with passwordless sudo. This means 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. Runtime bills the CPU the code uses (pricing).

Which Elixir to install

Elixir is not in the default image. There are two installation routes. Both were read on 26 October 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 29.1.2 sh install.sh elixir@1.20.4 otp@29.1.2

The apt route is one line. It also installs the Erlang runtime that Elixir needs (erlang-base and related packages). The install script fetches the current release and installs it under $HOME/.elixir-install/installs. In a sandbox, that path is /workspace/.elixir-install/installs (install Elixir).

Run a script

.exs files run directly with elixir. They need no project and no compile step.

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 an error exits with a non-zero code. The error and stack trace go to stderr. They give a model what it 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 untrusted code inside your own node gives it all of your node's rights. In a sandbox, the whole Erlang VM runs inside a microVM with its own kernel. The network, CPU, memory and cost limits are enforced on the host. Root in the sandbox cannot change them (security).

Run a Mix project's tests

Mix needs Hex to fetch dependencies. Fetch them while the network is on. Then turn off 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 back while the tests run.

Use the newest Elixir

Elixir's install script installs 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/29.1.2/bin:{home}/elixir/1.20.4-otp-29/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@29.1.2",             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. A stored image is billed by 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 images build --apt elixir,erlang-dev --name elixir

To keep a node running between requests, start it with sbx.spawn. The call returns immediately, and the process keeps running after your connection ends. When the sandbox is idle, pause it. A pause keeps memory and processes, and compute billing stops.

A new account gets 100 free hours of a 1 GB machine in its first 30 days. No payment card is needed:

Terminalnpx withruntime login

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

Sources

Checked 26 October 2026.

Facts on this page were checked on 26 October 2026.

Related.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card