Kembali ke Panduan Deploy

Belum tahu Docker? Mulai dari sini — ada gambar di tiap langkah

Deploy blog pakai Docker, dari nol

Pernah dengar kata "Docker" tapi bingung itu apa? Tenang. Panduan ini menganggap kamu belum pernah memakainya sama sekali. Kita mulai dari analogi paling sederhana, lalu pelan-pelan sampai blog kamu benar-benar tayang di dalam Docker. Tinggal ikuti dan cocokkan dengan gambar di tiap langkah.

Sekali bungkus

Blog + Node + semua dependensi jadi satu "kotak" yang rapi.

Jalan di mana saja

Kotak yang sama jalan di laptop maupun di VPS — tanpa drama.

Update gampang

Ganti kode → build ulang kotak → jalankan. Selesai.

1
Pahami dulu

Apa itu Docker, sebenarnya?

Bayangkan kamu mau mengirim makanan ke teman. Daripada membawa kompor, panci, bumbu, dan bahan satu per satu (dan berharap dapur temanmu punya alat yang sama), kamu cukup memasukkan seluruhnya ke dalam satu kotak bekal yang sudah lengkap. Di mana pun kotak itu dibuka, isinya sama persis.

Docker itu kotak bekal untuk aplikasi. Ia membungkus blog kamu bersama Node.js dan semua yang dibutuhkannya ke dalam satu paket yang disebut image. Saat image dijalankan, ia menjadi container— aplikasi yang hidup dan berjalan. Karena semuanya sudah ada di dalam kotak, blog akan jalan sama persis di laptop kamu maupun di VPS, tanpa kalimat klasik "kok di komputerku jalan, di server tidak?".

Dockerfile

Resep: cara membungkus blog jadi kotak.

Image

Kotak yang sudah jadi & siap dikirim.

Container

Kotak yang sedang dibuka & berjalan.

Ingat 3 kata ini saja: Dockerfile (resep) → image (kotak jadi) → container (kotak yang sedang jalan). Sisanya cuma perintah.
2

Install Docker di VPS

Masuk ke VPS lewat SSH seperti di panduan utama. Cara termudah memasang Docker adalah dengan skrip resmi mereka. Cukup jalankan baris-baris ini satu per satu:

deploy@vps: ~
deploy@vps:~$ curl -fsSL https://get.docker.com -o get-docker.shdeploy@vps:~$ sudo sh get-docker.shClient: Docker Engine - CommunityServer: Docker Engine - Community# biar bisa pakai docker tanpa sudo terus-terusan:deploy@vps:~$ sudo usermod -aG docker $USERdeploy@vps:~$ newgrp dockerdeploy@vps:~$ docker --versionDocker version 27.3.1, build ce1223035a
Setelah usermod -aG docker, sebaiknya logout lalu login lagi ke VPS agar hak akses grup benar-benar aktif. Kalau masih muncul permission denied saat memanggil Docker, itulah penyebabnya.
3

Siapkan Next.js untuk Docker (mode standalone)

Agar kotak Docker kita kecil dan cepat, minta Next.js menghasilkan versi standalone — sebuah folder mungil yang sudah memuat semua yang diperlukan untuk jalan. Cukup tambahkan satu baris di next.config.mjs di komputer kamu:

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  // ⬇️ baris penting untuk Docker: bikin folder kecil siap-jalan
  output: 'standalone',
  images: { unoptimized: true },
}
 
export default nextConfig
Baris output: 'standalone' inilah kuncinya. Tanpa ini, hasil build tersebar ke mana-mana dan kotak Docker jadi besar serta ribet. Simpan, lalu commit perubahan ini ke GitHub.
4

Buat file Dockerfile (resepnya)

Di folder utama project (sejajar dengan package.json), buat file bernama Dockerfile— tanpa ekstensi. Ini adalah "resep" yang memberi tahu Docker cara membungkus blog. Tampilan isinya kira-kira seperti ini di editor:

