Skip to main content

Next.js Deployment and Infrastructure

Answer

Next.js can be deployed to various platforms with different trade-offs. Understanding deployment options helps optimize for your specific requirements.

Deployment Options

Vercel Deployment

# Install Vercel CLI
npm i -g vercel

# Deploy from project root
vercel

# Production deployment
vercel --prod

# Environment variables
vercel env add VARIABLE_NAME

Docker Deployment

# Dockerfile
FROM node:18-alpine AS base

FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM base AS runner
WORKDIR /app
ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT 3000

CMD ["node", "server.js"]
// next.config.js for Docker
module.exports = {
output: "standalone",
};

Static Export

// next.config.js
module.exports = {
output: "export",
// Optional: Add trailing slashes
trailingSlash: true,
};
# Build static site
npm run build
# Output in /out directory

Environment Variables

# .env.local (development, not committed)
DATABASE_URL=postgresql://...
API_SECRET=my-secret

# .env.production (production defaults)
NEXT_PUBLIC_API_URL=https://api.example.com

# Runtime vs Build time
NEXT_PUBLIC_* = Available in browser (build time)
Without prefix = Server only (runtime)
// Usage
// Server only
const secret = process.env.API_SECRET;

// Client accessible
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

Multi-Zone Deployments

// app1/next.config.js
module.exports = {
basePath: "/shop",
assetPrefix: "/shop",
};

// app2/next.config.js
module.exports = {
basePath: "/blog",
assetPrefix: "/blog",
};

// Proxy configuration (nginx/vercel.json)
// /shop/* → app1
// /blog/* → app2
// /* → main app

Caching Headers

// next.config.js
module.exports = {
async headers() {
return [
{
source: "/static/:path*",
headers: [
{
key: "Cache-Control",
value: "public, max-age=31536000, immutable",
},
],
},
{
source: "/:path*",
headers: [
{
key: "X-Frame-Options",
value: "DENY",
},
],
},
];
},
};

Health Checks

// app/api/health/route.js
export async function GET() {
try {
// Check database connection
await db.query("SELECT 1");

return Response.json({
status: "healthy",
timestamp: new Date().toISOString(),
});
} catch (error) {
return Response.json(
{ status: "unhealthy", error: error.message },
{ status: 503 }
);
}
}

Logging and Monitoring

// Structured logging
import pino from "pino";

const logger = pino({
level: process.env.LOG_LEVEL || "info",
});

// In route handlers
export async function GET() {
logger.info({ path: "/api/users" }, "Request received");

try {
const users = await getUsers();
return Response.json(users);
} catch (error) {
logger.error({ error: error.message }, "Failed to get users");
return Response.json({ error: "Failed" }, { status: 500 });
}
}

CI/CD Example (GitHub Actions)

# .github/workflows/deploy.yml
name: Deploy

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: actions/setup-node@v3
with:
node-version: 18
cache: "npm"

- run: npm ci
- run: npm run lint
- run: npm run test
- run: npm run build

- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
vercel-args: "--prod"

Platform Comparison

FeatureVercelDockerStatic
SSR
ISRManual
EdgeCDN
Middleware
ComplexityLowHighLow
Vendor lock-inYesNoNo

Key Points

  • Vercel: Zero-config, full feature support
  • Docker: Self-hosted, use output: 'standalone'
  • Static: No server needed, output: 'export'
  • Use NEXT_PUBLIC_ for client-side env vars
  • Set proper cache headers for performance
  • Implement health checks for orchestration
  • Use CI/CD for automated deployments