Bertahun-tahun workflow container lokal saya punya pajak di setiap perubahan: edit file, pindah ke terminal, jalankan docker compose up --build, tunggu tiga puluh detik sampai dua menit, lalu cek browser. Kalikan dengan sekitar delapan puluh perubahan yang saya buat dalam satu sore yang fokus, dan saya membuang banyak waktu hanya untuk menunggu rebuild. Docker Compose Watch menghapus pajak itu hampir seluruhnya, dan fiturnya sudah ada di binary Compose yang Anda pakai sekarang.
Jawaban singkat untuk Anda yang sedang mencari: tambahkan blok develop.watch ke compose.yaml, deklarasikan path mana yang harus sync, rebuild, atau sync+restart, lalu jalankan docker compose up --watch. Compose Watch resmi GA di Compose v2.22.0 (dibundel dengan Docker Desktop 4.24), jadi kalau Anda pernah update Docker sejak akhir 2023, fiturnya sudah tersedia. Di artikel ini saya akan membahas cara saya memakainya untuk setup API NestJS plus worker, di mana ia mengalahkan bind mount, dan gotcha yang sempat menggigit saya di proyek nyata.
Kenapa Loop Dev Lokal yang Lama Terasa Lambat
Sebelum Watch, ada dua opsi untuk kode live di dalam container, dan keduanya punya sisi tajam:
- Rebuild penuh di setiap perubahan. Benar secara teknis tapi sangat lambat, terutama untuk proyek Node di mana npm install mendominasi waktu build meski layer caching sudah bagus.
- Bind mount direktori source Anda. Cepat, tapi ikut menyeret node_modules milik host, binary spesifik OS, dan file temp editor ke dalam container, menyebabkan drift klasik works-on-my-machine yang justru ingin Anda hindari dengan container.
- Di macOS dan Windows, bind mount juga membayar penalti translasi filesystem lewat VM, sehingga operasi yang berat file terasa jauh lebih lambat dibanding Linux native.
Compose Watch mengambil jalan ketiga: ia mengamati file di host dan menyalin perubahan satu arah ke container yang sedang berjalan. Image Anda tetap menjadi satu-satunya sumber kebenaran untuk dependency dan library sistem, sementara source code mengalir masuk saat Anda menekan save. Perubahan di sisi container tidak pernah bocor balik ke host, dan itu persis arah trust yang Anda inginkan saat development.
Tiga Action Watch: sync, rebuild, sync+restart
Setiap aturan watch memasangkan sebuah path dengan sebuah action. Memilih action yang tepat per path adalah sumber percepatan sesungguhnya, karena action termurah yang tetap menghasilkan container yang benar adalah pemenangnya.
| Action | Apa yang dilakukan | Kapan saya memakainya |
|---|---|---|
| sync | Menyalin file yang berubah ke container berjalan di path target, tanpa me-restart apa pun. | Source code di framework apa pun dengan hot reload: NestJS dengan webpack HMR, Next.js, Vite, script yang diawasi nodemon. |
| rebuild | Membangun ulang image dengan BuildKit dan mengganti container yang berjalan, seperti docker compose up --build untuk service itu. | package.json, lockfile, Dockerfile, apa pun yang terpanggang ke dalam layer image. Juga bahasa terkompilasi seperti Go di mana binary adalah artefaknya. |
| sync+restart | Menyalin file yang berubah, lalu me-restart proses container tanpa membangun ulang image. | File konfigurasi yang dibaca sekali saat boot: file .env, nginx.conf, ormconfig. Image-nya tidak masalah, prosesnya saja yang perlu start ulang. |
compose.yaml Nyata dengan Aturan Watch
Berikut versi ringkas dari setup yang saya jalankan untuk API NestJS plus background worker. Perhatikan bagaimana setiap path mendapat action termurah yang masih layak, dan bagaimana ignore mencegah file test memicu sync yang sia-sia:
# compose.yaml — NestJS API + worker, watch-enabled
services:
api:
build: ./api
ports:
- "3000:3000"
develop:
watch:
# hot-reload source straight into the container
- path: ./api/src
target: /app/src
action: sync
# dependency changes need a real rebuild
- path: ./api/package.json
action: rebuild
# config tweak? just restart the process
- path: ./api/.env.development
action: sync+restart
target: /app/.env.development
worker:
build: ./worker
develop:
watch:
- path: ./worker/src
target: /app/src
action: sync
ignore:
- "**/*.spec.ts"Lalu jalankan semuanya dengan watch aktif:
docker compose up --watch
# or, to run watch without attaching logs:
docker compose watchPro tip: tetap pakai docker compose up --watch (satu terminal, log dan watch jadi satu) sampai stack Anda membesar. Dengan lebih dari tiga atau empat service, pisahkan log ke terminal lain dengan docker compose logs -f api supaya notifikasi sync tidak menenggelamkan output aplikasi Anda.
Compose Watch vs Bind Mount: Mana yang Menang
Saya masih sering ditanya kenapa tidak mount saja direktori source dan selesai. Ini perbandingan jujurnya setelah menjalankan kedua pendekatan di proyek klien dan aplikasi saya sendiri yang di-deploy ke VPS:
Compose Watch
Sync satu arah dari host ke container. node_modules tetap milik container, dibangun untuk Linux di dalam image. File temp editor otomatis difilter, dan save beruntun di-debounce menjadi transfer batch. Lingkungan dev sangat dekat dengan perilaku image production.
Bind mount
Dua arah secara default, jadi tulisan dari container mengotori tree di host Anda. node_modules host menutupi milik image kecuali Anda menambah trik anonymous volume. I/O lebih lambat lewat batas VM macOS/Windows. Masih oke untuk eksperimen cepat dan proyek satu service.
Aturan praktis saya: bind mount untuk prototyping sekali pakai, Compose Watch untuk apa pun yang punya Dockerfile dan kelak akan dirilis. Action sync memberi kecepatan feedback selevel bind mount sambil menjaga image sebagai kontrak, dan kontrak itulah yang membuat lompatan dari laptop ke node production Docker Swarm saya terasa membosankan, dan memang itu tujuannya.
Gotcha yang Menghabiskan Satu Sore Saya
- Watch hanya bekerja pada service yang punya atribut build. Kalau sebuah service hanya punya image, Compose tidak punya apa-apa untuk di-rebuild atau di-sync dan seksi watch-nya diabaikan.
- Image container butuh binary stat, mkdir, dan rmdir agar sync berfungsi. Distroless dan beberapa image ultra-minimal tidak memilikinya, jadi siapkan varian dev dari Dockerfile Anda berbasis tag slim atau alpine.
- User container harus bisa menulis ke target sync. Kalau Dockerfile Anda turun ke user non-root (dan memang seharusnya begitu di production), pastikan user itu memiliki direktori target di stage dev Anda, atau sync akan gagal diam-diam.
- Path di ignore relatif terhadap path yang diawasi, bukan root proyek. Saya sempat kehilangan waktu bertanya-tanya kenapa exclusion dist saya tidak berefek sampai membaca ulang dokumentasinya.
Jangan arahkan action sync ke package.json dengan harapan dependency ikut terinstall. Sync hanya menyalin file; node_modules di container tidak akan berubah. Manifest dependency tempatnya di action rebuild, titik.
Checklist Adopsi: Migrasi Proyek dalam 15 Menit
- Jalankan docker compose version dan pastikan Anda di v2.22.0 atau lebih baru; upgrade Docker Desktop atau plugin Compose kalau belum.
- Hapus bind mount source code dari file compose Anda, sisakan hanya named volume untuk database dan cache.
- Tambahkan blok develop.watch per service: sync untuk source yang bisa hot reload, rebuild untuk manifest dependency dan Dockerfile, sync+restart untuk config yang dibaca saat boot.
- Tambahkan entri ignore untuk file test, output build, dan apa pun yang tidak dibutuhkan hot reloader Anda.
- Jalankan docker compose up --watch, ubah satu file, dan pastikan Anda melihat notifikasi sync diikuti baris log hot reload dari framework Anda.
Intinya
Compose Watch adalah salah satu fitur quality-of-life yang diam-diam balik modal dalam satu jam pertama. Loop terukur saya di codebase NestJS ukuran menengah turun dari sekitar 45 detik per perubahan dengan rebuild menjadi di bawah dua detik dengan sync plus webpack HMR, dan tidak seperti bind mount, saya tidak menukar fidelitas environment untuk mendapatkannya.
Kalau tim Anda masih rebuild image di setiap save, atau berkelahi dengan hantu node_modules dari bind mount, luangkan lima belas menit itu. Permukaan konfigurasinya cuma tiga action dan satu daftar ignore. Itu saja seluruh fiturnya, dan itu sudah cukup.
Sumber dan bacaan lanjutan

