# How to Move Files In and Out of a Cloud Sandbox

> Use sbx scp for files and folders of any size, in either direction. From code, the SDK and MCP read and write single files up to 5 MiB, and the SDK downloads up to 50 MiB. Clone repos inside the sandbox with git, and pull files from a stopped sandbox's snapshot without resuming it.

Source: https://sandboxer.com/guides/move-files-in-and-out-of-a-sandbox. Updated 2026-10-27.

**Short answer:** Use sbx scp for files and folders of any size, in either direction. From code, the SDK and MCP read and write single files up to 5 MiB, and the SDK downloads up to 50 MiB. Clone repos inside the sandbox with git, and pull files from a stopped sandbox's snapshot without resuming it.

## Which method to use

| Method | Direction | Best for | Limits |
| - | - | - | - |
| `sbx scp` | Both | Single files and whole folders, large files | No size cap from Sandboxer; limited by your bandwidth and the sandbox's disk |
| `rsync` over the CLI tunnel | Both | Syncing a folder again and again (sends only changes) | Same as `scp` |
| SDK `files.write` / `read` / `readBytes` | Both | Config, inputs and results from your code | 5 MiB per file (`413 file_too_large` above that) |
| SDK `files.download` | Out | Build artifacts and archives from code | 50 MiB per file |
| MCP `write_file` / `read_file` | Both | An agent moving text or small binaries (base64) | 5 MiB per file |
| `git clone` inside the sandbox | In | Source code, at the sandbox's own network speed | None from Sandboxer |
| Hosted port | Out | A download link for a browser or another machine | Anyone with the URL can fetch while it's hosted |
| `sbx snapshot pull` | Out | Getting files from a stopped sandbox without resuming it | Whole snapshot or one `--path` |

The files API and the SDK work on paths under `/home/user` or `/tmp`. Relative paths start at `/home/user`.

## sbx scp: files and folders, both directions

`sbx scp` runs your installed OpenSSH `scp` through the same authenticated tunnel as `sbx ssh`, so it works behind any firewall and needs no key setup. Use `<sandbox-id>:/path` as the remote side.

```bash
id=sb_yke1vvb5ca

# Upload a file and a folder (51 files)
sbx scp ./data.csv $id:/home/user/
sbx scp -r ./my-project $id:/home/user/

sbx exec $id "ls ~ && ls ~/my-project/src | wc -l"
# data.csv
# my-project
# 51

# Download a file and a folder
sbx scp $id:/home/user/out/results.tar.gz ./
sbx scp -r $id:/home/user/out/reports ./
ls reports
# r1.txt  r2.txt  r3.txt
```

When both ends have `tar`, a recursive copy to a new directory is sent as one tar stream, which is much faster for many small files.

Large files go the same way. A 100 MiB file of random bytes took under 10 seconds from our test machine (one measured run on prod, 2026-10-27):

```bash
head -c 104857600 /dev/urandom > big.bin
time sbx scp ./big.bin $id:/tmp/
# real    0m9.575s
```

Your own upload speed sets the pace, so expect a 1 GB file to take roughly ten times as long on the same connection.

## rsync: sync a folder repeatedly

For a folder you change and push again and again, `rsync` sends only what changed. `sbx ssh-config` prints an SSH config stanza for the pseudo-host `*.sbx`, so any OpenSSH tool can reach a sandbox by id. Append it to `~/.ssh/config` with `sbx ssh-config --install`, then:

```bash
rsync -av ./my-project/ $id.sbx:my-project/
# sending incremental file list
# README.md
#
# sent 1,061 bytes  received 42 bytes  441.20 bytes/sec
```

Only `README.md`, the one file we edited, was sent. The same stanza works for VS Code Remote-SSH (`sb_yke1vvb5ca.sbx`).

## SDK: read and write files from code

The TypeScript SDK (`npm i sandboxer-sdk`) reads `SANDBOXER_API_KEY` from the environment. This script ran against a live sandbox:

```typescript
import { Sandboxer } from "sandboxer-sdk";
import { readFileSync, writeFileSync } from "node:fs";

const sandbox = await Sandboxer.get("sb_yke1vvb5ca");

// Write text and bytes (parent directories are created)
await sandbox.files.write("inbox/config.json", JSON.stringify({ retries: 3 }));
await sandbox.files.write("inbox/data.csv", readFileSync("data.csv"));

// Read them back
console.log(await sandbox.files.read("inbox/config.json"));
const bytes = await sandbox.files.readBytes("inbox/data.csv");
console.log("data.csv:", bytes.length, "bytes");

// Download a larger artifact (up to 50 MiB)
const tarball = await sandbox.files.download("out/results.tar.gz");
writeFileSync("results.tar.gz", tarball);
console.log("results.tar.gz:", tarball.length, "bytes");
```

```text
{"retries":3}
data.csv: 24 bytes
results.tar.gz: 182 bytes
```

The Python SDK has the same calls: `files.write`, `files.read`, `files.read_bytes` and `files.download`.

