Quickstart
Install the sandbox client, start OpenSandbox locally, and run your first command in a container.
Install
bun add @alineo-labs/sandbox @alineo-labs/sqlite@alineo-labs/sqlite is the ledger every command is written to. For a ledger shared by several processes, use
@alineo-labs/postgres instead — storage.
Start a local sandbox server
The quickest path runs OpenSandbox in Docker and writes alineo.config.json for you:
bunx alineo-cli initA server started this way hands out container addresses your host can't reach, so pass useServerProxy: true to
the client in the next step.
To run it on the host instead, without Docker, create ~/.sandbox.toml:
[server]
host = "127.0.0.1"
port = 8080
[runtime]
type = "docker"
execd_image = "opensandbox/execd:v1.0.22"
[docker]
network_mode = "bridge"
[ingress]
mode = "direct"
[egress]
mode = "dns"and start it with uvx opensandbox-server. Container addresses are reachable from the host here, so no proxy is
needed.
Hello world
import { Sandbox } from "@alineo-labs/sandbox";
import { SQLiteAdapter } from "@alineo-labs/sqlite";
const client = new Sandbox({
baseUrl: "http://localhost:8080",
adapter: new SQLiteAdapter("./ledger.db"),
useServerProxy: true, // with `alineo init`; leave it off for `uvx opensandbox-server`
});
const sb = await client.sandbox({
image: "ubuntu:22.04",
resources: { cpu: "500m", memory: "512Mi" },
name: "hello-world",
});
try {
await sb.exec('echo "hello world"').pipe(process.stdout);
} finally {
await sb.close();
}bun hello.ts
# hello worldresources is required — OpenSandbox rejects a sandbox without it. There is no connect() on the client and no
close() on the adapter: it initialises on first use and closes itself when the process exits.
Capture output
Await the ExecHandle directly to get { stdout, stderr, exitCode }:
const sb = await client.sandbox({
image: "node:22-slim",
resources: { cpu: "500m", memory: "256Mi" },
});
try {
const { stdout: nodeVersion } = await sb.exec('node -e "process.stdout.write(process.version)"');
await sb.exec(`echo "Running on Node ${nodeVersion.trim()}"`).pipe(process.stdout);
} finally {
await sb.close();
}Write and read files
const sb = await client.sandbox({
image: "ubuntu:22.04",
resources: { cpu: "500m", memory: "256Mi" },
});
try {
await sb.writeFile("/tmp/hello.txt", "hello from alineo\n");
const content = await sb.readFile("/tmp/hello.txt");
console.log(content.trim()); // "hello from alineo"
} finally {
await sb.close();
}Next steps
- How it works — the full mental model
- exec & execCode — every exec option and streaming mode
- Checkpoint and resume — pick a crashed run up where it stopped
- Sandbox client reference — every client option
- Workflow SDK —
retry,when,forEachandparallel, with oneawaitat the end