GNU nano 7.2~/blog/Dockerfile
1# Tahap 1 — build2FROM node:20-alpine AS builder3WORKDIR /app4RUN corepack enable5COPY package.json pnpm-lock.yaml ./6RUN pnpm install --frozen-lockfile7COPY . .8RUN pnpm build9 10# Tahap 2 — jalan11FROM node:20-alpine AS runner12WORKDIR /app13ENV NODE_ENV=production14COPY --from=builder /app/public ./public15COPY --from=builder /app/.next/standalone ./16COPY --from=builder /app/.next/static ./.next/static17EXPOSE 300018CMD ["node", "server.js"]
^O Write Out^X Exit^W Where Is^K Cut

Salin teks lengkapnya dari sini:

# Dockerfile — resep membungkus blog jadi satu "kotak"
 
# 1) Tahap build: pasang dependency & build Next.js
FROM node:20-alpine AS builder
WORKDIR /app
RUN corepack enable
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
 
# 2) Tahap jalan: kotak akhir yang ramping & cepat
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# salin hanya hasil yang perlu dari tahap build
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
Kenapa ada dua FROM? Ini trik "multi-stage": tahap pertama dipakai untuk membangun (berat, banyak file), tahap kedua hanya menyalin hasil jadinya. Hasilnya kotak akhir jauh lebih kecil dan cepat dijalankan.
5

Buat .dockerignore

Sama seperti .gitignore, file .dockerignore memberi tahu Docker file apa saja yang tidak perlu ikut masuk ke dalam kotak. Ini membuat build lebih cepat dan kotak lebih ramping:

# .dockerignore — biar build cepat & kotak tetap ramping
node_modules
.next
.git
.env
npm-debug.log
README.md
Memasukkan node_modules dan .next di sini itu penting — keduanya akan dibuat ulang di dalam kotak, jadi tidak perlu ikut disalin dari komputer kamu.
6

Build image (membuat kotaknya)

Di VPS, masuk ke folder blog, lalu suruh Docker membaca resep tadi dan membuat image. Tanda -t blog artinya kita memberi nama blog pada kotaknya.

deploy@vps: ~
deploy@vps:~$ cd ~/blogdeploy@vps:~$ docker build -t blog .[+] Building 48.2s (16/16) FINISHED => [builder 4/6] RUN pnpm install --frozen-lockfile => [builder 6/6] RUN pnpm build => exporting to image => => naming to docker.io/library/blog:latest

Build pertama agak lama karena Docker mengunduh Node.js dan semua dependensi. Build berikutnya jauh lebih cepat karena hasilnya di-cache. Cek kotakmu sudah jadi:

deploy@vps: ~
deploy@vps:~$ docker imagesREPOSITORY   TAG      SIZEblog         latest   182MB
7

Jalankan container (membuka kotaknya)

Sekarang jalankan image tadi menjadi container. -d artinya jalan di latar belakang, -p 3000:3000 menyambungkan port dalam kotak ke port VPS, dan --restart unless-stopped bikin blog otomatis hidup lagi kalau server di-reboot.

deploy@vps: ~
deploy@vps:~$ docker run -d --name blog --restart unless-stopped -p 3000:3000 bloga1b2c3d4e5f6...deploy@vps:~$ docker psCONTAINER ID   IMAGE   STATUS          PORTSa1b2c3d4e5f6   blog    Up 6 seconds    0.0.0.0:3000->3000/tcp

Blog kamu kini berjalan di dalam Docker! Coba buka http://IP-VPS-KAMU:3000 di browser:

http://203.0.113.10:3000
Koneksi aman — HTTPS aktif

Blog berbasis markdown

Tulis di Markdown. Push ke GitHub. Deploy di VPS milikmu sendiri.

Menyiapkan VPS pertama kamu

Sebuah catatan singkat tentang menjalankan blog kamu sendiri…

Menulis dengan Markdown

Sebuah catatan singkat tentang menjalankan blog kamu sendiri…

Deployment berhasil
Mau lihat catatan/log container? Jalankan docker logs -f blog. Mau berhenti? docker stop blog. Mau hapus? docker rm blog.
8
Disarankan

Lebih rapi: pakai Docker Compose

