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 Non-Root User (Recommended)
# 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 loVPS_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! 🚀