SSD Nodes Learn RAM 8GB — $66/tahun
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-01

Batas Memori Docker Compose agar Tidak OOM

Pelajari cara mengatur batas memori dan CPU di Docker Compose dengan deploy.resources atau mem_limit, memahami exit 137, swap, serta menentukan ukuran yang aman.

Fungsi batas memori Docker Compose

Batas memori Docker Compose adalah pembatas ketat yang diterapkan kernel Linux pada cgroup (control group, fitur kernel untuk mengukur sumber daya sekumpulan proses) milik satu container. Tetapkan deploy.resources.limits.memory pada suatu service, dan container tersebut tidak akan pernah menggunakan memori melebihi angka yang Anda tulis. Jika mencoba melampauinya, kernel akan menghentikan proses di dalam container, dan container biasanya keluar dengan kode 137.

Hal ini paling penting pada VPS, karena jumlah RAM tetap dan tidak ada memori host tambahan yang dapat digunakan. Satu container dengan kebocoran memori atau query yang buruk dapat menggunakan setiap halaman memori bebas pada server 8GB. Kernel kemudian menghentikan proses yang dinilainya paling bermasalah. Proses tersebut sering kali berupa database atau sesi SSH Anda, bukan container yang menyebabkan masalah. Batas memori mengubah gangguan pada seluruh server menjadi masalah pada satu service yang dapat dimulai ulang.

services:
  app:
    image: ghcr.io/example/app:1.4
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: 1g
        reservations:
          memory: 256m

Terapkan pengaturan tersebut dan pastikan batasnya aktif:

docker compose up -d
docker stats --no-stream

Kolom MEM USAGE / LIMIT seharusnya menampilkan nilai seperti 142MiB / 1GiB. Jika kolom batas menampilkan seluruh RAM host, pengaturan tersebut tidak diterapkan. Sisa panduan ini tidak akan membantu sebelum pengaturan itu diterapkan. Jika file Compose masih baru bagi Anda, dasar-dasar Docker Compose untuk VPS menjelaskan tata letak file yang menjadi dasar konfigurasi ini.

deploy.resources.limits atau mem_limit: mana yang berlaku

Ada dua penulisan untuk gagasan yang sama. Hal ini menyebabkan kebingungan.

mem_limit, mem_reservation, memswap_limit, cpus, dan cpu_shares adalah kunci layanan tingkat atas yang diwarisi dari format file Compose lama. deploy.resources berasal dari skema Swarm dan sekarang menjadi bagian dari Compose Specification, yaitu format yang dibaca docker compose saat ini.

Keduanya berfungsi pada satu host. Compose V2, plugin docker compose, menerapkan deploy.resources.limits dan deploy.resources.reservations saat Anda menjalankan docker compose up, tanpa cluster Swarm. Bagian khusus Swarm dalam blok deploy adalah kunci lainnya: mode, placement, update_config, dan endpoint_mode hanya bermakna bagi docker stack deploy dan diabaikan oleh docker compose up. Jadi, anggapan umum bahwa "deploy memerlukan Swarm" tidak berlaku untuk subseksi resources. Jika mengikutinya, layanan Anda tidak memiliki batas sama sekali.

Pilih satu penulisan untuk setiap proyek. Menulis mem_limit: 512m dan deploy.resources.limits.memory: 1g pada layanan yang sama menghasilkan file yang sulit dipahami. Jangan menebak nilai mana yang berlaku. Tanyakan kepada daemon:

docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1

Nilai memori menggunakan satuan byte, sehingga 1g ditampilkan sebagai 1073741824. CPU menggunakan satuan nano CPU, sehingga 1.5 ditampilkan sebagai 1500000000. Nilai 0 pada bidang apa pun berarti tidak ada batas yang ditetapkan. Batas memori terkecil yang diterima Docker adalah 6m. Di bawah nilai tersebut, container tidak dapat dijalankan.

Apa yang terjadi ketika container mencapai batas

Container tidak melambat. Container berhenti.

Ketika suatu proses meminta page dan cgroup sudah mencapai memory.max, kernel terlebih dahulu merebut kembali resource yang dapat digunakan di dalam cgroup tersebut: page cache yang bersih, lalu page yang dapat dipindahkan ke swap. Jika reclaim tidak membebaskan cukup resource, OOM (out of memory) killer pada cgroup memilih proses di dalam container dan mengirimkan SIGKILL. Menghentikan PID 1 milik container akan mengakhiri container. Exit code 137 hanyalah 128 ditambah signal 9, sehingga 137 merupakan tanda SIGKILL apa pun, bukan bukti OOM dengan sendirinya.

docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1

true 137 adalah OOM kill. false 137 berarti ada sesuatu yang mengirim SIGKILL, dan penyebab umumnya adalah docker compose stop mencapai masa tenggang sepuluh detiknya karena aplikasi mengabaikan SIGTERM. Perbedaan ini menghemat waktu berjam-jam karena kedua masalah tersebut tidak berkaitan.

Ada dua tempat lain yang mencatat kejadian ini. Pantau daemon secara langsung:

docker events --filter event=oom

Kemudian baca log kernel, yaitu catatan yang tetap ada setelah restart:

sudo dmesg -T | grep -i -E 'memory cgroup out of memory|killed process'

Cgroup kill mencetak baris yang diawali Memory cgroup out of memory: Killed process 24713 (node). Baris tanpa awalan Memory cgroup adalah OOM pada host, yang berarti mesin itu sendiri kehabisan RAM. Kondisi inilah yang seharusnya dicegah oleh batas, sehingga kemunculannya menandakan bahwa jumlah seluruh batas terlalu tinggi atau ada service yang sama sekali tidak memiliki batas.

Dengan restart: unless-stopped, loop OOM mudah tersembunyi karena service terlihat aktif di docker compose ps satu detik setelah berhenti. Periksa kolom uptime dan jumlah restart, lalu pasangkan batas tersebut dengan healthcheck yang melaporkan aplikasi sebagai unhealthy agar container yang terus berhenti terlihat tanpa perlu terus memantaunya.

Reservasi adalah petunjuk, batas adalah aturan

reservations.memory (mem_reservation yang lebih lama) adalah batas bawah lunak. Docker menjelaskannya sebagai batas lunak yang diaktifkan saat daemon mendeteksi perebutan sumber daya atau memori rendah pada host. Reservasi tidak pernah menghentikan container menggunakan memori melebihi nilai tersebut, dan tidak pernah menjamin bahwa memori akan tersedia saat diminta oleh container. Reservasi hanya mengarahkan kernel untuk terlebih dahulu mereklamasi memori dari container yang penggunaan memorinya melebihi nilai reservasi.

Jadi, reservasi tidak melindungi apa pun jika digunakan sendiri. Gunakan reservasi untuk menandai layanan yang ingin Anda prioritaskan saat terjadi tekanan sumber daya, dan gunakan limit sebagai pengaman. Tetapkan reservasi di bawah limit. Jika tidak, container tidak akan dimulai: Docker menolak konfigurasi tersebut dengan Minimum memory limit can not be less than memory reservation limit.

Akuntansi swap secara tepat

Sebagian besar image VPS dirilis tanpa file swap. Jalankan swapon --show dan free -h. Jika total swap bernilai nol, semua pengaturan terkait swap di bawah ini tidak berpengaruh, dan batas memori Anda hanya berupa batas RAM.

memswap_limit bukan jumlah swap. Nilai tersebut adalah total memori ditambah swap. Dengan mem_limit: 1g dan memswap_limit: 2g, container mendapatkan RAM sebesar 1GB dan swap sebesar 1GB. Jika kedua nilai tersebut disamakan, container sama sekali tidak mendapatkan swap. Dengan menetapkan mem_limit dan membiarkan memswap_limit tidak ditetapkan, container dapat menggunakan swap hingga sebesar batas memorinya lagi.