Mengetik perintah docker run yang panjang tiap kali itu melelahkan. Docker Compose menyimpan semua pengaturan itu ke dalam satu file docker-compose.yml, jadi kamu cukup mengetik satu perintah pendek. Buat file ini di folder blog:

# docker-compose.yml — jalankan blog dengan satu perintah
services:
  blog:
    build: .
    container_name: blog
    restart: unless-stopped
    ports:
      - "3000:3000"
    # kalau blog kamu butuh env, aktifkan baris ini:
    # env_file: .env

Sekarang hentikan container lama (kalau masih jalan dari langkah 7), lalu jalankan semuanya hanya dengan satu baris:

deploy@vps: ~
deploy@vps:~$ docker rm -f blogdeploy@vps:~$ docker compose up -d --build[+] Building 12.4s ... FINISHED[+] Running 1/1 ✔ Container blog  Started
Mulai sekarang: docker compose up -d --build untuk menyalakan/memperbarui, dan docker compose down untuk mematikan. Jauh lebih mudah diingat.
9

Pasang Nginx + HTTPS di depan container

Saat ini blog hanya bisa diakses lewat :3000 dan masih http. Untuk memakai domain sendiri dengan https, kita taruh Nginx di depan container — persis seperti di panduan VPS utama. Container Docker tetap mendengarkan di localhost:3000, jadi konfigurasi Nginx-nya sama saja:

GNU nano 7.2/etc/nginx/sites-available/blog
1server {2    listen 80;3    server_name domainkamu.com www.domainkamu.com;4 5    location / {6        proxy_pass http://localhost:3000;7        proxy_http_version 1.1;8        proxy_set_header Host $host;9        proxy_set_header X-Real-IP $remote_addr;10    }11}
^O Write Out^X Exit^W Where Is^K Cut
Langkah Nginx, firewall, dan HTTPS gratis (Certbot) sudah dibahas detail di panduan deploy VPS (langkah 10–12). Ikuti bagian itu — yang berbeda hanya: blog kamu kini berjalan di dalam Docker, bukan PM2.
10

Cara update blog nanti

Mau menerbitkan tulisan baru atau mengubah kode? Alurnya selalu sama: push perubahan dari laptop ke GitHub, lalu di VPS tarik kode terbaru dan build ulang kotaknya.

deploy@vps: ~
deploy@vps:~$ cd ~/blogdeploy@vps:~$ git pulldeploy@vps:~$ docker compose up -d --build ✔ Container blog  Started# bersihkan kotak lama yang sudah tak terpakaideploy@vps:~$ docker image prune -fTotal reclaimed space: 180.2MB

Biar tidak perlu mengetik berulang, simpan langkah-langkah itu dalam file deploy.sh di repo kamu:

#!/usr/bin/env bash
set -e
cd ~/blog
echo "==> Ambil kode terbaru"
git pull
echo "==> Build ulang image & jalankan container baru"
docker compose up -d --build
echo "==> Bersihkan image lama yang tak terpakai"
docker image prune -f
echo "Deploy berhasil — blog jalan di dalam Docker!"

Jadikan bisa dieksekusi sekali saja dengan chmod +x deploy.sh. Setelah itu, tiap update cukup jalankan ./deploy.sh.

Kalau ada masalah

GejalaSolusi
permission denied saat panggil dockerLupa logout/login setelah usermod -aG docker $USER (lihat langkah 2)
Cannot find module server.jsLupa output: 'standalone' di next.config.mjs (langkah 3) lalu build ulang
Port 3000 already in useContainer lama masih jalan — hentikan dengan docker rm -f blog
Build kehabisan memori di VPS 1 GBTambahkan swap (lihat panduan utama langkah 8), lalu build ulang
Container langsung mati (Exited)Lihat sebabnya dengan docker logs blog

Perintah penyelamat: docker ps -a (lihat semua container), docker logs blog (lihat error), dan docker compose up -d --build (bangun ulang).

Selamat!

Kamu baru saja men-deploy blog di dalam Docker — dari yang tadinya belum tahu Docker sama sekali. Kembali ke artikel.