# How to run Docker inside a sandbox Run `sudo enable-docker` once in the sandbox; it installs Docker Engine, Buildx and Compose, and `docker` then works as the sandbox user. **On Runtime, Docker runs inside a microVM of its own.** Every sandbox is a Firecracker microVM with its own Linux kernel, so the Docker daemon, your containers and `docker build` all run inside that machine, and root inside it still cannot change its network, CPU, memory or cost. `docker run`, `docker build` and `docker compose` work as they do on any Linux machine. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while its containers wait and $0.08 an hour with both CPUs busy ([pricing](/docs/pricing)). ## Run a container ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 1800 }); const slow = { check: true, timeoutMs: 600_000 } as const; await sbx.exec("sudo enable-docker", slow); const hello = await sbx.exec("docker run --rm hello-world", slow); console.log(hello.stdout); ``` ```python check from withruntime import Sandbox with Sandbox.create(disk_mib=8192, timeout_seconds=1800) as sbx: sbx.exec("sudo enable-docker", check=True, timeout_ms=600_000) hello = sbx.exec("docker run --rm hello-world", check=True, timeout_ms=600_000) print(hello.stdout) ``` ```bash no-run sudo enable-docker # once per sandbox; the first docker command runs it too docker run --rm hello-world docker compose up -d ``` `enable-docker` installs Docker Engine, Buildx and Compose from Ubuntu's archive and starts them. After it, `docker` works without `sudo`. Installs take longer than the 60-second default for a command, so give them `timeoutMs`. ## Run a Compose project and open it in a browser Upload the project, start it, and share the published port as a private HTTPS preview: ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ diskMiB: 16_384, timeoutSeconds: 3600 }); const slow = { check: true, timeoutMs: 900_000 } as const; await sbx.files.upload("./app", "/workspace/app"); await sbx.exec("sudo enable-docker", slow); await sbx.exec("docker compose up -d --build", { ...slow, cwd: "/workspace/app" }); const preview = await sbx.previews.create(8080); // the port compose publishes console.log(preview.url, preview.token); ``` ```python check from withruntime import Sandbox with Sandbox.create(disk_mib=16_384, timeout_seconds=3600) as sbx: sbx.files.upload("./app", "/workspace/app") sbx.exec("sudo enable-docker", check=True, timeout_ms=900_000) sbx.exec("docker compose up -d --build", cwd="/workspace/app", check=True, timeout_ms=900_000) preview = sbx.previews.create(8080) # the port compose publishes print(preview["url"]) ``` A published port (`-p 8080:80`) listens inside the sandbox, so a preview shares it and `runtime sandbox port-forward 8080` brings it to your own machine. A preview is private by default: a request needs its token, sent as the `x-runtime-preview-token` header ([share a port](/docs/javascript#share-a-port)). ## Start every sandbox with Docker ready Build a custom image whose recipe installs Docker, and every sandbox made from it skips the install. `--no-start` leaves the daemon to start on the first `docker` command: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "docker", recipe: { commands: ["enable-docker --no-start"] }, }); await using sbx = await runtime.sandboxes.create({ image: "docker", diskMiB: 8192 }); await sbx.exec("docker run --rm hello-world", { check: true, timeoutMs: 300_000 }); ``` ```python check from withruntime import Runtime runtime = Runtime() runtime.images.build(name="docker", recipe={"commands": ["enable-docker --no-start"]}) with runtime.sandboxes.create(image="docker", disk_mib=8192) as sbx: sbx.exec("docker run --rm hello-world", check=True, timeout_ms=300_000) ``` Building an image is free; a stored image is charged on its size, and the free trial stores your first three free ([custom images](/docs/images)). An image can also come from your own Dockerfile, which runs as a Runtime image without Docker in the sandbox at all. ## Options and limits | What | How it works | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | Install | `sudo enable-docker`, once per sandbox, or in an image with `enable-docker --no-start` | | Daemon | Starts on the first `docker` command after a boot; a sandbox that runs no containers spends nothing on it | | Disk | Images and containers live on the sandbox's disk. Ask for more than the default 4 GiB, such as `diskMiB: 8192` | | Pulls | Docker Hub images come through Google's public mirror, `mirror.gcr.io`, first | | Internet from containers | Through the sandbox's proxy: each container and each `docker build` step gets `HTTP_PROXY` and `HTTPS_PROXY` set to `http://172.17.0.1:10800` | | Network rules | The sandbox's allow and deny lists apply to its containers too | | Published ports | Listen inside the sandbox; share them with a preview or `runtime sandbox port-forward` | The default 4 GiB sandbox had about 2.5 GiB free in a measurement on 24 September 2026, which is why the samples ask for 8 GiB or more ([disk, CPU and memory](/docs/sandbox-environment#disk-cpu-and-memory)). **Compose services calling each other.** Services reach each other by name, as anywhere. An HTTP client that honours `HTTP_PROXY` sends a call to another service through the proxy unless that name is in `NO_PROXY`, so list the names a service calls in its `environment`, under both spellings: ```yaml services: web: build: . ports: ["8080:3000"] environment: NO_PROXY: localhost,api,db no_proxy: localhost,api,db api: build: ./api db: image: postgres:17 ``` ## Where this helps - An agent that builds and runs a multi-service app can do it in a sandbox and show you the result through a preview ([preview agent-built apps](/use-cases/preview-agent-built-apps)). - Integration tests that need a real Postgres or Redis get their own, thrown away with the sandbox. - A [coding agent](/use-cases/coding-agent-sandbox) can run a repository's own `docker compose` setup unchanged. For why a microVM and not a container is the boundary, see [microVM vs container](/compare/microvm-vs-container) and [Firecracker vs Docker](/compare/firecracker-vs-docker). ## Start ```bash no-run npx withruntime sandbox run --trial --timeout 600 -- bash -c 'sudo enable-docker && docker run --rm hello-world' ``` New accounts get 50 free sandbox hours, no card. The first run prints a link to approve in your browser. Full details are in [Docker in the sandbox environment](/docs/sandbox-environment#docker). Facts on this page were checked on 25 September 2026.