NextjsSelf-Hosting & Docker

Self-Hosting & Docker

Next.js does not require Vercel. A Next.js app is, at its core, a Node.js server: run next build to produce an optimized production build, then next start to serve it. That pair of commands works on any machine with Node.js installed — a bare VM, an existing Kubernetes cluster, or a container platform like AWS ECS, Fly.io, or Railway.

The self-hosting build/run cycle

Bash
npm run build   # runs `next build` — compiles and optimizes for production
npm run start   # runs `next start` — serves the build on port 3000 by default
Packaging with Docker
Docker is the most common way to make a self-hosted Next.js deployment portable and reproducible. Two things matter for keeping the resulting image small and the build fast: a multi-stage Dockerfile, and the output: 'standalone' config option.

next.config.mjs

TSX
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
}

export default nextConfig
With standalone enabled, next build emits a .next/standalone folder that contains a minimal server and only the node_modules your app actually needs at runtime — copied automatically, without a separate pruning step. That folder can be copied into a tiny final image instead of shipping your entire node_modules tree.

Dockerfile — minimal multi-stage build

Text
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]

Build and run the image

Bash
docker build -t my-next-app .
docker run -p 3000:3000 my-next-app
What "standalone" saves you

Without output: standalone

With output: standalone

Final image needs the full node_modules copied in (often hundreds of MB)

Only the traced, actually-used dependencies are included

Runtime start command is next start, which needs the whole project on disk

Runtime start command is node server.js, generated for you, with no next CLI required

You own the operations
Self-hosting trades convenience for control. There is no platform automatically scaling instances under load, terminating TLS, distributing your app across regions, or restarting a crashed process for you — you are responsible for load balancing, horizontal scaling, health checks, log aggregation, monitoring, and zero-downtime deploys, either by configuring them yourself or by choosing a container platform that provides them.
  • next build + next start is all a self-hosted deployment fundamentally needs — any Node.js-capable host works.

  • Set output: 'standalone' in next.config.mjs so the build only includes the dependencies actually used at runtime.

  • A multi-stage Dockerfile keeps the final image small by discarding build-time-only files and dependencies.

  • Self-hosting means you are responsible for scaling, monitoring, and deploy orchestration that a managed platform would otherwise provide.