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-madenextjs-starter template: click Use this template on GitHub, thendeploy 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 thePORT 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.jsunless 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.
- On a paid plan (any account with compute added), Dockhold detects Next.js, runs
npm installandnpm run build, then starts it withnpm run start. Nothing else to do. - On a free account, add the
Dockerfilebelow at the root of your repo first. Auto-detection is a paid feature, so without one the deploy stops right away and tells you.
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 tonext.config.js:
const nextConfig = {
output: process.env.BUILD_STANDALONE ? 'standalone' : undefined,
}
module.exports = nextConfigThe env var keeps standalone on for the image and off locally, sonpm 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.appwith 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.envat runtime (route handlers, server components, getServerSideProps); keep API keys and tokens in the Vault. 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 injectsDATABASE_URL into your app. Read it fromprocess.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