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. Parallel CI jobs therefore never collide on ports, volumes or
container names, and root inside one sandbox 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 an hour at full load
(pricing). On 25 October 2026, Ubuntu 24.04's updates carried
Docker 29.1.3 and Compose 2.40.3. The archive that enable-docker installs from
carries the same versions.
Run the suite
The pattern is as follows. Start the services the tests depend on. Wait until their health checks pass. Run the test container once, and 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 must mount the
folder. For example, add volumes: ["./reports:/app/reports"] to the Compose
file. Output streams while the suite runs. If a run fails, the sample also
prints 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 October 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 that once, then fork the sandbox. Each copy starts with the same files, memory and running processes, including its containers. Each copy then 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 while it captures the state. The source then runs again. Each copy is billed as its own sandbox. An account without credit runs four sandboxes at once. A paid account runs 100.
Look inside a stack from your machine
Leave the sandbox running, and forward the ports Compose published. You can forward several in one command:
Terminalruntime sandboxes port-forward <id> 8080 15432:5432 6379Now localhost:8080 reaches the API, localhost:15432 reaches its Postgres,
and localhost:6379 reaches its Redis. The traffic goes through Runtime's
authenticated API. Nothing is 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, run runtime sandboxes pause <id>. It keeps the
containers exactly as they are, memory included. 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 largest size without credit. Paid sandboxes can ask for more.
- Service-to-service calls. Clients that honor
HTTP_PROXYneed the other services' names inNO_PROXY. The Docker how-to shows the two lines to add. - Outbound ports. Pulls go through
mirror.gcr.iofirst, and then reach the web on port 443. A sandbox without credit reaches ports 80 and 443 only. A paid sandbox reaches any port, such as a staging database outside the stack. - Startup. Put Docker in a custom image built with
enable-docker --no-start. Every sandbox then skips the install.
For agents that build and run multi-service apps, see preview agent-built apps and a coding agent sandbox.
Sources
- Docker,
docker compose upreference, https://docs.docker.com/reference/cli/docker/compose/up/, read 25 October 2026 - Launchpad, published sources for docker.io-app and docker-compose-v2 in Ubuntu 24.04, https://launchpad.net/ubuntu/noble, read 25 October 2026
Facts on this page were checked on 25 October 2026.