20. Deployment & DevOps
Overview
Modern web applications demand a reliable path from code commit to production. This chapter walks you through the most common deployment patterns for Next.js projects, beginning with platform‑as‑a‑service solutions like Vercel, moving to self‑managed virtual private servers, then diving into containerization with Docker, and finally securing and automating the whole pipeline. Each section includes concrete commands, configuration snippets, and best‑practice notes so you can copy‑paste them into your own workflow.
1. GitHub‑Triggered Builds (Vercel/Netlify)
When you push to a GitHub repository, Vercel (or Netlify) can automatically start a preview build. The typical flow is:
- Developer runs
git push origin main(or any branch). - GitHub sends a webhook to Vercel.
- Vercel clones the repository, installs dependencies, runs
npm run build, and deploys a preview URL. - If the push targets the
mainbranch, Vercel promotes the build to production.
Netlify follows an analogous process, using its own CLI and dashboard settings. The key benefit is zero‑configuration CI: as long as the repository is linked, every push yields a testable preview.
2. Vercel CLI Deployment
For more control, you can trigger deployments locally with the Vercel CLI.
Installation
npm i -g vercel
Preview Deploy
vercel (defaults to a preview environment).
Production Deploy
vercel --prod
Environment Variables
Variables are scoped to environments. Add them with:
- Preview:
vercel env add API_KEY preview - Production:
vercel env add API_KEY production
After adding, redeploy with vercel --prod to inject the new values.
3. VPS Deployment with NGINX
When you need full control over the server, a Virtual Private Server (VPS) paired with NGINX as a reverse proxy is a solid choice.
Step‑by‑Step Setup
- Install Node.js (using Ubuntu’s APT):
sudo apt-get update sudo apt-get install -y nodejs npm - Clone the repository:
git clone https://github.com/yourname/next-app.gitcd next-app - Install dependencies (using clean install):
npm ci - Build the Next.js app:
npm run build - Start the app with PM2 (process manager for zero‑downtime reloads):
pm2 start npm -- startpm2 save - Configure NGINX as a reverse proxy (see section 7 for the full snippet).
- Test and reload NGINX:
sudo nginx -tsudo systemctl reload nginx
At this point, visiting http://your‑domain.com forwards traffic to the Next.js instance listening on localhost:3000.
4. Docker Basics
Containerizing your Next.js app guarantees identical builds across developer machines, CI agents, and production hosts.
Dockerfile
Create a file named Dockerfile in the project root:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
Build and Run
docker build -t my-next-app .
docker run -p 3000:3000 my-next-app
The -p 3000:3000 flag maps the container’s port 3000 to the host’s port 3000.
5. Docker Compose
For multi‑service architectures (e.g., app + PostgreSQL), Docker Compose simplifies orchestration.
docker‑compose.yml
version: '3.8'
services:
web:
build: .
ports: ["3000:3000"]
env_file: .env
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: nextapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Start the stack in detached mode:
docker compose up -dStop and remove:
docker compose down
6. Containerization Benefits & Scaling
Using Docker yields:
- Isolated dependencies – each service runs with its own libraries, eliminating “works on my machine” issues.
- Consistent environments – the same image is used locally, in CI, and in production.
- Easy scaling – you can run multiple replicas of a service behind a load balancer.
Scaling with Docker Compose
To run three copies of the web service:
docker compose up --scale web=3Docker Compose will create web-1, web-2, and web-3, each bound to the host port 3000 (you’ll need a reverse proxy or Docker Swarm/Kubernetes for proper load distribution).
Kubernetes Autoscaling
In a Kubernetes cluster, you can define a HorizontalPodAutoscaler that adjusts replica count based on CPU utilization:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nextapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nextapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
This configuration keeps average CPU usage around 50 %, automatically adding or removing pods as traffic fluctuates.
7. NGINX Reverse Proxy
NGINX acts as a front‑end that forwards incoming HTTP requests to the Next.js process.
Basic Configuration
Create a file /etc/nginx/sites-available/nextapp (or include directly in nginx.conf):
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Explanation of key directives:
proxy_pass– forwards the request to the upstream Next.js server.proxy_http_version 1.1– enables keep‑alive connections.proxy_set_header UpgradeandConnection 'upgrade'– required for WebSocket support.proxy_set_header Host– preserves the original host header for correct routing.proxy_cache_bypass $http_upgrade– prevents caching of WebSocket upgrades.
After saving, test and reload:
sudo nginx -tsudo systemctl reload nginx
8. SSL Setup with Let's Encrypt
Securing traffic with HTTPS is essential. Certbot automates certificate acquisition and renewal.
Obtain a Certificate
Assuming NGINX is already configured as shown above, run:
sudo certbot --nginx -d example.com -d www.example.com
Certbot will:
- Validate domain ownership via the ACME protocol.
- Modify the NGINX server block to listen on port 443 with SSL.
- Insert the paths to the full chain and private key:
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
Automatic Renewal
Certbot installs a systemd timer that runs twice daily. To verify:
systemctl list-timers | grep certbot
If you prefer manual renewal, you can run:
sudo certbot renew --dry-run
9. CI/CD with GitHub Actions
Automate Vercel deployments directly from your repository.
Workflow File (`.github/workflows/deploy.yml`)
name: Deploy to Vercel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Vercel CLI
run: npm i -g vercel
- name: Deploy to Vercel
run: vercel --prod --token ${{ secrets.VERCEL_TOKEN }}
Key points:
- The workflow triggers on every push to
main. actions/checkout@v3fetches the repository.- Vercel CLI is installed globally.
- The production deployment uses a stored secret
VERCEL_TOKEN(create it in Vercel → Settings → Tokens).
You can extend this workflow to run tests, linting, or to deploy preview branches by changing the on clause.
10. Automated Deployment via Webhook on VPS
If you prefer a self‑hosted VPS, a simple webhook script can pull the latest code, rebuild, and restart the app with zero downtime.
Deploy Script (`/var/www/hooks/deploy.sh`)
#!/bin/bash
git pull origin main
npm ci
npm run build
pm2 restart all
Make it executable:
chmod +x /var/www/hooks/deploy.sh
Webhook Receiver
Many lightweight solutions exist (e.g., webhook Go library, or a tiny Node.js Express endpoint). The core idea is:
- GitHub sends a POST request to your VPS URL on push.
- The receiver validates the signature (using GitHub’s secret).
- If valid, it executes
deploy.sh.
Benefits of this approach:
- Zero‑downtime rolling updates – PM2 restarts processes one by one, keeping at least one instance alive.
- Rollback capability – you can redeploy a previous Git tag by checking out that tag before running the script.
- Auditability – each execution logs to
pm2 logsand to the webhook receiver’s stdout, providing a clear trail.
Summary
This chapter has covered the full spectrum of deploying a Next.js application:
- Automatic preview and production builds via Vercel/Netlify triggered by Git pushes.
- Manual, CLI‑driven Vercel deployments with environment‑variable management.
- Traditional VPS setup using Node.js, PM2, and NGINX as a reverse proxy.
- Containerization with Docker and multi‑service orchestration via Docker Compose.
- Scaling strategies ranging from Docker Compose replica commands to Kubernetes HorizontalPodAutoscaler.
- NGINX reverse‑proxy configuration, including WebSocket support.
- Let's Encrypt SSL automation with Certbot and automatic renewal.
- CI/CD pipelines using GitHub Actions for Vercel, and a self‑hosted webhook script for VPS environments.
By mastering these techniques, you can move code from a local development environment to a globally accessible, secure, and scalable production system with confidence.