Deploy a Next.js app
Full-stack Next.js (pages, API routes, and server rendering) on a live HTTPS URL.
Dockhold runs Next.js as a real Node server, so server components, API routes, and server-side rendering all work, so you don't have to flatten the app to a static export. This recipe is the whole path.
In a hurry? Start from the ready-made nextjs-starter template: click Use this template on GitHub, then deploy it. Or follow the steps below to set up a project you already have.
1. Bind next start to the assigned port
Make the start script use $PORT and bind all
interfaces. In package.json:
{
"scripts": {
"build": "next build",
"start": "next start -H 0.0.0.0 -p $PORT"
}
}
Your app must listen on 0.0.0.0 and read its port from the
PORT environment variable. Never localhost, never a
hardcoded port. Dockhold assigns PORT at runtime; an app that
ignores it can't receive traffic.
Don't set output: 'export' in next.config.js
unless you specifically want a static-only build. It disables API routes
and server rendering. For a normal Next.js app, leave it off.
2. Connect the repo and deploy
- Push the project to a GitHub repository.
- Open the dashboard, connect GitHub, and pick the repo.
-
Dockhold recognises Next.js on every account. With
output: 'standalone'innext.config.js(the starter has it) it runsnpm ciandnpm run build, then starts the standalone server. Nothing else to do. Without standalone output the build tells you within seconds and names the one-line fix. -
Prefer to control the build yourself? Add the
Dockerfilebelow at the root of your repo and Dockhold uses it as-is, on any account.
Next.js images get large fast, so this one uses standalone output: the
build works out which modules the server actually needs instead of copying
all of node_modules. Add this to
next.config.js:
const nextConfig = {
output: process.env.BUILD_STANDALONE ? 'standalone' : undefined,
}
module.exports = nextConfig
The env var keeps standalone on for the image and off locally, so
npm run dev and npm start keep working the way
they always did. Then the Dockerfile:
# Dockerfile
FROM node:22-alpine AS build
WORKDIR /app
ENV BUILD_STANDALONE=1
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && mkdir -p public
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
ENV 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
CMD ["node", "server.js"]
HOSTNAME=0.0.0.0 is the one line people miss. Without it the
server can bind the wrong interface and no traffic reaches you. The
standalone server reads $PORT on its own.
Your app goes live at https://<your-app>.dockhold.app
with HTTPS handled for you. Every later push to your main branch redeploys.
3. Environment variables
Set variables in the dashboard. Dockhold doesn't read a committed
.env. Server-side code reads them from process.env
at runtime (route handlers, server components, getServerSideProps);
keep API keys and tokens as secrets. Variables that reach the browser need
the NEXT_PUBLIC_ prefix and are baked in when the app is built,
and the build doesn't see your dashboard variables, so set those in code
(e.g. next.config.js), for public values only, never a secret.
4. Add a database (optional)
Enable the managed Postgres add-on and Dockhold injects
DATABASE_URL into your app. Read it from
process.env.DATABASE_URL in server code. Don't run your own
database, and don't commit a connection string.
Troubleshooting
-
App builds but the URL times out: the
startscript must include-H 0.0.0.0 -p $PORT. A defaultnext startmay bind the wrong host or port. -
API routes 404 in production: check you haven't enabled
output: 'export': static export drops the server. -
A public env var is undefined: it must be prefixed
NEXT_PUBLIC_and set in code, because the build can't read dashboard variables.
Next
- Deploy a standalone API
- Agent rules: make your AI tool generate Dockhold-ready apps.
- All recipes