Runtime

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")
  • spawn keeps the server running after the call returns; a process started with exec ends with its command.
  • --auth=trust lets any process in the sandbox connect without a password. Only code you run there, and you through the forward below, can reach 127.0.0.1: nothing on the internet can connect in to a sandbox.
  • -k /tmp puts 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/postgres

The 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/bin

To 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

Facts on this page were checked on 25 September 2026.