Skip to content

Next.js ​

What Zenavo detects ​

A repository with next in package.json builds with zbpack as Node.js, framework next.js:

StepCommand
Installnpm install, pnpm install, yarn install or bun install, after the lockfile in the repository (yarn without one)
Buildthe build script, normally next build
Startthe start script, normally next start

The Node.js version comes from engines.node in package.json (22 when it is not set). Commit your lockfile so the install is reproducible and the right package manager is used.

next start listens on $PORT, which Zenavo sets to 8080, on every interface, so nothing needs changing for a default app:

json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}

Deploy ​

  1. Add Service → GitHub, pick the repository and branch.
  2. Add the variables the app needs (see below) and click Next.
  3. Check that the preview shows nodejs and next.js, then click Deploy.
  4. On the Networking tab, generate a domain or bind your own.

Environment variables ​

  • Server-side variables (process.env.DATABASE_URL in route handlers, server components, getServerSideProps) are read when the app runs: set them on the Variable tab and redeploy.
  • NEXT_PUBLIC_* variables are written into the JavaScript bundle by next build, so they must be there during the build. Variables on the Variable tab are given to the running app only, so set these under Settings → Build → Build arguments instead, for example NEXT_PUBLIC_API_URL=https://api.example.com, or NEXT_PUBLIC_API_URL=${API_URL} to reuse a variable. They reach the build without a Dockerfile; see build arguments.
  • The site's own address is ZENAVO_<PORT>_URL, for example ${ZENAVO_WEB_URL}, once the port has a domain. Use it for NEXTAUTH_URL or similar.

Connect a database ​

Add PostgreSQL to the project and reference its variable from the Next.js service:

sh
DATABASE_URL=${POSTGRESQL_CONNECTION_STRING}

Prisma, Drizzle and pg all accept this URL. Run migrations when the service starts, because the build cannot reach the database: a predeploy script runs before start.

json
"scripts": {
  "build": "next build",
  "predeploy": "prisma migrate deploy",
  "start": "next start"
}

Smaller images with output: 'standalone' ​

With output: 'standalone' in next.config.js, next build writes a self-contained server to .next/standalone. zbpack does not use it, so to benefit, add a Dockerfile:

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

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

HOSTNAME=0.0.0.0 makes the standalone server listen on every interface, and the port comes from EXPOSE (here 3000). Each variable the build needs, such as NEXT_PUBLIC_API_URL, gets an ARG line before RUN npm run build and a value under Settings → Build → Build arguments.

Common problems ​

SymptomCause
Build fails with JavaScript heap out of memoryLarge apps need more memory than a build gets by default. Add NODE_OPTIONS=--max-old-space-size=4096 as a variable, or slim the build.
A NEXT_PUBLIC_* value is undefined in the browserIt only exists on the Variable tab. Set it as a build argument and redeploy. With your own Dockerfile, also declare it with ARG before next build.
Images from next/image failRemote images need images.remotePatterns in next.config.js.
The standalone server is unreachableHOSTNAME is not 0.0.0.0, so it listens on the container's hostname only.

Not affiliated with Zeabur.