How can I expose ports from sandbox?
How can expose ports? For example if I start a nextjs dev server inside the sandbox and I want to access it from outside.
Now I have a comprehensive picture. Here's the full answer:
##Exposing Ports in Vercel Sandbox
There are two ways to expose ports, depending on whether you're using the SDK or the CLI.
###SDK (@vercel/sandbox)
When creating a sandbox, pass the ports array to Sandbox.create(). You can expose up to 4 ports. Then use the sandbox.domain(port) method to get the public URL:
import { Sandbox } from "@vercel/sandbox";
const sandbox = await Sandbox.create({
source: {
url: "https://github.com/vercel/sandbox-example-next.git",
type: "git",
},
ports: [3000], // <-- expose port 3000
timeout: 300000,
});
// Start your dev server (detached so it runs in the background)
await sandbox.runCommand({
cmd: "npm",
args: ["run", "dev"],
detached: true,
});
// Get the public URL for port 3000
const url = sandbox.domain(3000);
console.log(url); // e.g. "https://<subdomain>.vercel.run"
How it works under the hood:
- The
portsarray is sent to the API when creating the sandbox (sandbox.tsL479). - The API returns routes — an array of
{ url, subdomain, port }objects (validators.tsL65-68). - The
domain(port)method looks up the matching route and returnshttps://<subdomain>.vercel.run(session.tsL616-623).
###CLI (sandbox)
Use the --publish-port (or -p) flag when creating or running a sandbox:
# Create a sandbox with port 3000 exposed
sandbox create --publish-port 3000
# Or run a command directly with a published port
sandbox run -p 3000 -- npm run dev
The port will be published to <DOMAIN>.vercel.run, as described in the CLI docs.
###Key Details
| Detail | Value |
|---|---|
| Max ports | Up to 4 ports per sandbox |
| Public URL format | https://<subdomain>.vercel.run |
| SDK method | sandbox.domain(portNumber) |
| CLI flag | --publish-port <PORT> or -p <PORT> |
You can see a full working example in examples/dev-server/dev-server.ts which clones a Next.js repo, installs dependencies, starts the dev server on port 3000, and opens the public URL in the browser.
But after providing the source object to the create function, that sandbox doesn't install dependencies or anything, right?
Exactly as expected. The source is just passed straight to client.createSandbox() at line 477 — it tells the API what files to put in the sandbox, nothing more.
That's why every example you see in this repo explicitly runs install and build commands as separate steps. For instance, from the dev-server example:
// 1. Create the sandbox (just clones the repo files)
const sandbox = await Sandbox.create({
source: {
url: 'https://github.com/vercel/sandbox-example-next.git',
type: 'git',
},
ports: [3000],
});
// 2. YOU have to install dependencies yourself
const install = await sandbox.runCommand({
cmd: 'npm',
args: ['install', '--loglevel', 'info'],
});
// 3. YOU have to start the dev server yourself
await sandbox.runCommand({
cmd: 'npm',
args: ['run', 'dev'],
detached: true,
});
So the workflow is always: seed the filesystem → run your own commands (install, build, dev, etc.). The sandbox gives you a blank Linux VM with your files in it — everything else is up to you.
If you want to skip the install step on subsequent runs, you could take a snapshot after installing dependencies and then create future sandboxes from that snapshot instead.
Can I use pnpm or what package manager is installed in the machine?
Here's what I can tell you based on the source code:
##What's pre-installed in the sandbox
The sandbox runs Amazon Linux 2023 and the Node runtimes (e.g. node24, node22) include node and npm on the PATH (at /vercel/runtimes/node22/bin etc.).
The pre-installed system packages are: git, tar, gzip, unzip, curl, openssl, procps, findutils, which.
Notably, only npm is mentioned and used across all examples and tests — there's no evidence in the codebase that pnpm, yarn, or bun come pre-installed.
##How to use pnpm (or yarn/bun)
You have two options:
###1. Install it via npm at runtime
// Install pnpm globally first
await sandbox.runCommand({
cmd: 'npm',
args: ['install', '-g', 'pnpm'],
});
// Then use it
await sandbox.runCommand({
cmd: 'pnpm',
args: ['install'],
});
###2. Use corepack (ships with Node)
Since Node ships with corepack, you could try enabling it:
await sandbox.runCommand({
cmd: 'corepack',
args: ['enable'],
});
// Then pnpm/yarn should be available
await sandbox.runCommand({
cmd: 'pnpm',
args: ['install'],
});
###3. Snapshot it to avoid reinstalling every time
If you don't want to pay the cost of installing pnpm on every sandbox creation, you can take a snapshot after installing it, and then create future sandboxes from that snapshot:
// First time: create sandbox, install pnpm, snapshot it
const sandbox = await Sandbox.create();
await sandbox.runCommand({ cmd: 'npm', args: ['install', '-g', 'pnpm'] });
const snapshot = await sandbox.snapshot();
// Save snapshot.id for later
// Future runs: start from snapshot (pnpm already installed)
const sandbox2 = await Sandbox.create({
source: { type: 'snapshot', snapshotId: snapshot.id },
});
You can also install entirely different toolchains via dnf with sudo: true, as shown in the install-packages example which installs Go the same way.
END OF POST