Self-Host Next.js: Output Standalone, Docker, dan Env Var
Belajar self-host Next.js: output standalone, isi image runner Docker, env var saat build vs runtime, NEXT_PUBLIC, dan health check, dari file nyata.
Oleh Matthews WongDitinjau pada
Poin utama
Self-hosting Next.js berarti menjalankan server Node.js sendiri alih-alih platform terkelola. Atur output ke standalone dan next build menulis server.js minimal dengan dependency yang terlacak saja. Anda menyalin public dan static sendiri, membekukan nilai NEXT_PUBLIC saat build, membaca variabel server saat runtime, dan memberi container sebuah health check.
Setelah pelajaran ini, Anda bisa
- Mengaktifkan output standalone dan mendaftar apa yang harus ada di image runner.
- Membedakan nilai NEXT_PUBLIC saat build dari variabel server saat runtime, dan menempatkan masing-masing dengan benar.
- Menambahkan health check yang membuktikan server Next.js menjawab.
Ikuti diagramnya langkah demi langkah
Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.
Dari next build hingga server yang berjalan dan dicek
Saat build, nilai NEXT_PUBLIC dibekukan dan folder standalone ditulis; Anda menambahkan static dan public. Saat runtime, server.js dijalankan, membaca variabel server secara langsung, dan menjawab health check.
Tombol: ← → langkah · Home / End · K putar atau jeda
Ada dua momen penting. Saat build, Next.js mengompilasi dan menentukan file yang dibutuhkan server. Saat runtime, proses Node.js melayani request di dalam container.
next build mengganti setiap referensi NEXT_PUBLIC di JavaScript browser dengan nilai tertulis langsung. Mengubah variabelnya kemudian tidak mengubah build yang sudah ada.
process.env.NEXT_PUBLIC_*Dengan output standalone, build menulis folder berisi server.js minimal dan hanya file node_modules yang terlacak dibutuhkan.
output: "standalone"Tebak langkah berikutnya Berikutnya, Anda menjalankan server standalone. Folder mana yang masih diharapkan Anda salin? (Hanya node_modules · public dan .next/static · Tidak ada, semua disalin otomatis)Build standalone tidak menyertakan public dan .next/static secara default, karena CDN sebaiknya yang melayani. Salin ke output dan server.js akan melayaninya.
COPY --from=builder /app/.next/static ./.next/staticRunner hanya memuat output ini, dan berjalan sebagai user non-root. PORT dan HOSTNAME menentukan tempat server mendengarkan.
CMD ["node", "server.js"]Tebak langkah berikutnya Berikutnya, Anda mengubah nilai NEXT_PUBLIC hanya di container yang berjalan. Apa yang dipakai bundle browser? (Nilai yang baru · Nilai lama saat build)Variabel tanpa prefix tetap di server dan dibaca saat request ditangani, jadi satu image bisa berjalan dengan nilai berbeda untuk tiap lingkungan.
const apiKey = process.env.GROQ_API_KEY;Health check meminta route murah dari dalam container. Jika server.js berhenti menjawab, container ditandai tidak sehat dan tidak diam-diam berjalan.
--spider http://127.0.0.1:3000/robots.txt
Apa yang diberikan output standalone?
Saat next build, Next.js melacak setiap halaman beserta dependency-nya lewat analisis statis penggunaan import, require, dan fs, untuk mengetahui file apa yang mungkin dimuat server produksi. Atur output ke standalone di next.config dan ia menyalin hanya file itu ke .next/standalone, folder yang bisa Anda deploy sendiri tanpa menjalankan npm install, bersama server.js minimal yang Anda jalankan dengan node, bukan next start.
Baca selengkapnya (2)Tutup
Pelacakan bisa keliru ke dua arah. Dokumentasi Next.js menjelaskan outputFileTracingIncludes untuk file yang terlewat dan outputFileTracingExcludes untuk yang disertakan tanpa perlu, masing-masing berdasarkan glob route. Di monorepo, root pelacakan secara default adalah direktori proyek, jadi file di luarnya butuh outputFileTracingRoot. Bila container standalone crash karena modul hilang, pelacakan adalah tersangka pertama.
Dokumentasi juga menyatakan server minimal tidak menyalin public atau .next/static secara default, karena idealnya ditangani CDN, tetapi Anda bisa menyalinnya ke standalone/public dan standalone/.next/static dan server.js akan melayaninya. Lupa langkah ini adalah gejala klasik: HTML termuat, tetapi setiap skrip, stylesheet, dan gambar menjawab 404.
Env var mana yang dibekukan saat build?
Secara default environment variable hanya ada di server. Nama berawalan NEXT_PUBLIC_ berbeda: Next.js menyisipkan nilainya ke bundle JavaScript saat next build, mengganti setiap referensi process.env dengan nilai tertulis langsung. Dokumentasi memperingatkan bahwa setelah build, aplikasi tidak lagi merespons perubahan, dan satu image Docker yang di-deploy ke beberapa lingkungan membekukan nilai itu seperti saat build.
Baca selengkapnya (1)Tutup
Dua detail sering menjebak. Pencarian dinamis seperti process.env[name], atau menyalin process.env ke variabel dulu, tidak disisipkan. Dan nilai yang dibaca di server saat dynamic rendering dievaluasi saat runtime, yang disebut dokumentasi sebagai hal yang memungkinkan satu image dipromosikan ke beberapa lingkungan. Jadi: pengaturan publik non-rahasia adalah build argument; secret dan nilai per-lingkungan tetap tanpa prefix dan diberikan saat container dijalankan. Pelajaran env var dan secret Docker di jalur DevOps membahas sisi container-nya.
Apa lagi yang berubah saat Anda menjalankan server sendiri?
Panduan self-hosting merekomendasikan reverse proxy seperti nginx di depan server Next.js alih-alih mengeksposnya langsung, sehingga request cacat, koneksi lambat, dan rate limit ditangani sebelum sampai ke renderer. Streaming membutuhkan proxy berhenti mem-buffer respons, dan panduan menunjukkan header X-Accel-Buffering untuk nginx. Pelajaran reverse proxy di jalur DevOps membahas pengaturannya.
Baca selengkapnya (2)Tutup
Next.js juga menyimpan cache sendiri. Secara default ia menyimpan halaman dan data hasil generate di memori, 50 MB kecuali diubah, dan di disk tiap instance. Itu cukup untuk satu container dengan disk persisten, tetapi tiap instance tambahan punya salinan sendiri, jadi banyak instance membutuhkan cache handler bersama. Untuk berhenti dengan bersih, kirim SIGINT atau SIGTERM dan biarkan server menyelesaikan request yang sedang berjalan, dengan masa pengosongan 10 sampai 30 detik yang direkomendasikan.
Dokumentasi menjelaskan konfigurasinya tetapi tidak mewajibkan endpoint kesehatan. Route murah apa pun yang membuktikan server menjawab bisa dipakai: route khusus yang Anda tambah, atau yang sudah ada. Pilih yang tidak menyentuh database, kalau tidak dependency yang lambat membuat server sehat tampak rusak.
Konfigurasi nyata di balik pelajaran ini
Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.
output: "standalone" as const, // self-hosted Docker deploy — trims node_modules to only what server.js needsSatu baris mengaktifkan fiturnya. Semua isi pelajaran ini mengikutinya: build kini menulis folder standalone dengan server.js sendiri, dan Dockerfile menyalin folder itu, bukan seluruh proyek.
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]Stage runner menyalin tiga hal: public, folder standalone, dan .next/static ke path yang diharapkan server.js. Flag chown memberi user non-root kepemilikan atas file yang dijalankannya. Proses lalu berjalan sebagai user non-root dan dimulai dengan node server.js, bukan next start.
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME=0.0.0.0PORT dan HOSTNAME adalah dua variabel yang disebut dokumentasi Next.js untuk server standalone. Dokumentasi menampilkan HOSTNAME 0.0.0.0 di contohnya sendiri. Compose kemudian mempublikasikan port container hanya di alamat loopback, jadi reverse proxy adalah pintu masuknya.
function getGroqClient(): Groq {
if (!groq) {
const apiKey = process.env.GROQ_API_KEY;
if (!apiKey) {
throw new Error("GROQ_API_KEY environment variable is missing");
}
groq = new Groq({ apiKey });
}
return groq;Secret khusus server, dibaca tanpa prefix NEXT_PUBLIC di dalam fungsi yang berjalan saat request ditangani, dan gagal dengan jelas bila tidak ada. Ia tidak pernah masuk bundle browser, dan container menerima nilainya saat dijalankan, bukan saat image dibuild.
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://127.0.0.1:3000/robots.txt",
]
interval: 30s
timeout: 5s
retries: 3
start_period: 20sPengecekan berjalan di dalam container, jadi menargetkan port internal. Ia meminta robots.txt, route murah yang sudah ada dan tidak menyentuh database, bukan endpoint baru. Start period memberi server.js waktu untuk booting sebelum kegagalan dihitung, dan tiga kegagalan berturut-turut menandai container tidak sehat.
cacheMaxMemorySize: 16 * 1024 * 1024,Cache memori default adalah 50 MB, seperti dinyatakan panduan self-hosting. Situs ini menurunkannya, karena hampir setiap route diprerender saat build dan dibaca dari disk, dan server berbagi mesin kecil. Setel hanya setelah Anda mengukur memori.
Kesalahan yang benar-benar membuang waktu
Halaman termuat tetapi semua aset 404
Folder standalone tidak menyertakan .next/static atau public secara default. Salin .next/static ke .next/static di dalam output dan public di samping server.js, lalu build ulang image dan cek URL skrip secara langsung.
Nilai NEXT_PUBLIC mengabaikan file env baru
Nilainya disisipkan saat next build, jadi me-restart container tidak bisa mengubahnya. Build ulang dengan nilai baru, atau sajikan pengaturan itu dari API route Anda sendiri agar klien memintanya saat runtime.
Health check yang selalu gagal
Penyebab umum adalah port yang salah, path yang salah, atau start period lebih pendek dari waktu booting. Jalankan perintah yang sama secara manual di dalam container dan baca exit code-nya.
Modul hilang hanya di container
Pelacakan file melewatkan sesuatu, sering kali file yang dimuat dengan cara yang tidak terlihat analisis statis. Tambahkan dengan outputFileTracingIncludes untuk route terkait, build ulang, dan pastikan ada di folder standalone.
Lakukan di komputer Anda sendiri
Jalankan server standalone di mesin Anda sendiri sebelum memasukkannya ke container. Anda akan melihat persis apa yang harus direproduksi Dockerfile.
Atur output ke standalone di next.config, lalu jalankan build produksi.
npm run buildJalankan server tanpa menyalin apa pun dan buka sebuah halaman. Perhatikan gaya dan skrip yang hilang, lalu periksa 404 di panel network browser.
node .next/standalone/server.jsHentikan, salin public dan .next/static ke folder standalone seperti ditunjukkan dokumentasi Next.js, lalu jalankan lagi. Aset kini termuat.
cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/Jalankan di port lain untuk melihat environment variable bekerja, lalu minta route murah seperti yang dilakukan health check.
PORT=8080 HOSTNAME=0.0.0.0 node .next/standalone/server.js
Tiga pertanyaan sebelum lanjut
Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.
Jawab dalam hati, lalu cek
1.Kenapa Dockerfile menyalin .next/static terpisah dari folder standalone?Lihat jawabannya
Jawaban:Output standalone tidak menyertakan .next/static dan public secara default, karena idealnya CDN yang melayani. Menyalinnya ke output membuat server.js bisa melayaninya sendiri.
2.Di mana API key sebaiknya disimpan, dan bagaimana route chat membacanya?Lihat jawabannya
Jawaban:Di variabel server tanpa prefix yang diberikan ke container saat dijalankan. Route membaca process.env.GROQ_API_KEY di dalam fungsi saat request, sehingga tidak pernah sampai ke bundle browser.
3.Apa yang dikendalikan PORT dan HOSTNAME untuk server.js?Lihat jawabannya
Jawaban:Port dan alamat tempat server mendengarkan. Situs ini mengatur 3000 dan 0.0.0.0 di Dockerfile agar container bisa dijangkau lewat port yang dipublikasikan.
Bahasan mendalam terkait di situs ini
Sumber
Sumber primer yang diperiksa pada tanggal riset. Ikuti tautannya untuk versi terbaru.