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
| Feature | Vercel | Docker | Static |
|---|---|---|---|
| SSR | ✅ | ✅ | ❌ |
| ISR | ✅ | Manual | ❌ |
| Edge | ✅ | ❌ | CDN |
| Middleware | ✅ | ✅ | ❌ |
| Complexity | Low | High | Low |
| Vendor lock-in | Yes | No | No |
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