Cara Deploy Next.js ke VPS dengan Docker dan Nginx - Nayaka Yoga Pradipta
ID | EN

Cara Deploy Next.js ke VPS dengan Docker dan Nginx

Kamis, 25 Des 2025

Capek bayar mahal buat hosting? Atau pengen punya kontrol penuh atas server kamu? Nah, deploy Next.js ke VPS dengan Docker dan Nginx bisa jadi solusi yang tepat. Di tutorial ini, gue bakal jelasin step-by-step gimana caranya dari nol sampai aplikasi kamu live dengan SSL gratis.

Kenapa Deploy ke VPS?

Sebelum mulai, mungkin lo bertanya-tanya: “Kenapa harus ribet pake VPS?”

Alasan utama:

  • Lebih murah - VPS mulai dari $5/bulan bisa handle multiple apps
  • Kontrol penuh - Lo bisa install apapun, configure sesuka hati
  • Scalable - Upgrade resource kapan aja tanpa migrasi
  • Learning experience - Skill DevOps lo bakal naik level

Kalo lo udah pake Vercel dan happy, ga masalah. Tapi kalo lo butuh lebih dari yang free tier tawarkan, atau pengen belajar deployment yang “real”, let’s go!

Prerequisites

Sebelum mulai, pastikan lo punya:

  • VPS dengan minimal 1GB RAM (recommended: 2GB). Bisa pake DigitalOcean, Vultr, Linode, atau provider lokal kayak IDCloudHost
  • Domain yang udah pointing ke IP VPS lo
  • SSH access ke VPS
  • Next.js project yang siap deploy
  • Basic knowledge terminal/command line

Setup VPS (Ubuntu 22.04)

Pertama, SSH ke VPS lo:

ssh root@your_server_ip

Update system dan install essential packages:

# Update package list
apt update && apt upgrade -y

# Install essential tools
apt install -y curl wget git nano ufw

Setup Firewall

# Allow SSH, HTTP, dan HTTPS
ufw allow OpenSSH
ufw allow 80
ufw allow 443

# Enable firewall
ufw enable

# Check status
ufw status
# Create user baru
adduser deploy

# Add ke sudo group
usermod -aG sudo deploy

# Switch ke user baru
su - deploy

Install Docker dan Docker Compose

Docker bikin deployment jadi consistent dan reproducible. Ga perlu worry soal “works on my machine” lagi.

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Add user ke docker group (biar ga perlu sudo)
sudo usermod -aG docker $USER

# Apply group changes
newgrp docker

# Verify installation
docker --version

Docker Compose udah included di Docker versi baru, tapi kalo belum:

# Install Docker Compose plugin
sudo apt install docker-compose-plugin

# Verify
docker compose version

Dockerfile untuk Next.js

Ini bagian penting. Kita bakal pake multi-stage build biar image size-nya kecil dan secure.

Buat file Dockerfile di root project Next.js lo:

# Stage 1: Dependencies
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Copy package files
COPY package.json package-lock.json* ./
RUN npm ci --only=production

# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Set environment variables for build
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV production

# Build the application
RUN npm run build

# Stage 3: Runner
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy necessary files
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

# Set correct permissions
RUN chown -R nextjs:nodejs /app

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

Penting! Tambahkan ini di next.config.js lo:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
}

module.exports = nextConfig

Docker Compose Configuration

Buat file docker-compose.yml:

version: '3.8'

services:
  nextjs:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: nextjs-app
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
    networks:
      - webnet
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

networks:
  webnet:
    driver: bridge

Build dan Run

# Build image
docker compose build

# Run container
docker compose up -d

# Check logs
docker compose logs -f nextjs

Sekarang app lo harusnya jalan di http://your_server_ip:3000.

Setup Nginx sebagai Reverse Proxy

Nginx bakal handle incoming requests dan forward ke Docker container. Plus, dia juga yang bakal manage SSL.

# Install Nginx
sudo apt install nginx -y

# Start dan enable
sudo systemctl start nginx
sudo systemctl enable nginx

Nginx Configuration

Buat config file baru:

sudo nano /etc/nginx/sites-available/nextjs

Paste konfigurasi ini:

