Deploy a Next.js app on Bahriya
This quickstart deploys a Next.js application as an HTTP container using Next.js's standalone output. It listens on port 3000, ships as a compact self-contained image, and serves over HTTPS on its default hostname.
This quickstart deploys a Next.js application as an HTTP container using Next.js's standalone output. It listens on port 3000, ships as a compact self-contained image, and serves over HTTPS on its default hostname.
Standalone output is the right choice for a container: Next.js traces exactly the files your app needs and produces a small server.js you can run with node, so you do not have to copy the whole node_modules tree into the runtime image.
Enable standalone output
In next.config.js, set the output mode:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;The standalone server listens on port 3000 by default and reads PORT from the environment, so it works with Bahriya without further changes.
Dockerfile
A multi-stage build installs dependencies, builds the app, then assembles a minimal runtime image from the standalone output:
# Dependencies
FROM node:22-slim AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# Build
FROM node:22-slim AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Runtime
FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
COPY --from=build /app/public ./public
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]Build-time versus runtime environment
Next.js reads two kinds of configuration, and the distinction matters when you containerise:
- Build-time values are baked into the compiled output during
npm run build. Anything exposed to the browser with theNEXT_PUBLIC_prefix is fixed at build time — set it in your CI build, not on the container. - Runtime values are read when the server starts. Server-only variables (database URLs, API keys) can be supplied as environment variables and secrets on the container, so the same image runs across regions and environments.
Because public variables are frozen at build time, rebuild the image if you need to change them.
Deploy from the Console
- Create a new HTTP container in your project.
- Set the image to your built reference and select a registry if it is private.
- Set the port to
3000. Next.js does not ship a health endpoint by default — add a route handler at/healthzthat returns 200, or point the health check at a lightweight page that always renders. - Choose regions, set CPU and memory, and create the container.
Deploy with Reis
reis container:create \
--type http \
--name "Web" \
--handle web \
--image ghcr.io/myorg/web:1.0.0 \
--project my-project \
--port 3000 \
--healthcheck /healthz \
--cpu 500 \
--memory 512 \
--replicas 2 \
--max_replicas 8 \
--regions falkenstein-1 \
--regions virginia-1 \
--env NODE_ENV=productionEnvironment variables, secrets, and domains
- Supply server-only configuration as environment variables and sensitive values as secrets, injected at runtime.
- Serve the app on your own hostname by following Custom domain setup.
Next steps
- HTTP containers — hostnames, autoscaling, and security.
- Proxy caching — cache static assets and pages at the edge.
- Deploy an HTTP container with flags — the full Reis reference.
- Environment variables — build-time versus runtime configuration.