Runtime

How to run Docker Compose integration tests in a cloud sandbox

Upload the repo, run sudo enable-docker, then docker compose up --wait and docker compose run your test service; copy the reports out.

On Runtime, a Compose stack gets a whole microVM, not a shared Docker host. Every sandbox is a Firecracker machine with its own kernel and its own Docker daemon, so parallel CI jobs never collide on ports, volumes or container names, and root inside one cannot touch another. A stack that idles between requests pays mostly for memory: $0.03125 an hour for 2 vCPU and 4 GiB at rest and $0.08 at full load (pricing). On 25 September 2026, Ubuntu 24.04's updates carried Docker 29.1.3 and Compose 2.40.3, the archive enable-docker installs from.

Run the suite

The pattern: start the services the tests depend on, wait until their health checks pass, run the test container once, keep its exit code.

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ diskMiB: 10_240, timeoutSeconds: 3600 });const long = { timeoutMs: 1_800_000, cwd: "/workspace/app" } as const;await sbx.files.upload("./", "/workspace/app");await sbx.exec("sudo enable-docker", { check: true, timeoutMs: 600_000 });await sbx.exec("docker compose up -d --build --wait --wait-timeout 300 db cache api", {  ...long,  check: true,});const tests = await sbx.exec("docker compose run --rm tests", {  ...long,  onStdout: (text) => process.stdout.write(text),});if (tests.exitCode !== 0) {  const logs = await sbx.exec("docker compose logs --no-color --tail 200 api db", long);  console.error(logs.stdout);}await sbx.files.download("/workspace/app/reports", "./reports");process.exitCode = tests.exitCode ?? 1;
Pythonimport sysfrom withruntime import Sandboxwith Sandbox.create(disk_mib=10_240, timeout_seconds=3600) as sbx:    sbx.files.upload(".", "/workspace/app")    sbx.exec("sudo enable-docker", check=True, timeout_ms=600_000)    sbx.exec("docker compose up -d --build --wait --wait-timeout 300 db cache api",             cwd="/workspace/app", check=True, timeout_ms=1_800_000)    tests = sbx.exec("docker compose run --rm tests", cwd="/workspace/app",                     timeout_ms=1_800_000, on_stdout=sys.stdout.write)    if tests.exit_code != 0:        print(sbx.exec("docker compose logs --no-color --tail 200 api db",                       cwd="/workspace/app").stdout, file=sys.stderr)    sbx.files.download("/workspace/app/reports", "reports")    sys.exit(tests.exit_code or 0)

For the reports to reach the sandbox's disk, the tests service mounts the folder, for example volumes: ["./reports:/app/reports"] in the Compose file. Output streams while the suite runs, and a failed run brings the last 200 log lines of the services it talked to.

The Compose flags that matter in CI

From Docker's reference for docker compose up, read 25 September 2026:

Flag What it does
--wait Waits for services to be running or healthy; implies detached mode
--wait-timeout 300 Gives up after that many seconds
--build Builds images before starting containers
--exit-code-from tests Returns that service's exit code; implies --abort-on-container-exit
--abort-on-container-exit Stops every container when one stops; cannot be combined with -d

--wait is only as good as the health checks: a service with no healthcheck counts as ready once it is running. up --exit-code-from tests is the one-command alternative to run --rm tests when the test service is part of the default stack.

Shard the suite across copies of a warm stack

Building images and seeding a database is the slow part. Do it once, then fork the sandbox: each copy starts with the same files, memory and running processes, containers included, and runs a different slice of the tests.

TypeScriptimport { Sandbox } from "withruntime";await using base = await Sandbox.create({ diskMiB: 10_240, timeoutSeconds: 3600 });const app = { cwd: "/workspace/app", check: true, timeoutMs: 1_800_000 } as const;await base.files.upload("./", "/workspace/app");await base.exec("sudo enable-docker", { check: true, timeoutMs: 600_000 });await base.exec("docker compose up -d --build --wait db cache api", app);await base.exec("docker compose run --rm tests npm run seed", app);const shards = await base.fork({ count: 4 });const results = await Promise.all(  shards.map((copy, i) =>    copy.exec(`docker compose run --rm tests npx jest --shard=${i + 1}/4`, {      cwd: "/workspace/app",      timeoutMs: 1_800_000,    }),  ),);await Promise.all(shards.map((copy) => copy.stop()));process.exitCode = results.some((r) => r.exitCode !== 0) ? 1 : 0;

A fork pauses the source for about a second on a fresh sandbox, longer the more memory it holds, and each copy is billed as its own sandbox. A trial account runs eight sandboxes at once and a paid one starts at 100.

Look inside a stack from your machine

Leave the sandbox running and forward the ports Compose published, several in one command:

Terminalruntime sandbox port-forward <id> 8080 15432:5432 6379

Now localhost:8080 is the API, localhost:15432 its Postgres, and localhost:6379 its Redis, all over Runtime's authenticated API with nothing opened to the internet (forward ports). To show the app to someone else, share the port as a private HTTPS preview instead.

If you step away mid-debug, runtime sandbox pause <id> keeps the containers exactly as they are, memory included, and the next command wakes the sandbox (pause and resume).

Sizing and limits

  • Disk. Images and volumes live on the sandbox's disk. The samples ask for 10 GiB, the free trial's largest; paid sandboxes can ask for more.
  • Service-to-service calls. Clients that honour HTTP_PROXY need the other services' names in NO_PROXY; the Docker how-to shows the two lines to add.
  • Outbound ports. Pulls go through mirror.gcr.io first and reach the web on 443. A trial sandbox reaches ports 80 and 443 only; a paid one reaches any port, such as a staging database outside the stack.
  • Startup. Put Docker in a custom image with enable-docker --no-start and every sandbox skips the install.

For agents that build and run multi-service apps, see preview agent-built apps and a coding agent sandbox.

Sources

Facts on this page were checked on 25 September 2026.