Images
Custom images give you full control over the sandbox environment. Define the Linux distribution, system packages, language toolchains, and filesystem layout in a Dockerfile, and your sandboxes boot straight into that environment with nothing to install at runtime. Reuse an image you already build for CI or production, or build one specifically for your agents.
Sandbox pulls images from Vercel Container Registry (VCR), a project-scoped registry for OCI images. Pin environments with tags and digests, share a repository to reuse one image across projects and teams, or mark a repository public to let any other team use your image.
When you pass an image to Sandbox.create(), Vercel resolves the reference against VCR and boots the sandbox from that image's filesystem. Bare repository names resolve against the authenticated project, and team-scoped references resolve against the referenced team and project.
You can use a Vercel Managed Image (VMI), your own custom images, a custom image shared with your team, or a public image published by another team.
Vercel maintains a set of managed images containing common tools and languages, published under the vercel/sandbox scope.
| Image | Base | Contents |
|---|---|---|
vercel/sandbox/universal:latest | vercel/sandbox/ubuntu | Node.js LTS (24), Python (3.14), coding agents, utilities |
vercel/sandbox/node:22|24|26 | vercel/sandbox/ubuntu | Node.js (major pinned), pnpm |
vercel/sandbox/python:3.14 | vercel/sandbox/ubuntu | Python 3.14 (pinned), pip, venv, uv |
vercel/sandbox/arch:latest | archlinux:latest | Arch Linux, yay (AUR), base-devel, git |
vercel/sandbox/ubuntu:latest | ubuntu:26.04 | Ubuntu + sudo |
The vercel/sandbox/universal:latest image is the default for sandboxes that don't specify an image. You can pin to a specific tag or digest, or use any of the other available managed image:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
image: 'vercel/sandbox/universal@sha256:...', // Use a specific digest of the universal image
// image: 'vercel/sandbox/node:24', // Use the Node.js 24 image
// image: 'vercel/sandbox/ubuntu', // Use the latest Ubuntu image
});Every managed image gets a nightly release. Rolling tags (e.g. universal:latest) and the major-version tags (node:26 or python:3.14) pick up operating system updates automatically, including security patches. New releases of Node.js, Python, preinstalled coding agents and other dependencies are picked up in nightly releases via an automated pipeline.
Images are open source, with their source code available on GitHub. To propose changes, open a pull request.
To avoid repeated work in every sandbox, or if you require a specific set of tools, libraries, or system packages, you can build your own custom image and push it to VCR. Custom images can also be shared with other teams or marked public for any team to use.
VCR only serves an image to Sandbox once it has prepared an optimized linux/amd64 build. After you push an image, VCR reports a readiness state on the repository details page:
| Status | Meaning |
|---|---|
Ready | VCR prepared the image and Sandbox can use it. |
Preparing | VCR is preparing a linux/amd64 image. |
Unoptimized | The image is pullable from VCR, but it is not linux/amd64 and cannot be used in Sandbox. |
If Sandbox.create() returns image_not_ready, retry after preparation finishes.
If the Dockerfile defines WORKDIR, new commands start in that directory. Otherwise, commands start from /.
First, push an OCI image to Vercel Container Registry (VCR):
vercel vcr build docker . my-repository:latest --pushAfter the image is pushed, create the sandbox with the repository reference for the authenticated project:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
image: 'my-repository:latest',
});
try {
const result = await sandbox.runCommand('pwd');
console.log(await result.stdout());
} finally {
await sandbox.stop();
}A team can share a VCR repository to give other teams read access to its images. Once a repository is shared with your team, create sandboxes from its images with a team-scoped reference:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
image: 'other-team/their-project/shared-repository:latest',
});Sharing also works within a team. VCR repositories belong to a single project, so by default a sandbox can only use images from its own project. To use a repository's images from other projects in your team, share the repository with your own team:
import { Sandbox } from '@vercel/sandbox';
// Created in a different project of my-team
const sandbox = await Sandbox.create({
image: 'my-team/other-project/my-repository:latest',
});A team can mark a VCR repository as public to give any other team read access to its images. Anyone on Vercel can then create sandboxes from the repository's images with a team-scoped reference:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
image: 'other-team/their-project/public-repository:latest',
});Vercel uses that approach to make Vercel Managed Images (VMI) accessible to any team.
image accepts a repository name, tag, digest, or team-scoped reference:
| Reference | What it resolves |
|---|---|
my-repository | The latest tag in the authenticated project |
my-repository:v1 | A specific tag in the authenticated project |
my-repository@sha256:... | A specific digest in the authenticated project |
team-slug/project-slug/my-repository | The latest tag in the referenced team and project |
team-slug/project-slug/my-repository:v1 | A specific tag in the referenced team and project |
team-slug/project-slug/my-repository@sha256:... | A specific digest in the referenced team and project |
vcr.vercel.com/team-slug/project-slug/my-repository:v1 | Same as the team-scoped reference, using the fully qualified registry URL |
The vcr.vercel.com/ prefix is optional and works with any team-scoped form, including tags and digests.
A team-scoped reference resolves against the referenced team and project. It works when the reference points at the authenticated project, or at a repository that has been shared with your team or marked as public. If the repository doesn't exist or isn't shared with your team, Sandbox.create() returns not_found.
Was this helpful?