SSD Nodes Learn Hosting plans →
Rehberler Matt ConnorYazan Matt Connor · Güncellendi 2026-08-27

Docker Compose bellek sınırı nasıl ayarlanır?

Docker Compose deploy.resources ayarı ile container bellek limitlerini belirleyerek 137 hata kodunu yönetin. Tek bir servisin tüm sunucu RAM kapasitesini tüketmesini engelleyin.

Docker Compose bellek sınırının işlevi

Docker Compose bellek sınırı, Linux çekirdeğinin bir container'ın cgroup'una (süreç grupları için kaynakları ölçen çekirdek özelliği) uyguladığı katı bir üst sınırdır. Bir servis üzerinde deploy.resources.limits.memory ayarlandığında, ilgili container belirttiğiniz miktardan daha fazla bellek kullanamaz. Container bu sınırı aşmaya çalıştığında, çekirdek içerideki bir süreci sonlandırır ve container genellikle 137 çıkış koduyla kapanır.

Bu durum, RAM miktarının sabit olduğu ve ödünç alınabilecek boş ana makine belleğinin bulunmadığı VPS ortamlarında kritik öneme sahiptir. Bellek sızıntısı olan veya hatalı bir sorgu çalıştıran tek bir container, 8GB belleğe sahip bir sunucudaki tüm boş sayfaları tüketebilir. Bu durumda çekirdek, soruna neden olan container yerine genellikle veritabanı veya SSH oturumunuz gibi en sorunlu gördüğü süreci sonlandırır. Sınırlar, tüm sunucunun çökmesi yerine yalnızca ilgili servisin yeniden başlatılmasıyla sonuçlanır.

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

Ayarı uygulayın ve sınırın aktif olduğunu doğrulayın:

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

MEM USAGE / LIMIT sütunu 142MiB / 1GiB gibi bir değer göstermelidir. Eğer sınır sütununda ana makinenin toplam RAM miktarı görünüyorsa, ayar uygulanmamıştır; bu durum düzeltilmeden kılavuzun geri kalanı işlevsiz kalacaktır. Eğer compose dosyası yapısı sizin için yeniyse, VPS için Docker Compose temelleri bölümü bu yapının üzerine inşa edildiği dosya düzenini açıklamaktadır.

deploy.resources.limits veya mem_limit: hangisi geçerli

Aynı kavram için iki farklı yazım biçimi bulunduğundan bu durum kafa karıştırıcıdır.

mem_limit, mem_reservation, memswap_limit, cpus ve cpu_shares, eski Compose dosya formatlarından miras kalan üst düzey servis anahtarlarıdır. deploy.resources, Swarm şemasından gelmiştir ve günümüzde docker compose tarafından okunan Compose Spesifikasyonu'nun bir parçasıdır.

Her ikisi de tek bir sunucuda çalışır. docker compose eklentisi olan Compose V2, herhangi bir Swarm kümesi olmasa bile docker compose up komutunu çalıştırdığınızda deploy.resources.limits ve deploy.resources.reservations ayarlarını uygular. deploy bloğunun yalnızca Swarm ile ilgili kısımları diğer anahtarlardır: mode, placement, update_config ve endpoint_mode, docker stack deploy için bir anlam ifade ederken docker compose up tarafından görmezden gelinir. Dolayısıyla, "deploy bloğu Swarm gerektirir" şeklindeki yaygın tavsiye, resources alt bölümü için yanlıştır; bu tavsiyeye uymak, servislerinizin hiçbir sınırlama olmadan çalışmasına neden olur.

Her proje için tek bir yazım biçimi seçin. Aynı serviste hem mem_limit: 512m hem de deploy.resources.limits.memory: 1g kullanmak, dosyanın ilk bakışta anlaşılmasını imkansız hale getirir. Hangi değerin geçerli olduğunu tahmin etmek yerine daemon'a sorun:

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

Bellek değerleri bayt cinsindendir, bu nedenle 1g değeri 1073741824 olarak yazdırılır. CPU değeri nano CPU cinsindendir, bu nedenle 1.5 değeri 1500000000 olarak yazdırılır. Herhangi bir alandaki 0 değeri, hiçbir sınırlama ayarlanmadığı anlamına gelir. Docker'ın kabul ettiği en küçük bellek sınırı 6m değeridir; bunun altındaki değerlerde container başlatılmayı reddeder.

Bir container limitine ulaştığında ne olur

Container yavaşlamaz. Doğrudan sonlandırılır.

