On Runtime, SQLite is already in every sandbox, and a full PostgreSQL server is
one apt install away. Each sandbox is a Firecracker microVM running Ubuntu
24.04.5 with sqlite3 in the image and passwordless sudo. A query that a model
wrote runs against a copy of the data, not your production database.
SQLite or PostgreSQL?
| Question | SQLite | PostgreSQL |
|---|---|---|
| In the default image? | Yes, the sqlite3 shell |
No: sudo apt-get install -y postgresql |
| Ubuntu 24.04 package, 26 Oct 2026 | sqlite3 3.45.1 |
postgresql 16 (postgresql-16 16.15) |
| Runs as | A command on a file | A server on port 5432 |
| Also reachable from | Python's sqlite3 module, in the image |
psql, any driver, on localhost |
| Best for | One-off queries, grading, CSV analysis | Testing SQL that production runs on Postgres |
Run a query on SQLite
Pass the SQL on stdin and name the database as an argument. With -bail,
the shell stops at the first error. -json prints rows as JSON, so your code
can parse them (sqlite3 command-line shell).
TypeScriptimport { Sandbox } from "withruntime";const setup = `CREATE TABLE orders(id INTEGER PRIMARY KEY, customer TEXT, total REAL);INSERT INTO orders(customer, total) VALUES ('ada', 30.5), ('bo', 12), ('ada', 7.25);`;const query = "SELECT customer, SUM(total) AS spent FROM orders GROUP BY customer ORDER BY spent DESC;";await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop",});await sbx.exec(["sqlite3", "-bail", "shop.db"], { stdin: setup, check: true });const rows = await sbx.exec(["sqlite3", "-bail", "-safe", "-json", "-readonly", "shop.db"], { stdin: query, timeoutMs: 30_000,});console.log(rows.exitCode, rows.stdout); // [{"customer":"ada","spent":37.75}, ...]Pythonfrom withruntime import Sandboxsetup = """CREATE TABLE orders(id INTEGER PRIMARY KEY, customer TEXT, total REAL);INSERT INTO orders(customer, total) VALUES ('ada', 30.5), ('bo', 12), ('ada', 7.25);"""query = "SELECT customer, SUM(total) AS spent FROM orders GROUP BY customer ORDER BY spent DESC;"with Sandbox.create(network={"internet": False}, timeout_seconds=300, on_lease_end="stop") as sbx: sbx.exec(["sqlite3", "-bail", "shop.db"], stdin=setup, check=True) rows = sbx.exec(["sqlite3", "-bail", "-safe", "-json", "-readonly", "shop.db"], stdin=query, timeout_ms=30_000) print(rows.exit_code, rows.stdout)Three flags make the shell safer for SQL you did not write:
-readonlyopens the database read-only.-safeturns off every shell feature that reads or writes a file other than the database:ATTACH,readfile(),writefile(),load_extension(),.shell,.system,.import,.outputand more.timeoutMsends a query that runs too long. The result then showstimedOut: true.
To query your own data, upload the database file with sbx.files.write, or a
folder with sbx.files.upload. Download the changed file afterward.
Run PostgreSQL in the sandbox
Ubuntu's postgresql package creates a cluster named 16/main. Start it with
pg_ctlcluster, which does not need a service manager. Then create a role for
the sandbox user, so psql connects over the local socket without a password.
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ timeoutSeconds: 1800, onLeaseEnd: "stop" });const slow = { check: true, timeoutMs: 600_000 } as const;await sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q postgresql", slow);await sbx.exec("sudo pg_ctlcluster 16 main status || sudo pg_ctlcluster 16 main start", slow);await sbx.exec( "sudo -u postgres createuser --superuser runtime && createdb -O runtime runtime", slow,);await sbx.network.set({ internet: false });await sbx.files.write( "/workspace/check.sql", "CREATE TABLE t(n int); INSERT INTO t SELECT generate_series(1, 1000); SELECT count(*), sum(n) FROM t;",);const run = await sbx.exec(["psql", "-v", "ON_ERROR_STOP=1", "-At", "-f", "check.sql"], { timeoutMs: 60_000,});console.log(run.exitCode, run.stdout); // 1000|500500Pythonfrom withruntime import Sandboxwith Sandbox.create(timeout_seconds=1800, on_lease_end="stop") as sbx: sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q postgresql", check=True, timeout_ms=600_000) sbx.exec("sudo pg_ctlcluster 16 main status || sudo pg_ctlcluster 16 main start", check=True) sbx.exec("sudo -u postgres createuser --superuser runtime && createdb -O runtime runtime", check=True) sbx.network.set(internet=False) run = sbx.exec(["psql", "-v", "ON_ERROR_STOP=1", "-At", "-c", "SELECT version();"], timeout_ms=60_000) print(run.exit_code, run.stdout)ON_ERROR_STOP=1 makes psql stop at the first failing statement and exit
non-zero, so a broken migration fails the command. The network rule refuses
connections out of the sandbox. localhost inside it keeps working.
Start every sandbox with Postgres running
An image can install PostgreSQL and define how a sandbox starts it. The create call returns once port 5432 is listening (start and ready commands):
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "postgres", recipe: { apt: ["postgresql"] }, start: { command: "sudo pg_ctlcluster 16 main start && sleep infinity", readyPort: 5432, readyTimeoutSeconds: 60, },});await using sbx = await runtime.sandboxes.create({ image: "postgres", network: { internet: false },});console.log(sbx.info.start); // { state: "ready", ... }await sbx.exec("sudo -u postgres psql -c 'SELECT 1'", { check: true });pg_ctlcluster returns once the server is up. A start command that ends
counts as exited, so sleep infinity keeps it running.
For many test runs against the same seeded data, load the data once, then fork the running sandbox. Each copy starts with the server running and the data in memory. A write in one copy does not reach any other copy.
What it costs
A database that sits idle between queries costs the CPU floor, a twentieth of a vCPU at $0.025 per vCPU-hour, plus memory at $0.0075 per GiB-hour. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while it waits and $0.08 an hour when fully busy (pricing). A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card required:
Terminalnpx withruntime sandboxes run -- sqlite3 :memory: 'select sqlite_version();'See also a data analysis agent, agent evals and running untrusted LLM code.
Sources
Checked 26 October 2026.
- SQLite command-line shell:
-bail,-json,-readonly,-safe - Ubuntu 24.04 (noble) packages: sqlite3, postgresql, postgresql-16, postgresql-common file list (
pg_ctlcluster)
Facts on this page were checked on 26 October 2026.