Menu

20. Deployment & DevOps

Next.js Master Roadmap - IT Technology

This chapter explores deployment strategies for Next.js apps, covering automatic builds on Vercel via GitHub pushes, manual VPS setup with NGINX, containerization using Docker and Docker Compose, secure reverse‑proxy and SSL configuration with Let's Encrypt, and CI/CD pipelines using GitHub Actions and webhook‑based automation. By the end, you’ll be able to move code from local development to production reliably, scale horizontally, and maintain secure, observable systems.

No MCQ questions available for this chapter.

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:

  1. Developer runs git push origin main (or any branch).
  2. GitHub sends a webhook to Vercel.
  3. Vercel clones the repository, installs dependencies, runs npm run build, and deploys a preview URL.
  4. If the push targets the main branch, 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

  1. Install Node.js (using Ubuntu’s APT):
    sudo apt-get update
    sudo apt-get install -y nodejs npm
    
  2. Clone the repository: git clone https://github.com/yourname/next-app.git cd next-app
  3. Install dependencies (using clean install): npm ci
  4. Build the Next.js app: npm run build
  5. Start the app with PM2 (process manager for zero‑downtime reloads): pm2 start npm -- start pm2 save
  6. Configure NGINX as a reverse proxy (see section 7 for the full snippet).
  7. Test and reload NGINX: sudo nginx -t sudo 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 -d

Stop 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=3

Docker 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 Upgrade and Connection '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 -t
sudo 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@v3 fetches 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:

  1. GitHub sends a POST request to your VPS URL on push.
  2. The receiver validates the signature (using GitHub’s secret).
  3. 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 logs and 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.