# 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](/docs/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](https://elixir-lang.org/install/)). ## Run a script `.exs` files run directly with `elixir`. They need no project and no compile step. ```ts check import { 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); ``` ```python check from withruntime import Sandbox script = """ 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](/docs/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: ```ts check import { 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: ```python check from withruntime import Sandbox home = "/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](/docs/images)). ```ts check import { 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); ``` ```bash no-run runtime 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](/how-to/pause-and-resume-a-sandbox) 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: ```bash no-run npx withruntime login ``` See also [running untrusted LLM code](/use-cases/run-untrusted-llm-code), [grading student code](/use-cases/grade-student-code) and [per-user dev environments](/use-cases/per-user-dev-environments). ## Sources Checked 26 October 2026. - [Install Elixir](https://elixir-lang.org/install/): `install.sh`, Elixir 1.20.4 and OTP 28.4 - Ubuntu 24.04 (noble) packages: [elixir](https://packages.ubuntu.com/noble/elixir), [erlang-nox](https://packages.ubuntu.com/noble/erlang-nox), [erlang-dev](https://packages.ubuntu.com/noble/erlang-dev) Facts on this page were checked on 26 October 2026.