How to run PostgreSQL in a sandbox for tests and agents
Install Ubuntu's postgresql package in a microVM, start a server with initdb and postgres, load your schema, and query it with psql.
On Runtime, a seeded database can be copied, running, in one call. Load the schema and fixtures once, then fork the sandbox: every copy starts with Postgres already up and the data in memory, so ten tests or ten agent attempts each get a private database without ten restores. Every sandbox is a Firecracker microVM, and one that holds an idle database costs $0.03125 an hour for 2 vCPU and 4 GiB (pricing). Ubuntu 24.04 shipped PostgreSQL 16.15 on 25 September 2026; the PostgreSQL project's own apt repository had 18 as its current stable release.
Start a database and load a schema
Ubuntu's package creates a cluster of its own; the sample drops it and makes
one in /workspace that belongs to the sandbox user, so nothing else depends
on how the system starts services.
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";const bin = "/usr/lib/postgresql/16/bin";await using sbx = await Sandbox.create({ timeoutSeconds: 1800 });const slow = { check: true, timeoutMs: 600_000 } as const;await sbx.exec("sudo apt-get update -qq && sudo apt-get install -y -qq postgresql", slow);await sbx.exec("sudo pg_dropcluster --stop 16 main", { check: true });await sbx.exec(`${bin}/initdb -D /workspace/pgdata -U postgres --auth=trust`, slow);await sbx.spawn(`${bin}/postgres -D /workspace/pgdata -k /tmp -c listen_addresses=127.0.0.1`);await sbx.exec("timeout 60 bash -c 'until pg_isready -q -h 127.0.0.1; do sleep 0.5; done'", { check: true, timeoutMs: 90_000,});await sbx.files.upload("./schema.sql", "/workspace/schema.sql");const psql = "psql -h 127.0.0.1 -U postgres -v ON_ERROR_STOP=1";await sbx.exec(`${psql} -f schema.sql`, { check: true });const count = await sbx.exec(`${psql} -Atc 'select count(*) from users'`, { check: true });console.log("users:", count.stdout.trim());await sbx.exec("pg_dump -h 127.0.0.1 -U postgres -Fc -f /workspace/app.dump postgres", { check: true,});await writeFile("app.dump", await sbx.files.read("/workspace/app.dump"));Pythonfrom withruntime import SandboxBIN = "/usr/lib/postgresql/16/bin"PSQL = "psql -h 127.0.0.1 -U postgres -v ON_ERROR_STOP=1"with Sandbox.create(timeout_seconds=1800) as sbx: sbx.exec("sudo apt-get update -qq && sudo apt-get install -y -qq postgresql", check=True, timeout_ms=600_000) sbx.exec("sudo pg_dropcluster --stop 16 main", check=True) sbx.exec(f"{BIN}/initdb -D /workspace/pgdata -U postgres --auth=trust", check=True) sbx.spawn(f"{BIN}/postgres -D /workspace/pgdata -k /tmp -c listen_addresses=127.0.0.1") sbx.exec("timeout 60 bash -c 'until pg_isready -q -h 127.0.0.1; do sleep 0.5; done'", check=True, timeout_ms=90_000) sbx.files.upload("schema.sql", "/workspace/schema.sql") sbx.exec(f"{PSQL} -f schema.sql", check=True) print(sbx.exec(f"{PSQL} -Atc 'select count(*) from users'", check=True).stdout) sbx.exec("pg_dump -h 127.0.0.1 -U postgres -Fc -f /workspace/app.dump postgres", check=True) sbx.files.download("/workspace/app.dump", "app.dump")spawnkeeps the server running after the call returns; a process started withexecends with its command.--auth=trustlets any process in the sandbox connect without a password. Only code you run there, and you through the forward below, can reach127.0.0.1: nothing on the internet can connect in to a sandbox.-k /tmpputs the Unix socket where the sandbox user may write it.
One seeded database per test
TypeScriptimport { Sandbox } from "withruntime";declare const seeded: Sandbox; // the sandbox above, schema and fixtures loadedconst copies = await seeded.fork({ count: 10 });const results = await Promise.all( copies.map((db, i) => db.exec(`psql -h 127.0.0.1 -U postgres -f tests/case-${i}.sql`, { timeoutMs: 300_000 }), ),);await Promise.all(copies.map((db) => db.stop()));console.log(results.map((r) => r.exitCode));A fork copies files, memory and running processes, so Postgres in each copy is the same server mid-flight, not a fresh start and a restore. The source is paused for about a second on a fresh sandbox, longer the more memory it holds. Forks make 1 to 10 copies per call; to start copies later, take a snapshot and create sandboxes from it.
Connect from your laptop or a GUI
Forward the port and point psql, DBeaver, TablePlus or your app at
localhost:
Pythonimport osimport psycopg # pip install "psycopg[binary]"from withruntime import Sandboxsbx = Sandbox.connect(os.environ["SANDBOX_ID"])with sbx.forward_port(5432, local_port=15432) as forward: with psycopg.connect(f"postgresql://postgres@127.0.0.1:{forward.local_port}/postgres") as conn: print(conn.execute("select version()").fetchone())Terminalruntime sandbox port-forward <id> 15432:5432psql postgresql://postgres@127.0.0.1:15432/postgresThe forward is a WebSocket through Runtime's API, authenticated with your key (forward ports). It works on the free trial. To give a service outside Runtime a connection string instead, a paid account can open a public TCP port (networking).
Keep the data
| You want | Use |
|---|---|
| Come back to the same running database | Pause: files, memory and processes are kept (30 days by default on paid accounts, 1 to 365), and the next request wakes it |
| Many databases from one prepared state, later | A snapshot, then sandboxes.create({ snapshot }) |
| Data that outlives any sandbox | initdb onto a volume mounted at /data; run sync after writes you must keep |
| A copy on your own machine | pg_dump, then files.read as above |
A sandbox with a volume attached cannot be snapshotted, and a fork takes a snapshot, so choose per database: fast copies, or a disk that outlives the machine.
A newer PostgreSQL, or Postgres in every sandbox
The PostgreSQL project's apt repository supports Ubuntu 24.04. Its script adds
the repository, and -y skips its prompt:
Terminalsudo apt-get install -y postgresql-commonsudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -ysudo apt-get install -y postgresql-18 # binaries in /usr/lib/postgresql/18/binTo skip the install in every sandbox, build it into a custom image; building is free, and the trial stores three images free.
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "postgres", recipe: { apt: ["postgresql"], commands: ["pg_dropcluster --stop 16 main"] },});Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build(name="postgres", recipe={"apt": ["postgresql"], "commands": ["pg_dropcluster --stop 16 main"]})A sandbox from the image starts at the initdb step of the first sample.
For Postgres next to an app and a cache, a Compose file is often simpler; see Docker Compose integration tests. For agents that need a database to try migrations against, see a coding agent sandbox.
Sources
- Ubuntu 24.04 postgresql-16 package, https://packages.ubuntu.com/noble/postgresql-16, read 25 September 2026
- PostgreSQL downloads for Ubuntu, https://www.postgresql.org/download/linux/ubuntu/, read 25 September 2026
- apt.postgresql.org.sh options, https://salsa.debian.org/postgresql/postgresql-common/-/raw/master/pgdg/apt.postgresql.org.sh, read 25 September 2026
Facts on this page were checked on 25 September 2026.