# How to run R code in a sandbox Run R in an isolated microVM with a code interpreter that returns plots as PNG and data frames as tables, or install R and use `Rscript`. **On Runtime, R needs no setup at all: the code interpreter installs it the first time you run an R cell, then keeps your variables between cells.** On 23 September 2026 that first install took about 90 seconds, once per sandbox, and an image with R skips it ([code interpreter](/docs/javascript#code-interpreter)). Every sandbox is a Firecracker microVM with its own kernel, and a 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while an analyst reads the last chart ([pricing](/docs/pricing)). ## R as a notebook ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ timeoutSeconds: 1800 }); await sbx.interpreter.run( "df <- data.frame(dose = c(1, 2, 4, 8), response = c(3.1, 5.8, 9.6, 14.2))", { language: "r", timeoutMs: 300_000, // the first R cell installs R }, ); const fit = await sbx.interpreter.run("coef(lm(response ~ dose, data = df))", { language: "r" }); console.log(fit.stdout, fit.results[0]?.data["text/plain"]); const plot = await sbx.interpreter.run("plot(df$dose, df$response, type = 'b')", { language: "r" }); const png = plot.results.find((r) => r.data["image/png"]); console.log(png ? "got a chart" : "no chart"); const table = await sbx.interpreter.run("df", { language: "r" }); console.log(table.results[0]?.data["application/vnd.runtime.table+json"]); ``` ```python check import base64 from withruntime import Sandbox with Sandbox.create(timeout_seconds=1800) as sbx: sbx.interpreter.run("df <- data.frame(dose = c(1, 2, 4, 8), response = c(3.1, 5.8, 9.6, 14.2))", language="r", timeout_ms=300_000) # the first R cell installs R fit = sbx.interpreter.run("coef(lm(response ~ dose, data = df))", language="r") print(fit["stdout"]) plot = sbx.interpreter.run("plot(df$dose, df$response, type = 'b')", language="r") for result in plot["results"]: if "image/png" in result["data"]: with open("dose.png", "wb") as out: out.write(base64.b64decode(result["data"]["image/png"])) table = sbx.interpreter.run("df", language="r") print(table["results"][0]["data"]["application/vnd.runtime.table+json"]) ``` - **Plots** come back as PNG images, base64 in `image/png`, with no `png()` or `dev.off()` in your code. - **Data frames** come back as tables your app can render. - **Any file** a cell hands to `display` (.png, .svg, .html, .csv) comes back as a result too. - The first R cell downloads R from Ubuntu's archive, so that sandbox needs the internet. After that, `sbx.network.set({ internet: false })` cuts it off for the code that follows. ## Which R to install For scripts outside the interpreter, or for an image, install R yourself: | Source | R version on 25 September 2026 | Command | | -------------------------------------- | ------------------------------ | ------------------------------------------------ | | Ubuntu 24.04 archive (universe) | 4.3.3 | `sudo apt-get install -y r-base-core` | | Ubuntu, with headers to build packages | 4.3.3 | `sudo apt-get install -y r-base-dev` | | CRAN's Ubuntu repository | 4.6 | Add the `noble-cran40` repository, then `r-base` | CRAN's own steps for Ubuntu add its signing key and the `$(lsb_release -cs)-cran40` repository, then run `sudo apt install --no-install-recommends r-base` ([CRAN](https://cran.r-project.org/bin/linux/ubuntu/)). CRAN also points to r2u, which installs CRAN packages as Ubuntu binaries, so they need no compile. ## Run an R script ```ts check import { Sandbox } from "withruntime"; const script = ` x <- c(12, 15, 11, 19, 22, 17) cat(sprintf("mean %.2f, sd %.2f\\n", mean(x), sd(x))) `; await using sbx = await Sandbox.create({ timeoutSeconds: 900, onLeaseEnd: "stop" }); await sbx.exec( "sudo apt-get update -q && sudo apt-get install -y -q --no-install-recommends r-base-core", { check: true, timeoutMs: 600_000, }, ); await sbx.network.set({ internet: false }); await sbx.files.write("/workspace/stats.R", script); const run = await sbx.exec(["Rscript", "stats.R"], { timeoutMs: 60_000 }); console.log(run.stdout); // mean 16.00, sd 4.20 ``` ```python check from withruntime import Sandbox script = """ x <- c(12, 15, 11, 19, 22, 17) cat(sprintf("mean %.2f, sd %.2f\\n", mean(x), sd(x))) """ with Sandbox.create(timeout_seconds=900, on_lease_end="stop") as sbx: sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q --no-install-recommends r-base-core", check=True, timeout_ms=600_000) sbx.network.set(internet=False) sbx.files.write("/workspace/stats.R", script) run = sbx.exec(["Rscript", "stats.R"], timeout_ms=60_000) print(run.stdout) # mean 16.00, sd 4.20 ``` An array runs `Rscript` with no shell in between. A script that loops forever returns `timedOut: true` with the output so far. ## R and your packages in every sandbox Build a [custom image](/docs/images) with R and the packages your analyses use. The interpreter finds R already there and starts its first R cell with no install: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "r-data", recipe: { apt: ["r-base-dev"], commands: [ `Rscript -e 'install.packages(c("data.table", "jsonlite"), repos = "https://cloud.r-project.org")'`, ], }, }); await using sbx = await runtime.sandboxes.create({ image: "r-data", network: { internet: false } }); const cell = await sbx.interpreter.run("library(data.table); data.table(a = 1:3)[, sum(a)]", { language: "r", }); console.log(cell.results[0]?.data["text/plain"]); ``` ```python check from withruntime import Runtime runtime = Runtime() runtime.images.build(name="r-data", recipe={ "apt": ["r-base-dev"], "commands": ["""Rscript -e 'install.packages(c("data.table", "jsonlite"), repos = "https://cloud.r-project.org")'"""], }) with runtime.sandboxes.create(image="r-data", network={"internet": False}) as sbx: cell = sbx.interpreter.run("library(data.table); data.table(a = 1:3)[, sum(a)]", language="r") print(cell["results"][0]["data"]["text/plain"]) ``` `install.packages` compiles packages from source, which is why the recipe takes `r-base-dev`. The image build runs in its own microVM, and building is free; a stored image is charged on its size. ## R next to Python One sandbox can hold an R context and a Python context at once, each with its own variables, so an agent can clean data in pandas and fit a model in R without leaving the machine. Files in `/workspace` are shared by both. ## Related - [A data analysis agent](/use-cases/data-analysis-agent) - [Build a code interpreter for a chatbot](/use-cases/code-interpreter-for-chatbots) - [What is a code interpreter?](/glossary/code-interpreter) - [Run untrusted Python code safely](/languages/python) New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- bash -c 'sudo apt-get update -q && sudo apt-get install -y -q r-base-core && R --version' ``` ## Sources - CRAN, R for Ubuntu (current R 4.6, noble supported, r2u): https://cran.r-project.org/bin/linux/ubuntu/ (read 25 September 2026) - Ubuntu 24.04 package index (`r-base`, `r-base-core` and `r-base-dev` 4.3.3-2build2): http://archive.ubuntu.com/ubuntu/dists/noble/universe/binary-amd64/ (read 25 September 2026) Facts on this page were checked on 25 September 2026.