CI/CD Next.js: Otomatisasi Build & Deploy dengan Docker, GitHub Actions, dan Github Container Registry (via SSH)
Ingin aplikasi Next.js kamu secara otomatis dibuild dan dideploy ke server setiap kali kamu melakukan push ke GitHub? Bayangkan kamu tidak perlu lagi login ke server, menarik update manual, atau menjalankan ulang container secara manual — semua proses tersebut bisa di-otomatisasi! Dalam artikel ini, kamu akan dipandu langkah demi langkah untuk membangun pipeline CI/CD yang modern dan efisien menggunakan kombinasi Docker, GitHub Actions, GitHub Container Registry (GHCR), serta deployment otomatis melalui SSH. Cocok untuk kamu yang ingin menghemat waktu, mengurangi potensi kesalahan, dan mengadopsi praktik DevOps terbaik dalam web development, cuy!

🔧 Apa yang Akan Kamu Pelajari
- Build dan containerize aplikasi Next.js menggunakan Docker
- Push Docker image ke GitHub Container Registry (GHCR)
- Setup GitHub Actions untuk otomatisasi build dan deployment
- Deploy ke server via SSH secara otomatis
🛠️ 1. Setup Proyek Next.js
Jika belum punya proyek Next.js:
npx create-next-app@latest my-next-app
cd my-next-app
Kemudian ubah output ke standalone
di next.config.js
:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
};
export default nextConfig;
📦 2. Containerize dengan Docker
Buat file Dockerfile
di root project:
# syntax=docker.io/docker/dockerfile:1
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
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
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
Struktur ini memastikan image tetap ringan dan hanya berisi kode siap jalan.
⚙️ 3. Siapkan GitHub Actions
Buat file workflow: .github/workflows/ci-cd.yml
name: Build & Deploy via GHCR
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
run: echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build Docker image
run: docker build -t ghcr.io/${{ github.repository_owner }}/my-next-app:latest .
- name: Push Docker image to GHCR
run: docker push ghcr.io/${{ github.repository_owner }}/my-next-app:latest
deploy:
runs-on: ubuntu-latest
needs: build-and-push
steps:
- name: Add SSH Key
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: SSH to VPS and deploy
run: |
ssh -o StrictHostKeyChecking=no ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} << EOF
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker pull ghcr.io/${{ github.repository_owner }}/my-next-app:latest
docker stop my-next-app || true
docker rm my-next-app || true
docker run -d --name my-next-app -p 80:3000 ghcr.io/${{ github.repository_owner }}/my-next-app:latest
EOF
🐳 4. Instalasi Docker di VPS
Agar server kamu bisa menarik dan menjalankan image dari GHCR, pastikan Docker sudah terinstal di dalamnya.
Ikuti panduan resmi Docker untuk instalasi di Ubuntu di sini: 👉 https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
🔑 5. Konfigurasi Secrets GitHub
Buka Repo Settings > Secrets and variables > Actions, lalu tambahkan secrets berikut:
Nama Secret | Penjelasan |
---|---|
GHCR_TOKEN |
Personal access token GitHub dengan akses write:packages dan read:packages |
VPS_HOST |
IP address server kamu |
VPS_USER |
Username SSH (contoh: ubuntu , root ) |
SSH_PRIVATE_KEY |
Private key (dalam format PEM) |
🔐 Tips:
- Gunakan
ssh-keygen
untuk membuat key baru jika belum punya, dan pastikan public key-nya sudah ditambahkan ke~/.ssh/authorized_keys
di server. - Buat
GHCR_TOKEN
dari halaman Developer Settings > Personal Access Tokens. - Centang minimal
write:packages
,read:packages
, danrepo
.
🧪 6. Tes Deployment
Setelah setup selesai:
Push perubahan ke branch
main
GitHub Actions akan:
- Build image Docker dari Next.js
- Push ke GitHub Container Registry
- SSH ke server dan jalankan container terbaru
Cek server kamu di browser: http://<ip-server>:3000
🧠 Penutup
Dengan pipeline ini, kamu:
- ✅ Menghemat waktu deployment
- ✅ Build di GHCR mencegah beban berat di VPS
- ✅ Menghindari human error
- ✅ Menerapkan standar DevOps modern
Pipeline ini bisa kamu kembangkan lebih lanjut, misalnya dengan notifikasi ke Discord/Slack, backup otomatis, atau staging environment.
❓ Masih Bingung?
Kalau kamu masih bingung dengan implementasinya, silakan cek repositori contoh yang saya buat: