Skip to content
Docs

Running Docker on Vercel

Learn how to run Docker on Vercel by deploying OCI container images as Vercel Functions, storing them in Vercel Container Registry (VCR), and running containers in Vercel Sandbox.

6 min read
Last updated July 1, 2026

Vercel supports Docker across your whole workflow. You can deploy an application packaged as an OCI (Open Container Initiative) container image as an autoscaling Vercel Function on Fluid compute, store images in Vercel Container Registry (VCR), run containers inside Vercel Sandbox for testing, and use Docker locally to keep builds reproducible. Each path suits a different stage of building and shipping.

This guide will teach you how to run Docker on Vercel across every stage of building and shipping, from local development through to production.

Vercel works with Docker in a few distinct ways, depending on what you're trying to do:

Where Docker fitsWhat it's for
Vercel FunctionsShip a containerized app with autoscaling and Active CPU pricing
Vercel Container RegistryStore, push, and pull OCI images for functions and sandboxes
Vercel SandboxTest images and run containerized dependencies in isolation
Local DevelopmentReproduce builds and test container images before deploying

The main way to run Docker on Vercel is to deploy a container image as a Vercel Function. Add a Dockerfile.vercel (or Containerfile.vercel) to your project root, and Vercel builds the image, stores it in VCR, and serves it from a Function that scales automatically with traffic. You're billed only for the CPU your code actively uses, and the Function scales to zero when idle.

Reach for this when framework detection can't cover your app:

  • It depends on a system library such as FFmpeg or Chromium
  • It uses a framework Vercel doesn't auto-detect yet
  • It needs to run exactly as it does elsewhere

Vercel detects the Dockerfile.vercel at your project root and adds a rewrite that routes all traffic to the resulting image. Here's a minimal dynamic server using Node.js and the srvx server:

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"]
server.ts
export default {
fetch(req: Request) {
return Response.json({ ip: req.headers.get("x-forwarded-for") })
}
}

Static servers work the same way. A Dockerfile.vercel that starts FROM nginx:alpine and copies your files into /usr/share/nginx/html serves them as static content. Deploy either kind by running vercel deploy or by pushing to a connected Git repository.

To deploy more than one application in a single project, define each one as a service and route traffic between them with rewrites in vercel.json. Set each service's entrypoint to its Dockerfile path, relative to the service's root:

vercel.json
{
"services": {
"frontend": {
"root": "frontend/",
"entrypoint": "Dockerfile.vercel"
},
"backend": {
"root": "backend/",
"entrypoint": "Dockerfile.vercel"
}
},
"rewrites": [
{ "source": "/api/(.*)", "destination": { "service": "backend" } },
{ "source": "/(.*)", "destination": { "service": "frontend" } }
]
}
BehaviorWhat to expect
Port ResolutionContainers serve HTTP traffic on port 80 by default. Override it with the PORT environment variable in project settings.
Scale-inFunctions that receive no traffic for five minutes in production or 30 seconds in preview scale down. On scale-down, the container receives SIGTERM with a 30-second grace period before termination.
ObservabilityVercel broadcasts stdout and stderr logs to all inflight requests on the instance, and Vercel Observability metrics work like any other function.
Pricing and LimitsContainer image functions follow the same Active CPU pricing model and limits as every other Vercel Function.

Vercel Container Registry (VCR) is a project-scoped registry for OCI images, hosted at vcr.vercel.com. It supports the Docker Registry HTTP API v2, so docker push, docker pull, and docker tag work without new tooling. A full image reference includes the registry host, team slug, project slug, repository name, and tag:

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

Vercel provides VERCEL_OIDC_TOKEN automatically in deployments. For local use, run vercel link and vercel env pull to get the token, then authenticate and push:

Terminal
printf '%s' "$VERCEL_OIDC_TOKEN" | docker login vcr.vercel.com \
--username oidc \
--password-stdin
docker push vcr.vercel.com/team-slug/project-slug/my-repository:latest

Vercel recommends building with Docker Buildx and zstd compression for images you push to VCR. Once an image is in VCR, both Vercel Functions and Vercel Sandbox can use it. For authentication options, compression flags, and limits, see the Vercel Container Registry documentation.

Vercel Sandbox gives you an isolated Linux environment for safely running untrusted or user-generated code, and it works with Docker in two ways:

  • You can install and run the Docker engine inside a sandbox
  • You can boot a sandbox directly from a custom image in VCR

Sandboxes can install and run Docker, allowing you to build images and run containers without touching your host system. Each sandbox is a Firecracker microVM with its own kernel and filesystem, and it runs with sudo access, which is what lets you start the Docker daemon inside it. This is useful for running containerized services like Redis or Postgres as test dependencies, validating an image before you deploy it, or previewing an app served from a container.

Install Docker, start the daemon, then run a container:

run-docker.ts
import { Sandbox } from "@vercel/sandbox";
const sandbox = await Sandbox.create();
await sandbox.runCommand({
sudo: true,
cmd: "dnf",
args: ["install", "-y", "docker"],
});
// Start the Docker daemon and wait for it to be ready
await sandbox.runCommand({ sudo: true, cmd: "dockerd", detached: true });
await sandbox.runCommand({
cmd: "sh",
args: ["-lc", "until sudo docker info >/dev/null 2>&1; do sleep 1; done"],
});
await sandbox.runCommand({
cmd: "docker",
args: ["run", "--rm", "-d", "--name", "redis", "redis:alpine"],
});
await sandbox.runCommand({
cmd: "docker",
args: ["exec", "redis", "redis-cli", "PING"],
});

Starting a container runtime needs system-level privileges, so run these commands with sudo. With persistent sandboxes, the Docker installation and any pulled images carry over between sessions, so you skip setup on the next run.

One behavior to watch: a container you run inside a sandbox has its own filesystem and trust store, so it doesn't inherit the sandbox's proxy CA certificate. The sandbox host mounts that certificate at /etc/pki/ca-trust/source/anchors/vercel-proxy-ca.pem and trusts it automatically, but a container started inside the sandbox can't see it. As a result, HTTPS requests from inside the container fail TLS verification when the sandbox firewall terminates them.

To fix this, mount the certificate into the container and add it to the container's own trust store at build or run time:

Terminal
docker run --rm \
-v /etc/pki/ca-trust/source/anchors/vercel-proxy-ca.pem:/usr/local/share/ca-certificates/vercel-proxy-ca.crt:ro \
my-image \
sh -c "update-ca-certificates && my-command"

The exact location and trust-update command depend on the base image: use update-ca-certificates on Debian or Ubuntu, and update-ca-trust on Amazon Linux, Fedora, or RHEL. If your app reads a CA bundle from an environment variable instead of the system trust store, point that variable (such as NODE_EXTRA_CA_CERTS) at the mounted certificate path inside the container.

To define the sandbox environment itself with Docker, push your image to VCR and boot the sandbox from it instead of installing packages at runtime:

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

The sandbox boots with your image's packages and tools already in place, rather than a built-in runtime. See sandbox images to learn how to build and push a custom image.

Docker also fits into local development. To develop and test a container image before deploying, run vercel dev, which needs the docker CLI and a running Docker daemon on your machine. Even for zero-configuration frameworks you deploy without a container, a Dockerfile can pin dependencies and give you a consistent build environment across machines. The Next.js repository includes a Dockerfile example for this workflow. To verify a Vercel build locally without pushing code, run vercel build, then run vercel deploy --prebuilt to upload the output from .vercel/output without sending source code.

Was this helpful?

supported.