Bir süreç bir sayfa talep ettiğinde ve cgroup zaten memory.max değerine ulaşmışsa, çekirdek önce o cgroup içindeki temiz sayfa önbelleğini, ardından takas edilebilecek sayfaları geri kazanmaya çalışır. Geri kazanım yeterli alanı açmazsa, cgroup OOM (bellek yetersizliği) sonlandırıcısı container içindeki bir süreci seçer ve ona SIGKILL sinyali gönderir. Container'ın PID 1 sürecini öldürmek, container'ı sonlandırır. 137 çıkış kodu, 128 artı 9 sinyalinden oluşur; dolayısıyla 137, herhangi bir SIGKILL sinyalinin izidir, tek başına OOM'un kanıtı değildir.

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

true 137 bir OOM sonlandırmasıdır. false 137 ise SIGKILL sinyalinin başka bir kaynaktan gönderildiği anlamına gelir; bunun yaygın nedeni, uygulamanın SIGTERM sinyalini görmezden gelmesi sonucu docker compose stop değerinin on saniyelik bekleme süresini doldurmasıdır. Bu ayrımı bilmek saatler kazandırır, çünkü iki sorunun birbiriyle hiçbir ortak noktası yoktur.

Olayı kaydeden iki yer daha mevcuttur. Daemon'ı canlı izleyin:

docker events --filter event=oom

Ardından, yeniden başlatma sonrasında da varlığını koruyan kayıt olan çekirdek günlüğünü okuyun:

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

Bir cgroup sonlandırması, Memory cgroup out of memory: Killed process 24713 (node) ile başlayan bir satır yazdırır. Memory cgroup ön eki olmayan bir satır, ana makine OOM durumudur; bu, makinenin kendisinin RAM'inin tükendiği anlamına gelir. Bu, limitlerin önlemeyi amaçladığı başarısızlık türüdür; dolayısıyla bunu görmek, limitlerinizin toplamının çok yüksek olduğuna veya bazı servislerin hiçbir limitinin olmadığına işarettir.

restart: unless-stopped ile bir OOM döngüsü kolayca gizlenebilir, çünkü servis öldükten bir saniye sonra docker compose ps içerisinde çalışıyor görünür. Uptime sütununu ve yeniden başlatma sayısını kontrol edin; limiti, uygulamayı sağlıksız olarak raporlayan bir sağlık kontrolü ile eşleştirin. Böylece sürekli ölen bir container, siz onu izlemeseniz bile görünür hale gelir.

Rezervasyon bir ipucudur, limit ise kuraldır

reservations.memory (daha eski adıyla mem_reservation) esnek bir alt sınırdır. Docker bunu, daemon ana makinede çekişme veya düşük bellek tespit ettiğinde devreye giren esnek bir limit olarak tanımlar. Bu değer, bir container'ın bu sınırı aşmasını engellemez ve container bellek talep ettiğinde belleğin boş olacağını garanti etmez. Yalnızca çekirdeği (kernel), rezervasyon değerinin üzerinde olan container'lardan bellek geri kazanımı yapmaya yönlendirir.

Dolayısıyla rezervasyon tek başına hiçbir şeyi korumaz. Bunu, sistem üzerindeki baskı anlarında öncelikli tutulmasını istediğiniz servisleri işaretlemek için kullanın ve güvenlik için limit değerine güvenin. Rezervasyon değerini limitin altında tutun; aksi takdirde container başlatılamaz ve Docker yapılandırmayı Minimum memory limit can not be less than memory reservation limit hatasıyla reddeder.

Swap muhasebesi üzerine dürüst bir bakış

Çoğu VPS imajı, herhangi bir swap dosyası olmadan gelir. swapon --show ve free -h komutlarını çalıştırın. Eğer toplam swap değeri sıfırsa, aşağıdaki swap ile ilgili tüm ayarlar işlevsiz kalır ve bellek sınırınız yalnızca RAM kapasitesi ile kısıtlı olur.

memswap_limit, swap miktarı değildir. Bellek ve swap toplamını ifade eder. mem_limit: 1g ve memswap_limit: 2g kullanıldığında, container 1GB RAM ve 1GB swap alanına sahip olur. İki değeri birbirine eşit ayarlamak, container için swap alanını tamamen devre dışı bırakır. mem_limit değerini ayarlayıp memswap_limit değerini boş bırakmak, container'ın bellek sınırı kadar swap kullanmasına olanak tanır.

Ubuntu 24.04 ve Debian 13 varsayılan olarak cgroup v2 kullanır; burada swap ayrı bir sayaçtır (memory.swap.max) ve ek bir yapılandırma gerektirmez. Eski Your kernel does not support swap limit capabilities mesajı, swapaccount=1 parametresi olmadan başlatılan cgroup v1 ana makinelerinden kaynaklanır. Bu sistemlerde bellek sınırı uygulanmaya devam ederken swap kısmı göz ardı edilir.

Swap kullanımının size ne kazandırdığı konusunda dürüst olun. Swap, OOM kill işlemini daha az olası kılmaz, sadece yavaşlatır; çünkü bellek sızdıran bir süreç, RAM'i doldurduğu kadar rahat bir şekilde swap alanını da doldurur. Bu sırada, paylaşımlı VPS depolama biriminde swap kullanan bir container, sunucudaki diğer tüm servisleri yavaşlatır. Gecikmeye duyarlı işlemler için, swap alanı olmayan ve doğru yapılandırılmış bir sınır, daha hızlı ve öngörülebilir bir hata yönetimi sağlar.

Bellek kullanımının neden olduğundan daha kötü göründüğü

MEM USAGE içindeki docker stats değeri sayfa önbelleğini (page cache) de kapsar; bu nedenle büyük dosyalar okuyan bir container, sınırına doğru yükselir ve orada kalır. Bu durum normaldir ve bir sızıntı değildir; çünkü temiz önbellek, OOM killer devreye girmeden önce geri kazanılır. Kendi kendine barındırılan bir Jellyfin medya sunucusu gibi bir servis, tam olarak bu nedenle sürekli olarak üst sınırına yakın görünür.

Container içinden sayıyı önbellek ve gerçek çalışma kümesi olarak ayırın:

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

anon anonim bellektir; yani bırakılamayan çalışma kümesidir. file ise bırakılabilir olan sayfa önbelleğidir. Sınırınızı toplam değere göre değil, anon artı bir paya göre belirleyin. memory.events dosyası tartışmayı kesin olarak sonlandırır: sıfırdan büyük bir oom_kill sayacı, çekirdeğin container başladığından beri içinde bir şeyi sonlandırdığı anlamına gelir; artan bir max sayacı ise container'ın şu anda üst sınırında tutulduğunu gösterir. Her iki komut da imaj içinde bir shell ve coreutils gerektirir, bu yüzden distroless veya scratch imajlarda çalışmazlar.

8GB VPS üzerinde boyutlandırma sınırları

İşe uygulamalardan değil, ana makineden başlayın. 8GB kapasiteli bir VPS üzerinde; kernel, Docker daemon, sshd, journald ve kendi oturum kabuğunuz için yaklaşık 1GB boşluk bırakın. Geriye dağıtılabilecek yaklaşık 7GB kalır; tüm konteyner sınırlarının toplamı bu değerin altında tutulmalıdır. Kaynakları aşırı taahhüt etmek (overcommitting), iki servisin aynı anda yoğunlaştığı güne kadar sorunsuz çalışabilir.

8GB kapasiteli bir sunucu için uygulanabilir bir dağılım şöyledir:

  • Reverse proxy: 128m sınırı. Küçük bir süreçtir; bu kadar dar bir sınır, hatalı bir yapılandırma yüklemesini anında yakalar.
  • PostgreSQL: 2g sınırı; veritabanı yapılandırmasında shared_buffers değeri yaklaşık 512MB olarak ayarlanmalıdır.
  • Uygulama konteyneri: 1g sınırı.
  • Arka plan işçisi (worker): 512m sınırı.
  • Medya veya dosya servisi: 2g sınırı; bunun büyük bir kısmı sayfa önbelleği (page cache) olacaktır.

Bu sayıları kendi yığınınızda doğrudan kullanmayın. Servisleri bir gün boyunca gerçek yük altında çalıştırın, docker stats değerini izleyin, her konteyner için en yüksek anon değerini alın ve buna yaklaşık yarısı kadar bir pay (headroom) ekleyin. Çok dar belirlenmiş bir sınır, hiç sınır olmamasından daha kötüdür; çünkü normal bir trafik artışı sırasında sağlıklı bir servisi sonlandırır.

Bir tuzak özel olarak belirtilmelidir. Siz belirtmediğiniz sürece sınır, çoğu çalışma zamanı (runtime) için görünmezdir. PostgreSQL, shared_buffers ve work_mem değerlerini konteyner sınırını aşacak şekilde boyutlandırır ve sonuçta sonlandırılır. Bir JVM (Java virtual machine), yığınını (heap) ana makine RAM'i yerine cgroup sınırına göre boyutlandırmak için -XX:MaxRAMPercentage=75 parametresine ihtiyaç duyar. Node.js, konteyner sınırının altında olacak şekilde megabayt cinsinden --max-old-space-size değerine ihtiyaç duyar; aksi takdirde çöp toplayıcısı (garbage collector), kernel müdahale edene kadar yığını büyütmeye devam eder. Ollama için de durum farklı bir ayar düğmesiyle aynıdır; çünkü num_ctx değerini artırmak KV önbelleğini yüzlerce megabayt büyütür ve konteyner uzun bir istemin ortasında sonlanır. Cgroup pazarlık yapmaz. Doğrudan sonlandırır.

CPU limitleri tamamen farklı şekilde çalışır

cpus: "1.5", bir çekirdeğin %150'si anlamına gelir ve CFS (completely fair scheduler) kotası olarak uygulanır. Container, her 100ms'lik periyotta, tüm iş parçacıkları arasında paylaştırılmak üzere 150ms'lik CPU süresi alır. Bu süre tükendiğinde, çekirdek container'ı bir sonraki periyoda kadar bekletir.

Önemli fark budur. Bellek limitini aşan bir container sonlandırılır. CPU limitini aşan bir container ise kısıtlanır ve çalışmaya daha yavaş bir şekilde devam eder. Bu nedenle CPU limitini agresif bir şekilde ayarlamak güvenlidir, ancak bellek limiti için bir pay bırakılması gerekir.

cpu_shares farklı bir araçtır: yalnızca CPU'lar gerçekten doygunluğa ulaştığında önem kazanan göreceli bir ağırlıktır. 1024 ve 512 paya sahip iki container, yoğun bir çekirdeği kabaca ikiye bir oranında paylaşır; boşta olan bir sunucuda ise hiçbirine kısıtlama uygulanmaz. Servisleri önem derecesine göre sıralamak için payları kullanın; örneğin gece çalışan bir kod dönüştürme işleminin web sunucunuzu yavaşlatmasını engellemek gibi gerçek bir tavan değerine ihtiyaç duyduğunuzda ise cpus kullanın.

FAQ

deploy.resources.limits Docker Swarm olmadan çalışır mı?

Evet. Compose V2, tek bir sunucuda docker compose up komutunu çalıştırdığınızda deploy.resources.limits ve deploy.resources.reservations ayarlarını uygular. Bunu, sınırı bayt cinsinden yazdıran ve sınır uygulanmadığında 0 çıktısı veren docker inspect --format '{{.HostConfig.Memory}}' <container> komutu ile doğrulayabilirsiniz. deploy içerisinde yer alan ve gerçekten Swarm gerektiren anahtarlar mode, placement, update_config ve endpoint_mode anahtarlarıdır.

Docker Compose'da 137 çıkış kodu ne anlama gelir?

Bu kod, ana sürecin SIGKILL sinyali aldığını gösterir; çünkü 137 değeri, 128 ile 9 numaralı sinyalin toplamıdır. Bunun yaygın nedeni çekirdeğin OOM killer mekanizmasıdır, ancak bir uygulama SIGTERM sinyalini görmezden geldiğinde de kapatma zaman aşımı nedeniyle aynı kod üretilir. İkisini birbirinden ayırmak için docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> komutunu çalıştırın. true 137 bir bellek kaynaklı sonlandırmadır, false 137 ise değildir.

mem_limit mi yoksa deploy.resources.limits.memory mi kullanmalıyım?

Her ikisi de docker compose ile çalışır. deploy.resources.limits.memory, güncel Compose Specification formatıdır ve yeni dosyalar için daha iyi bir varsayılandır. Dosyanızın geri kalanı eski üst düzey anahtarları kullanıyorsa mem_limit kullanımını koruyun. Her ikisini aynı serviste tanımlamak dosyanın okunabilirliğini zorlaştırır; bu nedenle birini seçin ve sonucu docker inspect ile doğrulayın.

Container neden bellek sınırına ulaştığı halde sonlandırılmıyor?

docker stats içindeki kullanım değeri, çekirdeğin OOM kill tetiklemek yerine baskı altında boşalttığı sayfa önbelleğini (page cache) de içerir. docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat komutunu çalıştırın ve geri kazanılamayan çalışma kümesini gösteren anon değerini okuyun. Düşük bir anon değeri yanında yüksek bir file değeri, container'ın ölmek üzere olduğunu değil, disk üzerinde giriş/çıkış işlemi yaptığını gösterir.

8GB RAM'li bir VPS üzerinde ne kadar bellek boş bırakmalıyım?

Çekirdek, Docker daemon, sshd, journald ve kendi shell oturumunuz için yaklaşık 1GB boşluk bırakın, ardından tüm container sınırlarının toplamını kalan 7GB altında tutun. Bir değer belirlemeden önce gerçek yük altında bir gün boyunca her container için en yüksek anon değerini izleyin ve toplamı doldurulması gereken bir hedef değil, bir bütçe olarak değerlendirin.