Ubuntu 24.04 dan Debian 13 menggunakan cgroup v2 secara default. Pada versi ini, swap memiliki penghitung terpisah (memory.swap.max), dan pengaturan ini berfungsi tanpa konfigurasi tambahan. Pesan lama Your kernel does not support swap limit capabilities berasal dari host cgroup v1 yang di-boot tanpa swapaccount=1. Pada host tersebut, batas memori tetap berlaku, sedangkan bagian swap diabaikan.

Pahami manfaat swap secara realistis. Swap membuat penghentian proses akibat OOM menjadi lebih lambat, bukan membuatnya lebih kecil kemungkinannya, karena proses yang mengalami kebocoran memori akan memenuhi swap sama mudahnya seperti memenuhi RAM. Sementara itu, container yang terus-menerus menggunakan swap pada penyimpanan VPS bersama akan memperlambat semua layanan lain di server tersebut. Untuk apa pun yang sensitif terhadap latensi, batas yang tepat tanpa swap akan gagal lebih cepat dan lebih terprediksi.

Mengapa penggunaan memori terlihat lebih buruk daripada kondisi sebenarnya

Angka MEM USAGE dalam docker stats mencakup page cache. Karena itu, container yang membaca file besar akan meningkat hingga mendekati batasnya dan tetap berada di sana. Ini normal dan bukan kebocoran, karena cache bersih akan direklamasi sebelum OOM killer dipanggil. Layanan seperti server media Jellyfin yang di-host sendiri akan terlihat terus-menerus mendekati batasnya karena alasan ini.

Pisahkan angka tersebut menjadi cache dan working set sebenarnya dari dalam container:

docker compose exec app grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat
docker compose exec app cat /sys/fs/cgroup/memory.events

anon adalah memori anonim, yaitu working set yang tidak dapat dibuang. file adalah page cache, yang dapat dibuang. Tetapkan batas berdasarkan anon ditambah margin, bukan berdasarkan totalnya. File memory.events memberikan kepastian: penghitung oom_kill yang lebih besar dari nol berarti kernel telah mematikan sesuatu dalam container ini sejak container dimulai, sedangkan penghitung max yang terus meningkat berarti container saat ini ditahan pada batasnya. Kedua perintah memerlukan shell dan coreutils di dalam image, sehingga gagal pada image distroless atau scratch.

Batas ukuran pada VPS 8GB

Mulailah dari host, bukan dari aplikasi. Pada VPS 8GB, sisakan sekitar 1GB untuk kernel, daemon Docker, sshd, journald, dan shell login Anda. Dengan demikian, tersedia sekitar 7GB untuk dibagikan, dan jumlah semua batas container harus tetap di bawah nilai tersebut. Overcommit dapat berjalan baik sampai dua layanan mencapai beban puncak secara bersamaan.

Pembagian yang dapat digunakan pada sistem 8GB:

  • Reverse proxy: batas 128m. Proses ini kecil, dan batas seketat ini akan segera mendeteksi pemuatan ulang konfigurasi yang tidak terkendali.
  • PostgreSQL: batas 2g, dengan shared_buffers yang ditetapkan sekitar 512MB dalam konfigurasi database.
  • Container aplikasi: batas 1g.
  • Worker latar belakang: batas 512m.
  • Layanan media atau file: batas 2g, yang sebagian besar akan digunakan sebagai cache halaman.

Jangan menyalin angka tersebut ke stack Anda sendiri. Jalankan layanan dalam beban nyata selama satu hari, pantau docker stats, catat nilai puncak anon untuk setiap container, lalu tambahkan sekitar setengahnya lagi sebagai ruang cadangan. Batas yang terlalu ketat lebih buruk daripada tanpa batas, karena dapat menghentikan layanan yang sehat selama lonjakan traffic normal.

Satu masalah perlu dibahas secara khusus. Sebagian besar runtime tidak mengetahui batas tersebut kecuali Anda memberitahukannya. PostgreSQL dapat menetapkan ukuran shared_buffers dan work_mem melebihi batas containernya, lalu dihentikan. JVM (Java virtual machine) memerlukan -XX:MaxRAMPercentage=75 agar ukuran heap ditetapkan berdasarkan batas cgroup, bukan RAM host. Node.js memerlukan --max-old-space-size dalam megabyte, yang ditetapkan di bawah batas container. Jika tidak, garbage collector akan membiarkan heap bertambah sampai kernel menghentikannya. cgroup tidak melakukan negosiasi. cgroup menghentikan proses.

