Skip to content
~/huseyin
tr
All posts
/1 min read#nextjs#docker#nginx

Self-hosting Next.js with Docker

Standalone output, a multi-stage Dockerfile and Nginx in front. The same setup this site runs on.

Running Next.js on your own VPS, without being tied to a platform, is easier than you might think. This site runs on the setup below.

Turn on standalone output

One line in next.config.ts makes the build put only the files needed to run into a separate folder:

ts
const nextConfig = { output: "standalone" };

That keeps the full node_modules folder out of the image, so the image gets much smaller.

A multi-stage Dockerfile

Install dependencies, build, then copy only the output into a small image:

dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
USER node
CMD ["node", "server.js"]

Put Nginx in front

The container only listens on 127.0.0.1:3000. Nginx talks to the outside world:

nginx
server {
  server_name example.com;
  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

For the certificate, sudo certbot --nginx -d example.com is enough. Certbot sets up renewal too.

Updating

Shipping a new version takes two commands:

bash
docker compose build
docker compose up -d

You can wire this into a GitHub Actions workflow so it runs on every push to main.

Have a project in mind?

An idea, an unfinished job or a server that needs moving. Whatever it is, write to me and I'll reply the same day.

Get in touch