### What happens at the limits

We tried each limit. Errors arrive as a `SandboxerError` with status and code:

```text
write 6 MiB:      413 file_too_large  Files may be at most 5 MiB; use scp for larger files.
readBytes 20 MiB: 413 file_too_large  Files over 5 MiB: use GET /sandboxes/{sandboxId}/artifacts (50 MiB) or scp.
download 20 MiB:  20971520 bytes
download 60 MiB:  413 file_too_large  Artifacts may be at most 50 MiB; use scp for larger files.
```

So: up to 5 MiB, use `read`/`write`. Up to 50 MiB out, use `download`. Anything bigger, or anything going in over 5 MiB, use `sbx scp`.

## MCP: files for an agent

An agent connected to the [Sandboxer MCP server](/docs/mcp) gets `write_file` and `read_file`, each up to 5 MiB. Text is `utf8` by default; pass `"encoding": "base64"` for binary. These are real results from the local `sbx mcp` server (each call also passed `sandboxId`, left out here):

```json
{"name": "write_file", "arguments": {"path": "notes/todo.md", "content": "- ship the guide\n"}}
→ "Wrote 17 bytes to /home/user/notes/todo.md."

{"name": "write_file", "arguments": {"path": "notes/pixel.bin", "content": "AAEC/w==", "encoding": "base64"}}
→ "Wrote 4 bytes to /home/user/notes/pixel.bin."

{"name": "read_file", "arguments": {"path": "notes/pixel.bin", "encoding": "base64"}}
→ "AAEC/w=="
```

Without `sandboxId`, the tools use the sandbox this MCP session created or used last.

## git clone inside the sandbox

Source code doesn't need to pass through your machine at all. Clone it from inside:

```bash
sbx exec $id --timeout 120 "git clone --depth 1 https://github.com/expressjs/express.git && du -sh express"
# Cloning into 'express'...
# 2.0M    express
```

For private GitHub repos, attach them to an [environment](/docs/environments) with `sbx env add-repo base acme/web --branch main`. New sandboxes clone them into `/home/user/<repo>` with a token, so `git push` works too when the environment passes GitHub credentials.

## Download through a hosted port

To hand a file to a browser or another machine, serve it and [host the port](/docs/hosting):

```bash
sbx exec $id --detach "cd out && python3 -m http.server 8000"
sbx host $id 8000 --public
# "url":"https://hidden-chief-hare-8000.box.sandboxer.com"

curl -sS -o results.tar.gz -w "HTTP %{http_code} %{size_download} bytes\n" \
  https://hidden-chief-hare-8000.box.sandboxer.com/results.tar.gz
# HTTP 200 182 bytes

sbx host hide $id 8000      # take the URL down; the server keeps running
```

Give the server a second to start before the first request. Without `--public` the URL is private and carries a `_token`: treat it like a password. After `sbx host hide`, the same URL returned 404.

## Get files from a stopped sandbox

A stopped sandbox costs nothing, and you don't have to resume it to get files out. Every stop writes a snapshot. Find it, list it and pull just what you need:

```bash
sbx stop $id
sbx snapshot latest $id                       # "id":"46b816c3-…","trigger":"stop"
sbx snapshot tree 46b816c3-… --path out
#        9 B  out/reports/r1.txt
#        9 B  out/reports/r2.txt
#        9 B  out/reports/r3.txt
#      182 B  out/results.tar.gz

sbx snapshot pull 46b816c3-… --path my-project -o ./restore
# ✓ Pulled 52 files (373 B) into ./restore
```

Leave out `--path` to download the whole snapshot (`home_user/` and `docker/`). Tree paths are relative to `/home/user`.

## FAQ

### What is the largest file I can upload to a sandbox?

`sbx scp` and `rsync` have no Sandboxer size limit; the sandbox's disk and your bandwidth are the limits. The SDK, the files API and MCP accept up to 5 MiB per file and return `413 file_too_large` above that.

### Can I upload binary files through MCP or the SDK?

Yes. The SDK sends bytes as base64 for you when you pass a `Uint8Array` or `Buffer`. With MCP, pass `"encoding": "base64"` to `write_file` and `read_file`.

### Can I download files without resuming a stopped sandbox?

Yes. `sbx snapshot latest <sandboxId>` finds the snapshot the stop wrote, `sbx snapshot tree <snapshotId>` lists its files, and `sbx snapshot pull <snapshotId> --path <folder>` downloads them. The sandbox stays stopped and isn't billed.

### Which paths can the files API reach?

Paths that resolve under `/home/user` or `/tmp`. Relative paths start at `/home/user`. For anything else, use `sbx scp` or `sbx exec`.

### Is there a file browser?

Yes. The dashboard has a file browser with upload and download on every sandbox page.

## Related

- [SSH and file transfer](/docs/ssh)
- [Snapshots](/docs/snapshots)
- [TypeScript SDK](/docs/sdk-typescript)
- [Pause and resume a sandbox with its memory](/guides/pause-and-resume-a-sandbox-with-memory)
