Mounts
Bind-mount host directories and share named volumes between sandboxes.
Bind-mount a host directory
Ship local code into the sandbox without baking it into the snapshot:
import { runInSandbox } from "@beamhop/lightbox";
const out = await runInSandbox(
{
snapshot: "node-ci",
name: "test-run",
mounts: { "/work": "./my-project" }, // host path → guest path
workdir: "/work",
},
(sb) => sb.shell("npm test"),
);
console.log(out.stdout());
The host directory is visible inside the guest at /work for the lifetime
of the sandbox. Writes from the guest appear on the host.
Share a persistent volume across sandboxes
A named volume survives sandbox restarts and can be mounted into many sandboxes — perfect for caches:
import { launchSandbox } from "@beamhop/lightbox";
async function run(jobId: string) {
const sb = await launchSandbox({
snapshot: "node-ci",
name: `job-${jobId}`,
mounts: {
"/work": "./my-project",
"/root/.npm": { volume: "npm-cache" }, // shared across all jobs
},
workdir: "/work",
});
await sb.shell("npm ci && npm test");
await sb.stop();
}
await Promise.all([run("1"), run("2"), run("3")]);
The first launchSandbox creates the npm-cache volume on demand. The
second and third calls reuse it. After all three jobs finish, the cache
persists for the next run.
Configure quota / labels up front
If you care about the volume’s quota or labels, create it explicitly. After
that, just reference the name in mounts:
import { ensureVolume, launchSandbox } from "@beamhop/lightbox";
await ensureVolume("npm-cache", {
quotaMib: 4096,
labels: { team: "platform", purpose: "ci-cache" },
});
const sb = await launchSandbox({
snapshot: "node-ci",
name: "ci-1",
mounts: { "/root/.npm": { volume: "npm-cache" } },
});
ensureVolume is idempotent — calling it on an existing volume is a no-op.
Quota and labels are only applied on initial creation; they’re not edited on
subsequent calls.
Clean up
import { removeVolume } from "@beamhop/lightbox";
await removeVolume("npm-cache"); // idempotent — silent if it doesn't exist