Batas CPU berperilaku sangat berbeda

cpus: "1.5" berarti 150% dari satu core, yang diberlakukan sebagai kuota CFS (completely fair scheduler). Container mendapatkan waktu CPU selama 150ms dalam setiap periode 100ms, yang digunakan bersama oleh semua thread di dalamnya. Setelah waktu tersebut habis, kernel membuatnya menunggu hingga periode berikutnya.

Inilah perbedaan pentingnya. Container yang melampaui batas memorinya akan dihentikan. Container yang melampaui batas CPU akan diperlambat dan tetap berjalan. Karena itu, batas CPU aman jika ditetapkan secara agresif, sedangkan batas memori memerlukan ruang cadangan.

cpu_shares adalah alat yang berbeda: bobot relatif yang hanya berpengaruh saat CPU benar-benar jenuh. Dua container dengan shares sebesar 1024 dan 512 akan membagi core yang sibuk dengan rasio kira-kira dua banding satu. Pada host yang tidak sibuk, keduanya tidak dibatasi. Gunakan shares untuk menentukan prioritas layanan, dan gunakan cpus jika memerlukan batas maksimum yang nyata, misalnya untuk mencegah pekerjaan transcode malam hari menghabiskan sumber daya sehingga web server tidak memperoleh CPU yang cukup.

FAQ

Apakah deploy.resources.limits berfungsi tanpa Docker Swarm?

Ya. Compose V2 menerapkan deploy.resources.limits dan deploy.resources.reservations saat Anda menjalankan docker compose up pada satu host. Konfirmasikan dengan docker inspect --format '{{.HostConfig.Memory}}' <container>, yang mencetak batas dalam byte dan mencetak 0 jika tidak ada batas yang diterapkan. Kunci di dalam deploy yang benar-benar memerlukan Swarm adalah mode, placement, update_config, dan endpoint_mode.

Apa arti kode keluar 137 di Docker Compose?

Artinya, proses utama menerima SIGKILL karena 137 adalah 128 ditambah sinyal 9. OOM killer kernel adalah penyebab yang umum, tetapi batas waktu penghentian menghasilkan kode yang sama jika aplikasi mengabaikan SIGTERM. Jalankan docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> untuk membedakan keduanya. true 137 adalah penghentian karena kehabisan memori, sedangkan false 137 bukan.

Sebaiknya saya menetapkan mem_limit atau deploy.resources.limits.memory?

Keduanya berfungsi dengan docker compose. deploy.resources.limits.memory adalah bentuk Compose Specification saat ini dan merupakan pilihan default yang lebih baik untuk file baru. Pertahankan mem_limit jika bagian lain dari file Anda sudah menggunakan kunci tingkat atas yang lebih lama. Menetapkan keduanya pada satu service hanya membuat file lebih sulit dibaca, jadi pilih salah satu dan verifikasi hasilnya dengan docker inspect.

Mengapa container saya menggunakan batas memori penuhnya tanpa dihentikan?

Angka penggunaan di docker stats mencakup page cache, yang akan dilepas oleh kernel saat terjadi tekanan memori, bukan memicu penghentian OOM. Jalankan docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat dan baca nilai anon, yang merupakan working set yang tidak dapat direklamasi. Nilai file yang tinggi di samping nilai anon yang rendah menunjukkan bahwa container sedang melakukan input dan output disk, bukan container yang akan segera berhenti.

Berapa banyak RAM yang harus saya sisakan agar tidak dialokasikan pada VPS 8GB?

Sisakan sekitar 1GB untuk kernel, Docker daemon, sshd, journald, dan shell Anda sendiri. Kemudian, pertahankan jumlah seluruh batas container di bawah sisa 7GB. Pantau nilai puncak anon per container dalam beban nyata selama sehari sebelum menetapkan angka. Perlakukan total tersebut sebagai anggaran, bukan sebagai target yang harus dipenuhi.