upstream nextjs_upstream {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # Redirect HTTP to HTTPS (uncomment setelah SSL setup)
    # return 301 https://$server_name$request_uri;

    location / {
        proxy_pass http://nextjs_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400;
    }

    # Static files caching
    location /_next/static {
        proxy_pass http://nextjs_upstream;
        proxy_cache_valid 60m;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_min_length 1000;
}

Enable site dan test config:

# Create symlink
sudo ln -s /etc/nginx/sites-available/nextjs /etc/nginx/sites-enabled/

# Remove default site
sudo rm /etc/nginx/sites-enabled/default

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

Sekarang akses http://yourdomain.com - harusnya udah jalan!

SSL dengan Let’s Encrypt

HTTPS itu wajib di 2024. Untungnya, Let’s Encrypt bikin ini gratis dan gampang.

# Install Certbot
sudo apt install certbot python3-certbot-nginx -y

# Generate SSL certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot bakal otomatis modify Nginx config lo. Setelah selesai, update config buat redirect HTTP ke HTTPS:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
    # SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # ... rest of your config
}

Auto-Renewal

Certbot udah setup auto-renewal, tapi lo bisa test:

# Test renewal
sudo certbot renew --dry-run

CI/CD dengan GitHub Actions

Sekarang kita setup auto-deployment. Setiap push ke main, app bakal otomatis deploy.

Buat file .github/workflows/deploy.yml:

name: Deploy to VPS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy to VPS
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            cd /home/deploy/your-app
            git pull origin main
            docker compose build --no-cache
            docker compose up -d
            docker system prune -f

Setup GitHub Secrets

Di repository GitHub lo, go to Settings > Secrets and variables > Actions, tambahkan:

  • VPS_HOST - IP address VPS lo
  • VPS_USER - Username (e.g., deploy)
  • VPS_SSH_KEY - Private SSH key

Generate SSH key di local:

ssh-keygen -t ed25519 -C "github-actions"

Copy public key ke VPS:

ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your_server_ip

Private key (~/.ssh/id_ed25519) masukin ke GitHub Secrets.

Monitoring dan Logging

Deployment tanpa monitoring itu kayak nyetir malam tanpa lampu. Here’s basic setup:

Docker Logs

# View logs
docker compose logs -f

# View specific service
docker compose logs -f nextjs

# Last 100 lines
docker compose logs --tail 100 nextjs

System Monitoring dengan htop

sudo apt install htop -y
htop

Basic Health Check Script

Buat file health-check.sh:

#!/bin/bash

HEALTH_URL="http://localhost:3000"
DISCORD_WEBHOOK="your_discord_webhook_url"

response=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)

if [ $response != "200" ]; then
    # Restart container
    docker compose restart nextjs
    
    # Send notification (optional)
    curl -H "Content-Type: application/json" \
         -d '{"content":"⚠️ NextJS app was down and has been restarted!"}' \
         $DISCORD_WEBHOOK
fi

Setup cron job:

# Edit crontab
crontab -e

# Add this line (check every 5 minutes)
*/5 * * * * /home/deploy/health-check.sh

Tips Optimisasi

1. Enable Docker BuildKit

export DOCKER_BUILDKIT=1
docker compose build

2. Use Docker Layer Caching

Struktur Dockerfile yang bener bikin build lebih cepat:

# Dependencies first (rarely changes)
COPY package*.json ./
RUN npm ci

# Source code last (frequently changes)
COPY . .

3. Resource Limits

Tambahkan di docker-compose.yml:

services:
  nextjs:
    # ... other config
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M

4. Nginx Caching

# Add proxy cache
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=nextjs_cache:10m max_size=1g inactive=60m;

location / {
    proxy_cache nextjs_cache;
    proxy_cache_valid 200 60m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    # ... rest of config
}

5. Cleanup Script

Buat cleanup.sh buat bersihin unused Docker resources:

#!/bin/bash
docker system prune -af --volumes
docker image prune -af

Troubleshooting

Container ga mau start:

docker compose logs nextjs
docker compose ps

Port already in use:

sudo lsof -i :3000
sudo kill -9 <PID>

Nginx 502 Bad Gateway:

  • Check apakah container running: docker ps
  • Check container logs: docker compose logs nextjs
  • Verify port mapping di docker-compose.yml

SSL certificate issues:

sudo certbot certificates
sudo certbot renew --force-renewal

Kesimpulan

Selamat! Lo udah berhasil deploy Next.js ke VPS dengan setup yang production-ready:

  • ✅ Docker untuk containerization
  • ✅ Nginx sebagai reverse proxy
  • ✅ SSL gratis dari Let’s Encrypt
  • ✅ CI/CD dengan GitHub Actions
  • ✅ Basic monitoring

Setup ini bisa handle traffic yang lumayan gede dan gampang di-scale. Kalo traffic naik, tinggal upgrade VPS atau setup load balancer.

Next step? Mungkin lo bisa explore:

  • Docker Swarm atau Kubernetes untuk orchestration
  • Prometheus + Grafana untuk advanced monitoring
  • Cloudflare untuk CDN dan DDoS protection

Ada pertanyaan? Drop di comment atau reach out ke gue di Twitter. Happy deploying! 🚀