# How to upload a folder to a sandbox Call `sbx.files.upload(localDir, "/workspace/project")`; the whole folder travels as one compressed archive and keeps its permissions. **On Runtime a folder upload is one call and one archive, not a loop of file writes.** Scripts stay runnable because each file keeps its mode, and single large files go in parallel 1 MiB chunks, each checked by SHA-256, resuming after a dropped connection. There is no size limit beyond the sandbox's disk. The sandbox that receives it started in a median 207 ms from create to running, measured on 24 September 2026 ([speed](/docs/speed)). ## Upload a project and run it ```ts import { mkdtemp, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Sandbox } from "withruntime"; const project = await mkdtemp(join(tmpdir(), "project-")); await writeFile(join(project, "main.py"), "print('hi')\n"); await writeFile(join(project, "run.sh"), "python3 main.py\n", { mode: 0o755 }); await using sbx = await Sandbox.create(); await sbx.files.upload(project, "/workspace/project"); console.log(await sbx.files.list("/workspace/project")); const run = await sbx.exec("./run.sh", { cwd: "/workspace/project" }); console.log(run.stdout); ``` ```python import pathlib import tempfile from withruntime import Sandbox project = pathlib.Path(tempfile.mkdtemp()) (project / "main.py").write_text("print('hi')\n") with Sandbox.create() as sbx: sbx.files.upload(str(project), "/workspace/project") for entry in sbx.files.list("/workspace/project"): print(entry["type"], entry["size"], entry["path"]) print(sbx.exec(["python3", "main.py"], cwd="/workspace/project").stdout) ``` From a terminal, `cp` takes a sandbox path as `:/path` and copies directories whole: ```bash id=$(runtime sandbox create) mkdir -p project && echo "print('hi')" > project/main.py runtime sandbox cp ./project "${id}:/workspace/project" runtime sandbox files "${id}" /workspace --depth 2 runtime sandbox stop "${id}" ``` ## One file, or many small ones `files.write` is for a single file you already hold in memory. It makes parent directories and replaces the file atomically, and `mode` sets its permissions: ```ts import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create(); await sbx.files.write("/workspace/bin/start.sh", "#!/bin/sh\necho ready\n", { mode: 0o755 }); await sbx.files.write("/workspace/data/input.csv", "a,b\n1,2\n"); console.log(await sbx.files.exists("/workspace/data/input.csv")); ``` ```python from withruntime import Sandbox with Sandbox.create() as sbx: sbx.files.write("/workspace/bin/start.sh", "#!/bin/sh\necho ready\n", mode=0o755) sbx.files.write("/workspace/data/input.csv", "a,b\n1,2\n") print(sbx.files.exists("/workspace/data/input.csv")) ``` For a whole tree, `upload` sends one archive instead of a write per file. ## Sizes, speeds and costs | What | Figure | | -------------------- | ----------------------------------------------------------------------------------- | | How a folder travels | One compressed archive, unpacked in the sandbox with permissions kept | | Large single files | Parallel 1 MiB chunks, each checked by SHA-256, resumed after a dropped link | | Size limit | None beyond the disk | | Default disk | 4 GiB, about 2.5 GiB free (measured 24 September 2026); ask for more with `diskMiB` | | Trial disk | Up to 10 GiB | | Disk speed | Bursts to about 250 MB/s for 30 seconds, then about 40 MB/s | | Default mode | 0o644 for `write` without `mode`; `upload` keeps each file's own | | Cost | Nothing per upload; the disk is part of the sandbox | ## Mistakes to avoid - **Filling the default disk.** The system image and installed packages share `diskMiB`. A large repository or dataset needs `Sandbox.create({ diskMiB: 16_384 })` (`disk_mib` in Python) ([disk, CPU and memory](/docs/sandbox-environment#disk-cpu-and-memory)). - **Relative sandbox paths.** SDK paths are absolute. `/workspace` is the sandbox user's home; `sudo` reaches the rest. - **Uploading the same tree on every run.** When every sandbox needs the same code or packages, bake them into a [custom image](/docs/images) once, or keep data on a [volume](/docs/javascript#volumes) that outlives sandboxes. - **A script that will not run.** A file made with `write` and no `mode` gets 0o644. Pass `mode: 0o755`, or upload the folder, which keeps the mode it had. ## Related - [Download files from a sandbox](/how-to/download-files-from-a-sandbox) to get results back. - [Watch files for changes](/how-to/watch-files-for-changes) while an agent edits the upload. - [Coding agent sandbox](/use-cases/coding-agent-sandbox): check out or upload a repository, then let the agent work. - [Files](/docs/javascript#files) in the SDK reference. Facts on this page were checked on 25 September 2026.