Nuxt Dockerfile for Production: Nitro Server on Node Alpine

This Nuxt Dockerfile builds your app on node:24-alpine and ships only the self-contained .output folder that Nitro produces. No node_modules are copied, because the build bundles everything the server needs. The server runs as the non-root node user on port 3000, listening on all interfaces.

CI status
Build passing
Image size (uncompressed)
174.1 MB
Build time
19 s
Runtime image
node:24-alpine
Port
3000
Last built
2026-10-15
Dockerfile
# syntax=docker/dockerfile:1
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:24-alpine
ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000
WORKDIR /app
COPY --from=build --chown=node:node /app/.output ./.output
USER node
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]
.dockerignore
.git
node_modules/
.nuxt/
.output/
.env*
Dockerfile
.dockerignore

Before you build

How this Dockerfile works

  1. FROM node:24-alpine AS build

    The build stage holds Nuxt, Vite and all development dependencies. None of it is needed once the server is built.

  2. COPY package.json package-lock.json ./ RUN npm ci

    Installs locked dependencies in a cached layer that's reused until the manifests change.

  3. RUN npm run build

    Runs nuxt build, which bundles the server and client into .output with only the dependencies the server actually imports.

  4. ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000

    Nitro reads HOST and PORT at startup. Binding to 0.0.0.0 makes the server reachable through the container's published port.

  5. COPY --from=build --chown=node:node /app/.output ./.output

    Copies the complete server bundle and public assets. This is the only thing the runtime image needs besides Node.

  6. USER node EXPOSE 3000 CMD ["node", ".output/server/index.mjs"]

    Starts the Nitro server directly with node as the unprivileged user, so stop signals reach the process.

Build and run

terminal
docker build -t nuxt-app .
docker run --rm -p 3000:3000 nuxt-app

Build from the project root and open http://localhost:3000. Override runtimeConfig values at runtime with environment variables such as NUXT_PUBLIC_API_BASE.

Other Nuxt Dockerfile variants

The Nitro node-server output is the standard way to self-host a Nuxt app with server rendering. Fully prerendered sites from nuxt generate can be served from Nginx instead, without Node. Verified variants appear in the table below.

VariantRuntime imageImage sizeCI
Nitro server on Node Alpinenode:24-alpine174.1 MBPassing

Troubleshooting

Error: Cannot find module '/app/.output/server/index.mjs'
The build used a different Nitro preset, such as one for a hosting platform. Remove the preset setting, or set NITRO_PRESET=node-server, then rebuild.
The app works in the container but not from the host
The server is listening on localhost only. Keep HOST=0.0.0.0 in the runtime stage and publish the same port as PORT.
Runtime config changes don't take effect
Values read at build time are baked into the bundle. Define them in runtimeConfig and override them with NUXT_ prefixed environment variables when starting the container.

FAQ

Why doesn't the image copy node_modules?
Nitro traces the server's imports during the build and copies what's needed into .output/server. The full node_modules folder isn't required at runtime.
Can I use pnpm or Bun instead of npm?
Yes. Change the lockfile and install command in the build stage. The runtime stage stays the same because it only runs the built .output folder.
How do I serve a prerendered Nuxt site?
Run nuxt generate and serve the generated public folder from Nginx. That needs no Node runtime, but it can't render pages on request.