Skip to content

How to Deploy Next.js with Docker (Standalone Output, 2026)

Ship Next.js outside Vercel as a small Docker image: output standalone, a multi-stage Dockerfile, HOSTNAME and PORT, and NEXT_PUBLIC variables at build time.

Published
Reading time
4 min
On this page

Key takeaways

  • Set output: 'standalone' in next.config so the build produces a self-contained server.js with only the files it needs.
  • A multi-stage Dockerfile on Node 22 keeps the final image small; copy .next/static and public next to server.js.
  • Run with HOSTNAME=0.0.0.0 and the platform's PORT, or skip Docker entirely: npm run build + npm start also works.

Next.js runs anywhere Node.js runs. With the standalone output you can ship it as a small Docker image to any container host. This guide builds that image and deploys it.

Two ways to deploy Next.js

  1. No Dockerfile (simplest). DockHosting detects package.json, runs npm run build, and starts with npm start. next start already reads the PORT variable, so this works with no changes.
  2. Docker (full control). Use this when you need system packages, a specific Node version, or a reproducible image across environments. The rest of this guide covers this option.

1. Enable standalone output

ts
// next.config.ts
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  output: "standalone",
};
 
export default nextConfig;

next build now creates .next/standalone/server.js and copies in only the node_modules your app actually uses. That's the reason the final image is small.

2. A multi-stage Dockerfile

dockerfile
# Dockerfile
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
 
FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# NEXT_PUBLIC_* values are baked in at build time
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build
 
FROM node:22-alpine AS run
WORKDIR /app
ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0
RUN addgroup -S app && adduser -S app -G app
COPY --from=build --chown=app:app /app/.next/standalone ./
COPY --from=build --chown=app:app /app/.next/static ./.next/static
COPY --from=build --chown=app:app /app/public ./public
USER app
CMD ["node", "server.js"]

Why each line matters:

  • Three stages keep compilers, dev dependencies and source code out of the final image.
  • HOSTNAME=0.0.0.0 makes the server reachable from outside the container. Bound to localhost, you'd get a 502.
  • .next/static and public aren't included in standalone by default, so you copy them next to server.js.
  • A non-root user limits what a compromised process can do.

The server listens on PORT (it defaults to 3000), and the platform sets it.

Add a .dockerignore so local builds and secrets stay out of the image:

text
node_modules
.next
.git
.env*

3. Deploy

bash
curl -fsSL https://cli.dockhosting.dev/install.sh | sh   # Windows: irm https://cli.dockhosting.dev/install.ps1 | iex
dock login
dock deploy

When a Dockerfile is present, DockHosting runs docker build against your repo. Multi-stage builds and build args are supported. You get an HTTPS URL on .dockhosting.cloud, logs with dock logs, and a zero-downtime switch on each deploy.

4. Environment variables

  • Server-only secrets (database URLs, API keys): set them in the project's environment variables. They're read at runtime.
  • NEXT_PUBLIC_*: these are inlined into the browser bundle at build time, so pass them as build args.

Need a database? Attach managed PostgreSQL and read DATABASE_URL in Route Handlers or Server Components. Prisma and Drizzle both accept it. See connecting to managed PostgreSQL.

5. Sizing

A Next.js server usually needs more memory than a small API, especially while it renders many pages. The Free plan's 512 MB is fine for small sites. Hobby (1 GB) or Pro (3 GB, up to 2 GB per app) leave room for traffic. Containers can use up to 4 GB on Team.

Static export instead?

If your site has no server features (no Server Actions, Route Handlers or SSR), output: "export" produces plain HTML in out/. DockHosting serves static sites with automatic HTTPS and no cold starts.

More Docker? Read deploying any Docker container.

Frequently asked questions

Do I need Docker to deploy Next.js on DockHosting?

No. Without a Dockerfile, DockHosting detects Node.js from package.json, runs npm run build and starts the app with npm start (next start reads the PORT variable). A Dockerfile gives you full control over the image when you need it.

Why are my NEXT_PUBLIC_ variables undefined in production?

NEXT_PUBLIC_ variables are inlined into the JavaScript at build time. They must exist when next build runs, so pass them as build args in the Dockerfile (ARG then ENV) rather than only at runtime.

Do Server Actions, middleware and ISR work in Docker?

Yes. The standalone server is the same Next.js server, so the App Router, Route Handlers, middleware and on-demand revalidation all work. ISR's cache lives in the container, so it starts fresh on each deploy.

  • Next.js
  • Docker
  • Node.js
  • React
  • Deployment

Written by Doughmi ILYAS

Doughmi builds DockHosting, the developer-first platform behind dock deploy: the build system, the managed databases and the dashboard. On this blog: guides to shipping apps without running servers, written with the commands the platform actually ships.

Summarize with

Share

All posts

Create an account, run dock deploy, and get a live HTTPS URL in minutes.