To use Docker with a V0-exported Next.js app, export your V0 project to GitHub, then create a multi-stage Dockerfile that builds and serves the Next.js app in a production-ready container. Use a Node.js alpine base image to keep the container small, configure the build stage to run next build, and the runtime stage to run next start on port 3000. Docker lets you deploy your V0 app anywhere — AWS, GCP, your own server — not just Vercel.
Run V0-Generated Next.js Apps in Docker Containers
V0 by Vercel generates Next.js code that deploys to Vercel with one click — but sometimes you need to deploy to AWS ECS, Google Cloud Run, a Kubernetes cluster, your own VPS, or any other container-based environment. Docker lets you package your V0 app into a portable container image that runs identically everywhere.
The key to an efficient Next.js Docker setup is Next.js's standalone output mode, configured with `output: 'standalone'` in next.config.js. This mode copies only the files necessary to run the app into a standalone directory — no node_modules, no build artifacts — resulting in container images that are 10x smaller than naive Docker setups. The V0 Knowledge Base in CLAUDE.md confirms V0 generates Next.js App Router code, and standalone output is fully compatible.
Docker is also valuable for local development when your team uses different operating systems and Node.js versions. A docker-compose.yml file ensures everyone runs the exact same environment. The `docker-compose up` command starts both the Next.js app and any supporting services (databases, Redis, etc.) in a reproducible way without requiring any local installation beyond Docker Desktop.
Integration method
Docker integrates with V0-exported Next.js apps as a local development and deployment tool. After exporting your V0 project to GitHub, you add a Dockerfile and docker-compose.yml to the repository. The Docker build process compiles your Next.js app into an optimized standalone output, packages it in a minimal Node.js Alpine container, and produces an image you can run locally or deploy to any container hosting platform.
Prerequisites
- Docker Desktop installed on your machine (download from docker.com/products/docker-desktop)
- Your V0 project exported to GitHub using V0's Git panel
- Git installed and the repository cloned to your local machine
- Basic familiarity with the terminal for running Docker commands
- Node.js 22 or later (only needed for local non-Docker development; Docker handles this for containerized use)
Step-by-step guide
Export Your V0 Project to GitHub and Clone It
Export Your V0 Project to GitHub and Clone It
Before adding Docker, you need the V0-generated code on your local machine. In the V0 interface, open the Git panel in the left sidebar and connect your GitHub account if you haven't already. Click 'Push to GitHub' to create a new repository with your V0 project code. V0 creates a branch named `v0/main-{hash}` and submits a pull request — merge this into main to get the code into your primary branch. Once the repository exists on GitHub, open your terminal and clone it: `git clone https://github.com/your-username/your-repo-name.git`. Navigate into the project directory with `cd your-repo-name`. Run `ls` to verify the standard Next.js file structure is present: `app/`, `components/`, `package.json`, `tsconfig.json`, and `next.config.js` (or `next.config.ts` in newer V0 exports). Before adding Docker, run `npm install` and `npm run dev` to confirm the app works locally without Docker. This baseline check ensures any issues you encounter later are Docker-specific rather than code issues. If `npm run dev` fails, fix those issues first — Docker won't fix underlying code problems. Check the Node.js version requirement in package.json. V0-generated projects typically require Node.js 22 based on the CLAUDE.md specification. Your Dockerfile should use the matching Node.js version to ensure build/runtime parity.
Pro tip: Check your next.config.js for any Vercel-specific configuration that might need to be adjusted for non-Vercel deployment — Vercel-specific headers and rewrites are usually fine but some Vercel-specific plugins may not work in Docker.
Expected result: The V0 project is cloned locally, npm install succeeds, and npm run dev starts the development server at localhost:3000.
Configure Next.js Standalone Output
Configure Next.js Standalone Output
The most important step for an efficient Docker setup is enabling Next.js's standalone output mode. Open `next.config.js` (or `next.config.ts`) in your project root and add `output: 'standalone'` to the Next.js configuration. This instructs `next build` to produce a self-contained output in `.next/standalone/` that includes only the files needed to run the app — typically reducing the Docker image from 500MB+ to under 100MB. Standalone mode works by tracing all imports and copying only the required files, replacing the full `node_modules` with a minimal subset. The resulting `.next/standalone/server.js` is a complete Node.js HTTP server that doesn't need the original project directory at all. After enabling standalone output, also configure the output for public assets. The standalone build doesn't automatically include the `.next/static/` directory or the `public/` folder — these need to be copied into the container separately in your Dockerfile. This is a common gotcha that results in missing CSS, fonts, and images if you overlook it. If your V0 project already has a next.config.js with existing configuration (image domains, environment variables, redirects), add `output: 'standalone'` without removing the existing config. The output setting is purely a build optimization and doesn't affect how the app behaves — it only changes what files are included in the build output.
1// next.config.js — add output: 'standalone'2/** @type {import('next').NextConfig} */3const nextConfig = {4 output: 'standalone', // Add this line5 // Keep any existing configuration below:6 images: {7 remotePatterns: [8 // your existing image domains9 ],10 },11 // any other existing config...12};1314module.exports = nextConfig;Pro tip: Run `npm run build` locally after adding `output: 'standalone'` to verify the standalone output is generated correctly. Check that `.next/standalone/` directory is created and contains a `server.js` file.
Expected result: After running npm run build, a .next/standalone/ directory is created containing server.js and a node_modules subset. .next/standalone/server.js starts the app successfully with `node .next/standalone/server.js`.
Create the Multi-Stage Dockerfile
Create the Multi-Stage Dockerfile
Create a `Dockerfile` in your project root. Use a multi-stage build to keep the final image small and secure: the first stage installs dependencies and builds the app, the second stage is the lean production runtime that only contains what's needed to run the server. For the base image, use `node:22-alpine` — Alpine Linux is a minimal Linux distribution that produces significantly smaller images than `node:22-bullseye` or `node:22` (typically 50-80MB vs 200-400MB). Node.js 22 matches V0's requirements per the CLAUDE.md specification. The build stage runs `npm ci` (for reproducible installs using package-lock.json) and `npm run build`. The `npm ci` command is preferred over `npm install` in CI/Docker environments because it strictly follows the lockfile, ensuring identical dependency versions every build. The production stage copies only the standalone output directory, the public folder, and the static assets from the build stage. Set `NODE_ENV=production` and run `node server.js` to start the app. The `EXPOSE 3000` instruction is documentation only — it doesn't actually publish the port (that's done with `-p` flag when running the container). For security, avoid running the server as root. Create a non-root user `nextjs` in the production stage and switch to it before the CMD instruction. This follows container security best practices and is required by some deployment platforms.
1# Dockerfile2FROM node:22-alpine AS base34# Install dependencies stage5FROM base AS deps6RUN apk add --no-cache libc6-compat7WORKDIR /app8COPY package.json package-lock.json* ./9RUN npm ci1011# Build stage12FROM base AS builder13WORKDIR /app14COPY --from=deps /app/node_modules ./node_modules15COPY . .16ENV NEXT_TELEMETRY_DISABLED=117RUN npm run build1819# Production runtime stage20FROM base AS runner21WORKDIR /app22ENV NODE_ENV=production23ENV NEXT_TELEMETRY_DISABLED=12425# Create non-root user for security26RUN addgroup --system --gid 1001 nodejs27RUN adduser --system --uid 1001 nextjs2829# Copy public folder30COPY --from=builder /app/public ./public3132# Set correct permissions for standalone files33RUN mkdir .next34RUN chown nextjs:nodejs .next3536# Copy standalone build output37COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./38COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static3940USER nextjs41EXPOSE 300042ENV PORT=300043ENV HOSTNAME=0.0.0.04445CMD ["node", "server.js"]Pro tip: Add a .dockerignore file to your project root to exclude node_modules, .next, .git, and .env files from the Docker build context. This speeds up builds significantly and prevents accidentally including secrets in the image.
Expected result: Running `docker build -t my-v0-app .` completes successfully and produces an image under 200MB. Running `docker run -p 3000:3000 my-v0-app` starts the app accessible at localhost:3000.
Add .dockerignore and docker-compose.yml
Add .dockerignore and docker-compose.yml
Create a `.dockerignore` file to exclude unnecessary files from the Docker build context. Without this file, Docker sends your entire project directory — including `node_modules` (which can be hundreds of MB), `.next` build cache, `.git` history, and any `.env` files — to the Docker daemon before every build. This dramatically slows down builds and risks accidentally including sensitive files in the image. For local development, create a `docker-compose.yml` that defines how to run your V0 app along with any supporting services. Docker Compose makes it easy to pass environment variables, mount volumes for hot reloading, and add services like PostgreSQL or Redis that your app depends on. The `docker-compose.yml` should map port 3000 inside the container to port 3000 on your local machine (`3000:3000`). For development, mount your local `app/` and `components/` directories as volumes so code changes are reflected without rebuilding the image — though this requires running with `npm run dev` mode rather than the production standalone server. For environment variables, never commit `.env` files to Git. Instead, create a `.env.example` file with placeholder values and document which environment variables are required. Team members copy this to `.env.local` and fill in real values. In `docker-compose.yml`, reference the `.env` file using `env_file: .env.local` to load variables at container startup. A common pattern for teams is having two Compose files: `docker-compose.yml` for production-like local testing (uses the production Dockerfile) and `docker-compose.dev.yml` for development (uses `npm run dev` with volume mounts for hot reloading). Run with `docker-compose -f docker-compose.dev.yml up` for development.
1# .dockerignore2Dockerfile3.dockerignore4node_modules5.next6.git7.gitignore8.env9.env.local10.env.*.local11README.md12*.md13.DS_Store1415---16# docker-compose.yml (for production-like local testing)17version: '3.8'18services:19 app:20 build:21 context: .22 dockerfile: Dockerfile23 ports:24 - '3000:3000'25 env_file:26 - .env.local27 environment:28 - NODE_ENV=production29 restart: unless-stoppedPro tip: To verify your .dockerignore is working, run `docker build --no-cache -t test .` and check the 'Sending build context to Docker daemon' line — it should show a small number (under 10MB) rather than hundreds of MB.
Expected result: docker-compose up starts the V0 app successfully at localhost:3000. The build context size in the docker build output is small (under 10MB), indicating .dockerignore is working.
Test the Docker Build and Prepare for Deployment
Test the Docker Build and Prepare for Deployment
Build and test the Docker image locally before pushing to a registry for deployment. Run `docker build -t my-v0-app .` from your project root and watch for any build errors. Common issues include missing environment variables (which cause next build to fail if they're required at build time) and missing system dependencies for native npm packages. After a successful build, run `docker run -p 3000:3000 --env-file .env.local my-v0-app` to start the container. Open your browser to localhost:3000 and verify the app loads correctly. Check that CSS styles load (confirming the `.next/static` directory was copied), images load (confirming the `public` directory was copied), and API routes work. To check the container image size, run `docker images my-v0-app`. A well-optimized standalone Next.js app with Node.js Alpine should be 80-150MB. If the image is much larger, verify that the multi-stage build is actually being used and the `.next` cache isn't being copied into the final stage. For deploying to a container registry, tag the image with your registry URL: `docker tag my-v0-app registry.example.com/my-v0-app:latest` and push with `docker push registry.example.com/my-v0-app:latest`. For Docker Hub: `docker tag my-v0-app username/my-v0-app:latest && docker push username/my-v0-app:latest`. For Google Container Registry: tag with `gcr.io/your-project/my-v0-app` and push. The deployment process from there depends on your target platform (Cloud Run, ECS, Kubernetes, etc.) but the Docker image is the same regardless of where you deploy it. For teams using CI/CD, add a GitHub Actions workflow that builds the Docker image and pushes to your registry on every push to main. RapidDev can help configure the full CI/CD pipeline if you need GitHub Actions + container registry + deployment automation all connected.
1# Build the image2# docker build -t my-v0-app .34# Run with env file5# docker run -p 3000:3000 --env-file .env.local my-v0-app67# Check image size8# docker images my-v0-app910# Tag for Docker Hub11# docker tag my-v0-app username/my-v0-app:v1.0.012# docker push username/my-v0-app:v1.0.01314# Tag for Google Container Registry15# docker tag my-v0-app gcr.io/your-project-id/my-v0-app:latest16# docker push gcr.io/your-project-id/my-v0-app:latestPro tip: Always tag images with a specific version (e.g., v1.0.0 or git commit SHA) in addition to 'latest'. Using only 'latest' makes it impossible to roll back to a previous version if a deployment fails.
Expected result: Docker image builds successfully, container runs at localhost:3000 with the full V0 app working, and image size is under 200MB. Image is ready to push to a container registry.
Common use cases
Deploy V0 App to a VPS or Cloud VM
Package your V0-exported Next.js app as a Docker image and deploy it to a DigitalOcean Droplet, AWS EC2 instance, or any Linux server with Docker installed. This avoids Vercel's platform dependencies and gives you full control over the deployment environment.
Copy this prompt to try it in V0
Local Development Environment for a Team
Use docker-compose to create a consistent local development environment that includes your V0 Next.js app, a PostgreSQL database, and Redis cache. Team members run `docker-compose up` to get the full stack running without installing Node.js, PostgreSQL, or Redis locally.
Copy this prompt to try it in V0
Deploy to Google Cloud Run or AWS Fargate
Build a Docker image from your V0 project, push it to a container registry (Docker Hub, Google Container Registry, or AWS ECR), and deploy to a serverless container platform. Cloud Run and Fargate automatically scale to zero when no traffic flows, reducing hosting costs compared to always-on servers.
Copy this prompt to try it in V0
Troubleshooting
next build fails inside Docker with 'ENOENT: no such file or directory' for environment variables
Cause: Some environment variables required at build time (like NEXT_PUBLIC_* values) are not available during the Docker build stage.
Solution: Pass required build-time environment variables as Docker build arguments using --build-arg in the docker build command, or use ARG and ENV in the Dockerfile to declare them. Alternatively, use environment variable placeholder values at build time and inject real values at runtime only.
1# In Dockerfile:2ARG NEXT_PUBLIC_API_URL3ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL45# Build command:6# docker build --build-arg NEXT_PUBLIC_API_URL=https://api.example.com -t my-app .CSS styles and images are missing when the container runs
Cause: The .next/static directory and public/ folder were not copied to the production stage of the multi-stage Dockerfile.
Solution: Verify that both COPY lines exist in your production stage: one for .next/static and one for public/. These must be explicitly copied because the standalone output only includes server.js and the minimal node_modules.
1# These two lines MUST be in your runner/production stage:2COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static3COPY --from=builder /app/public ./publicContainer starts but shows 'Application error: a client-side exception has occurred'
Cause: Server-side environment variables are missing at runtime — the app starts but fails when API routes try to access process.env.SECRET_KEY which is undefined.
Solution: Pass environment variables to the container at runtime using --env-file or -e flags. Check your .env.local file has all required server-side variables. Remember NEXT_PUBLIC_* variables must be available at build time, while server-only variables (without the prefix) can be injected at runtime.
1# Run with environment file:2docker run -p 3000:3000 --env-file .env.local my-v0-app34# Or pass individual vars:5docker run -p 3000:3000 -e DATABASE_URL=postgres://... my-v0-appDocker build is very slow (5+ minutes) on every change
Cause: The COPY . . instruction invalidates Docker's layer cache on every file change, causing npm ci to re-run even when package.json hasn't changed.
Solution: Structure the Dockerfile to copy package.json and run npm ci before copying the rest of the source code. Docker caches layers — if package.json doesn't change, the npm ci layer is cached and subsequent builds skip dependency installation.
1# Correct order for cache efficiency:2COPY package.json package-lock.json* ./3RUN npm ci # Cached unless package.json changes4COPY . . # Only invalidates build layer, not deps layer5RUN npm run buildBest practices
- Always use multi-stage Docker builds — the build stage needs dev tools and the full node_modules, but the production runtime needs neither
- Enable Next.js standalone output mode (output: 'standalone' in next.config.js) to reduce image size from 500MB+ to under 150MB
- Use node:22-alpine base images instead of node:22-debian for significantly smaller image sizes
- Run containers as a non-root user for security — create a system user in the Dockerfile and switch to it before CMD
- Tag images with specific versions (git SHA or semantic version) rather than only 'latest' so you can roll back deployments
- Store all secrets as runtime environment variables injected at container start — never bake API keys or database credentials into the image
- Add a .dockerignore file — without it, Docker sends your full node_modules to the build context on every build, making builds unnecessarily slow
Alternatives
Deploy directly from GitHub to Vercel without Docker — use Vercel if you want zero-configuration deployment without managing containers or servers.
GitLab CI/CD can build Docker images and deploy to any target as part of a pipeline — use GitLab if you want integrated CI/CD with Docker build automation.
IntelliJ IDEA has built-in Docker plugin support for managing containers and Dockerfiles — use IntelliJ if you want IDE-integrated Docker management during local development.
Frequently asked questions
Do I need Docker if I'm deploying to Vercel?
No, Docker is not needed for Vercel deployments. Vercel handles containerization internally when you push to GitHub. Docker becomes useful when you want to deploy your V0 app to other platforms (AWS, GCP, your own server), run a consistent local development environment across different team members' machines, or deploy alongside other containerized services in a Kubernetes cluster.
What Node.js version should I use in my Dockerfile?
Use Node.js 22 to match V0's deployment target on Vercel (specified in the CLAUDE.md as the recommended Node version). Use the alpine variant (node:22-alpine) for smaller image sizes. Avoid older Node.js versions — V0-generated Next.js projects use modern JavaScript features that may not work on Node.js 18 or earlier.
How do I pass environment variables to the Docker container?
There are two types of environment variables for Next.js in Docker. Build-time variables (NEXT_PUBLIC_* prefix) must be passed as Docker build arguments using --build-arg and declared as ARG/ENV in the Dockerfile — they get baked into the JavaScript bundle. Runtime variables (server-only, no NEXT_PUBLIC_ prefix) are injected when the container starts using --env-file .env.local or -e KEY=VALUE flags in docker run.
Why is my Docker image so large (500MB+)?
You're likely not using a multi-stage build, or you haven't enabled Next.js standalone output mode. Without standalone output, the production stage includes the full node_modules directory. Add `output: 'standalone'` to next.config.js and ensure your Dockerfile's production stage only copies the .next/standalone directory, not the full project. A properly optimized V0 Next.js app should produce a Docker image of 80-150MB.
Can I use docker-compose for both development and production?
Yes, but use separate compose files. Create docker-compose.yml for production-like testing (uses the multi-stage Dockerfile, runs `node server.js`) and docker-compose.dev.yml for development (uses a simpler Dockerfile that runs `npm run dev` with volume mounts for hot reloading). Run development with `docker-compose -f docker-compose.dev.yml up` and production testing with `docker-compose up`.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation