Skip to content
Docs

How to use Vercel Container Registry

Push, store, and pull OCI container images with Vercel Container Registry, then deploy them to Vercel Functions and Vercel Sandbox.

4 min read
Last updated July 11, 2026

Getting a container image from your laptop to a running function usually means standing up a separate registry, wiring up credentials, and configuring your build pipeline to push there. Vercel Container Registry (VCR) removes that step by giving every Vercel project a built-in, OCI-compatible registry at vcr.vercel.com. You push images with the same docker tooling you already use. Vercel Functions and Vercel Sandbox pull them directly, and VCR optimizes images automatically for Fluid compute.

In this guide, you'll learn how to:

  • Authenticate Docker with Vercel Container Registry
  • Build and tag an OCI image locally
  • Push the image to your project's registry
  • Pull the image for local verification or CI
  • Reference the image from a Vercel Function or Vercel Sandbox
  • Work within VCR's size, format, and plan limits

Before you begin, make sure you have:

  • A Vercel account with access to the project you want to push to
  • Docker installed and running locally, and optionally Docker Buildx for multi-platform builds
  • The Vercel CLI installed and authenticated with vercel login
  • A local project linked to your Vercel project with vercel link

Every Vercel project has its own namespace in VCR. A full image reference has four parts: the registry host, your team slug, your project slug, and the repository name with a tag or digest.

vcr.vercel.com/team-slug/project-slug/my-repository:latest

VCR speaks the Docker Registry HTTP API v2 and stores images in OCI format, so docker login, docker push, docker pull, and docker tag work without extra tooling. When you push an image, VCR builds an optimized snapshot in the background so it boots faster inside Vercel Functions and Vercel Sandbox on Fluid compute.

From the root of the project you want to push images for, link the local directory to your Vercel project and download its environment variables:

Terminal
vercel link
vercel env pull .env.local
source .env.local

vercel env pull writes VERCEL_OIDC_TOKEN into .env.local, which you'll use as the Docker password. The token is scoped to the linked project and expires after 12 hours, so refresh it with vercel env pull before the 12-hour window closes.

Log Docker in to vcr.vercel.com using the OIDC token as the password and oidc as the username:

Terminal
printf '%s' "$VERCEL_OIDC_TOKEN" | docker login vcr.vercel.com \
--username oidc \
--password-stdin

Docker responds with Login Succeeded when the credentials are accepted.

If OIDC isn't available in your environment (for example, in a CI system that doesn't run vercel env pull), create a token on the Account Tokens page and log in with your team ID as the username:

Terminal
printf '%s' "$VERCEL_TOKEN" | docker login vcr.vercel.com \
--username "$VERCEL_TEAM_ID" \
--password-stdin

Tag the image with the full VCR path so docker push knows where to send it. Replace team-slug and project-slug with the values for your linked project.

Terminal
docker build -t vcr.vercel.com/team-slug/project-slug/my-repository:latest

If the repository doesn't exist yet, VCR creates it the first time you push, as long as you have access to the project.

To push the image, use docker push with the tag you built:

Terminal
docker push vcr.vercel.com/team-slug/project-slug/my-repository:latest

For production images, build with Buildx and zstd compression. This produces smaller layers and faster snapshot boots on Fluid compute, and lets you build for both linux/amd64 and linux/arm64 in one step:

Terminal
docker buildx build \
--platform linux/amd64,linux/arm64 \
--output "type=image,name=vcr.vercel.com/team-slug/project-slug/my-repository:latest,push=true,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true"

To confirm the push landed, pull the image back to your machine or into a CI job using the same full path:

Terminal
docker pull vcr.vercel.com/team-slug/project-slug/my-repository:latest

You can also browse tags and digests from the Images tab in your project dashboard.

To run the image as a Vercel Function, add a Dockerfile.vercel (or Containerfile.vercel) at the root of your project. Vercel detects it during the build, pushes the image to VCR, and routes traffic to the resulting function.

Dockerfile.vercel
FROM node:26-alpine
RUN npm i -g srvx
WORKDIR /app
COPY server.ts .
# srvx listens on $PORT by default
CMD ["srvx", "--prod"]

Deploy with vercel deploy or by pushing to a connected Git branch. Container-based functions serve HTTP on port 80 by default and follow the same Active CPU pricing and limits as any other Vercel Function.

To boot a sandbox from a custom VCR image, pass the repository and tag to Sandbox.create():

import { Sandbox } from "@vercel/sandbox";
const sandbox = await Sandbox.create({
image: "my-repository:latest",
});

The sandbox starts with your image's packages and binaries already in place instead of the built-in runtime, which is useful when your workload depends on system libraries or specific toolchains.

Keep these limits in mind when planning your images:

  • Compressed layer size: up to 500 MB per layer
  • Total image size: up to 15 GB, calculated from compressed layers plus the config blob
  • Manifest body: up to 4 MB
  • Compression: VCR accepts gzip or zstd compression only. Uncompressed OCI layers are rejected.
  • Architectures: linux/amd64 and linux/arm64 (single-platform manifests must declare os and architecture in the image config)
  • Repositories, images, and tags per plan: Hobby (10 / 50 / 1,000), Pro (1,000 / 10,000 / 10,000), and Enterprise (5,000 / 50,000 / 50,000)
  • Storage cost: $0.10 per GB per month

Additional practices to keep in mind:

  • Build with Docker Buildx and zstd compression for faster boots on Fluid compute
  • Use immutable tags (like a commit SHA) for production images, and reserve latest for local iteration
  • Push from your Vercel build environment when possible; it's authenticated for the current project automatically

Was this helpful?

supported.