Deployment
This guide covers production deployment to Vercel (recommended), Docker, and self-hosted Node.js
servers. All paths assume you have already run pnpm build successfully.
Pre-deployment checklist
-
AUTH_SECRETis set to a strong random value (openssl rand -base64 32) -
AUTH_URLis set to your production domain withhttps:// -
AUTH_TRUST_HOSTistrueonly if running behind a reverse proxy - Demo credential variables (
DEMO_USER_EMAIL,DEMO_USER_PASSWORD) are either removed or overridden with production values - The
authorizefunction inauth.tshas been replaced with real database authentication -
DISABLE_PROTECTED_ROUTE_CHECKisfalse(or not set) -
NEXT_PUBLIC_GOOGLE_MAP_APIis set if the app uses Google Maps pages - All secrets are stored in the hosting platform's secret manager, not in committed files
Production build
# pnpm
pnpm build
# npm
npm run build
# yarn
yarn buildFor a clean build (removes .next cache):
pnpm clean-buildStart the production server after a successful build:
pnpm startVercel
Vercel is the recommended deployment target. Next.js projects deploy with zero configuration.
Push your code to a Git repository
Ensure your repository is on GitHub, GitLab, or Bitbucket.
Import the project in Vercel
Go to vercel.com/new and import your repository. Vercel detects Next.js automatically.
Set environment variables
In the Vercel dashboard under Settings → Environment Variables, add:
AUTH_SECRET = <openssl-generated-secret> AUTH_URL = https://your-domain.vercel.app AUTH_TRUST_HOST = true NEXT_PUBLIC_IMAGES_PATH = /assets/imagesFor production database auth, add
DATABASE_URLas well.Deploy
Click Deploy. Vercel builds and deploys automatically. Every push to
maintriggers a new production deployment.
Vercel creates preview deployments for every pull request. Set AUTH_URL in the preview
environment to https://your-project-*.vercel.app to make auth work on preview URLs, or use
AUTH_TRUST_HOST=true to auto-detect the request origin.
Docker
Use Docker for self-hosted or containerized deployments.
Dockerfile
# Stage 1: Install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
# Stage 2: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm install -g pnpm && pnpm build
# Stage 3: Production runner
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/.next/standalone ./
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]The Dockerfile above assumes Next.js output: 'standalone' is enabled in next.config.js.
Add this setting to minimize the Docker image size by including only the necessary files:
// next.config.js
module.exports = {
output: 'standalone',
// ...existing config
};Build and run
# Build the image
docker build -t jumbo-react-next .
# Run with environment variables
docker run -p 3000:3000 \
-e AUTH_SECRET=your-secret \
-e AUTH_URL=http://localhost:3000 \
-e AUTH_TRUST_HOST=true \
jumbo-react-nextSelf-hosted Node.js
For a plain Node.js server without Docker:
# On your server
git clone <repo-url> app && cd app
pnpm install --frozen-lockfile
pnpm build
# Start with PM2 for process management
npm install -g pm2
pm2 start npm --name "jumbo-react-next" -- start
pm2 save
pm2 startupSet environment variables either via a .env.production file (excluded from git) or your
server's environment management tool (e.g. systemd unit environment block, Nginx upstream env).
Security headers
next.config.js applies four security headers to all routes:
// next.config.js
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
],
},
];
},| Header | Value | Purpose |
|---|---|---|
X-Frame-Options | DENY | Prevents clickjacking via iframes |
X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
Referrer-Policy | strict-origin-when-cross-origin | Limits referrer data to same origin |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Restricts browser feature access |
For production, consider adding a Content Security Policy (CSP) header to further restrict resource loading. See the MDN CSP documentation for guidance.
Post-deployment verification
- App loads at your production URL without console errors
- Unauthenticated requests redirect to
/auth/login-1 - Authentication flow completes and lands on the dashboard
- The locale redirect works (navigating to
/dashboards/miscredirects to/en-US/dashboards/misc) - All seven dashboards load without errors
- Dark mode theme switching works in the Customizer drawer
- Security headers are present (verify with securityheaders.com)