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

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

Docker Compose deploy.resources.limits.memory ayarını kullanarak container bellek kullanımını sınırlandırın. 137 çıkış kodu ve OOM hatalarını önlemek için VPS yapılandırması.

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çlanan bir izolasyon sağlar.

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ütununda 142MiB / 1GiB gibi bir değer görünmelidir. Eğer sınır sütununda ana makinenin toplam RAM miktarı görünüyorsa, ayar uygulanmamış demektir; bu durum düzeltilmeden kılavuzun geri kalanı işlevsiz kalacaktır. Eğer compose dosyası yapısına aşina değilseniz, VPS için Docker Compose temelleri bölümü, bu yapılandırmanı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 dosyası biçimlerinden devralınan üst düzey servis anahtarlarıdır. deploy.resources ise Swarm şemasından gelmiş olup, günümüzde docker compose aracının okuduğu Compose Spesifikasyonu'nun bir parçasıdır.

Her ikisi de tek bir sunucuda çalışır. docker compose eklentisi olan Docker Compose, herhangi bir Swarm kümesi olmasa dahi docker compose up komutunu çalıştırdığınızda deploy.resources.limits ve deploy.resources.reservations ayarlarını uygular. Eklenti şu anda v5 serisindedir (2026-10-10 tarihinde kontrol edildiğinde en güncel kararlı sürüm v5.6.0 idi). Eski Compose dosyası biçimi sürümleriyle karışıklığı önlemek için v2'den doğrudan v5'e geçilmiş, 3 ve 4 sürümleri atlanmıştır; hiçbir v5 sürümü kaynak limitlerinin işlenme biçimini değiştirmemiştir. deploy bloğunun Swarm odaklı kısımları, docker stack deploy için bir düğüm kümesi genelinde zamanlama ve dağıtımları tanımlayan placement, update_config ve endpoint_mode gibi diğer anahtarlardır. Dolayısıyla, "deploy bloğu Swarm gerektirir" şeklindeki yaygın tavsiye, resources alt bölümü için yanlıştır ve buna uymak, servislerinizin hiçbir limit olmadan çalışmasına neden olur.

Proje başına tek bir yazım biçimi seçin. Aynı servis üzerinde mem_limit: 512m ve deploy.resources.limits.memory: 1g kullanmak okunabilirliği zorlaştırmanın ötesindedir: Compose Spesifikasyonu, mem_limit ayarlandığında bunun limits.memory ile tutarlı olması gerektiğini belirtir ancak çelişki durumunda hangi değerin öncelikli olacağına dair bir kural tanımlamaz. Hangi yazımı kullanırsanız kullanın, daemon'ın gerçekte neyi uyguladığını doğrulayın:

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 limitin ayarlanmadığı anlamına gelir. Docker'ın kabul ettiği en küçük bellek limiti 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 sonlanır.

Bir süreç bir sayfa talep ettiğinde ve cgroup zaten memory.max sınırındaysa, çekirdek önce o cgroup içindeki temiz sayfa önbelleğini, ardından swap 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ürecinin öldürülmesi container'ı sonlandırır. 137 çıkış kodu, 128 ile 9 sinyalinin toplamıdır; dolayısıyla 137 herhangi bir SIGKILL sinyalinin izidir, tek başına OOM olduğunun 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 genellikle başka bir sürecin SIGKILL gönderdiği anlamına gelir (bir program kasıtlı olarak 137 ile çıkış yapabilir ancak bu nadirdir) ve 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ı olarak izleyin:

docker events --filter event=oom

Ardından yeniden başlatma sonrasında da varlığını koruyan ç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. Bu satır, bir container'ın kendi sınırına ulaştığı anlamına gelir ve sunucuda hala bol miktarda boş RAM varken de ortaya çıkabilir. Memory cgroup öneki olmayan bir satır, sunucu genelinde bir OOM durumudur; bu da makinenin kendisinin RAM'inin tükendiği anlamına gelir. Bu, limitlerin önlemeyi amaçladığı hata türüdür; dolayısıyla böyle bir durumla karşılaşmak, genellikle limitlerinizin toplamının çok yüksek olduğu, bazı servislerin hiçbir limiti olmadığı veya Docker dışındaki bir şeyin ayırdığınız boş alandan daha fazlasını kullandığı anlamına gelir.

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

Reservation bir ipucudur, limit ise kuraldır

reservations.memory (daha eski adıyla mem_reservation) yumuşak bir taban değeridir. Docker bunu, daemon ana makinede çekişme veya düşük bellek durumu tespit ettiğinde devreye giren yumuşak bir limit olarak tanımlar. Bu değer, container'ın bunun üzerine çıkmasını engellemez ve container bellek talep ettiğinde belleğin boş olacağını garanti etmez. Yalnızca çekirdeği, reservation değerinin üzerinde olan container'lardan bellek geri kazanımı yapmaya yönlendirir.

Dolayısıyla reservation 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. Reservation değerini limit değerinin 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 kullanımı hakkında dürüst olmak gerekirse

Çoğu VPS imajı, varsayılan olarak 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ğıda belirtilen 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ını değil, bellek ve swap toplamını ifade eder. mem_limit: 1g ve memswap_limit: 2g değerleri ile container 1GB RAM ve 1GB swap alanına sahip olur. Bu iki 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; bu sürümde swap ayrı bir sayaçtır (memory.swap.max) ve ek bir yapılandırma gerektirmeden çalışır. 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ı geçerli olmaya devam ederken swap kısmı göz ardı edilir.

Swap kullanımının size ne kazandırdığı konusunda dürüst olun. Swap, bellek sızıntısı olan bir sürecin RAM'i doldurduğu gibi swap alanını da aynı hızla doldurması nedeniyle OOM (Out of Memory) kill durumunu engellemez, sadece geciktirir. Bu sırada, paylaşımlı VPS depolama biriminde swap kullanan bir container, sunucudaki diğer tüm servislerin yavaşlamasına neden olur. Gecikmeye duyarlı işlemler için, swap alanı olmayan ve doğru yapılandırılmış bir bellek sınırı, daha hızlı ve öngörülebilir bir hata yönetimi sağlar.

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

Linux üzerinde, docker stats içindeki MEM USAGE değeri yalnızca etkin olmayan sayfa önbelleğini (cgroup v2 üzerinde inactive_file) çıkarır; bu nedenle yakın zamanda kullanılan önbellek sayılmaya devam eder ve büyük dosyalar okuyan bir container, limitine 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 atılamayan çalışma kümesidir. file ise atılabilen sayfa önbelleğidir. Limitlerinizi toplam değere göre değil, anon artı bir paya göre belirleyin. memory.events dosyası tartışmayı kesin olarak bitirir: sıfırın üzerindeki bir oom_kill sayacı, çekirdeğin bu container başladığından beri bir şeyi sonlandırdığı anlamına gelir; sürekli artan bir max sayacı ise container'ın şu anda üst sınırında tutulduğunu gösterir. Her iki komut da image içinde bir shell ve coreutils gerektirir, bu nedenle distroless veya scratch image üzerinde çalışmazlar.

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

İşe uygulamalardan değil, ana makineden başlayın. 8GB bir VPS üzerinde çekirdek, Docker daemon, sshd, journald ve kendi oturum kabuğunuz için yaklaşık 1GB alan ayırın. Geriye dağıtılacak yaklaşık 7GB kalır; tüm container sınırlarının toplamı bu değerin altında kalmalıdır. Kaynakları aşırı taahhüt etmek (overcommitting), iki servisin aynı anda zirve yaptığı güne kadar sorunsuz çalışabilir.

8GB bir makine için uygulanabilir bir dağılım:

  • Reverse proxy: 128m sınırı. Küçük bir süreçtir; bu kadar dar bir sınır, kontrolden çıkan bir yapılandırma yeniden 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 container'ı: 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 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 container için zirve anon değerini alın ve buna yaklaşık yarısı kadar bir pay 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 bir notu hak ediyor. Bazı çalışma zamanları (runtime), kendilerine belirtmediğiniz sürece sınırı görmezden gelir; sınırı okuyanlar ise kendilerini beklediğiniz şekilde boyutlandırmayabilir. PostgreSQL, shared_buffers ve work_mem değerlerini container sınırının üzerine çıkaracak şekilde boyutlandırabilir ve sonuçta sonlandırılır. Modern bir JVM (Java virtual machine), cgroup sınırını varsayılan olarak algılar; ancak OpenJDK gibi HotSpot sürümleri yığına (heap) bu sınırın yalnızca yüzde 25'ini verir. Bu nedenle, yığının sınırın daha fazlasını kullanmasına izin verirken yığın dışındaki bellek için de yer bırakmak amacıyla genellikle -XX:MaxRAMPercentage=75 kullanılır. Node.js, 12. sürümden itibaren varsayılan yığınını cgroup sınırından, container sınırının yaklaşık yarısı kadar ve 2GB civarında bir tavanla boyutlandırır. Bu yüzden, farklı bir tavan istediğinizde --max-old-space-size değerini yalnızca megabayt cinsinden ayarlayın ve çekirdek müdahale etmeden önce çöp toplayıcının (garbage collector) çalışabilmesi için bu değeri container sınırının altında tutun. Ollama da farklı bir ayar düğmesiyle aynı durumdadır; çünkü num_ctx değerini artırmak KV önbelleğini yüzlerce megabayt büyütür ve container uzun bir istemin ortasında sonlanır. Cgroup pazarlık yapmaz. Doğrudan sonlandırır.

CPU limitleri tamamen farklı ç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 150ms CPU süresi alır ve bu süre tüm thread'leri arasında paylaştırılır. Bu süre dolduğunda, kernel container'ı bir sonraki periyoda kadar bekletir.

Önemli fark buradadır. 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üvenliyken, bellek limiti için bir pay bırakmak 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 yaklaşık ikiye bir oranında paylaşır; boş 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 transcode işleminin web sunucunuzu yavaşlatmasını engellemek gibi gerçek bir üst sınır koymanız gerektiğinde ise cpus kullanın.

FAQ

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

Evet. Docker Compose ve docker compose eklentisi (artık v5 serisinde), tek bir ana makine üzerinde docker compose up komutunu çalıştırdığınızda deploy.resources.limits ve deploy.resources.reservations ayarlarını uygular. Bunu, limiti bayt cinsinden yazdıran ve limit uygulanmadığında 0 çıktısı veren docker inspect --format '{{.HostConfig.Memory}}' <container> komutu ile doğrulayabilirsiniz. deploy içindeki, Swarm göz önünde bulundurularak yazılmış anahtarlar; placement, update_config ve endpoint_mode gibi küme (cluster) odaklı anahtarlardır.

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

Bu kod neredeyse her zaman ana sürecin SIGKILL sinyali aldığını gösterir; çünkü 137 değeri, 128 ile 9 numaralı sinyalin toplamıdır (bir program kasıtlı olarak da 137 ile çıkış yapabilir ancak bu nadirdir). Çoğu durumda sebep ç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ırmayı, false 137 ise bunun aksini belirtir.

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 formudur 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 ayarını koruyabilirsiniz. Compose Specification, her ikisi de ayarlandığında bu iki değerin tutarlı olmasını gerektirir; bu nedenle birini seçin ve sonucu docker inspect ile doğrulayın.

Container neden sonlandırılmadan tam bellek limitinde kalıyor?

docker stats içindeki kullanım değeri, çekirdeğin OOM kill tetiklemek yerine baskı altında boşalttığı yakın zamanlı sayfa önbelleğini (page cache) de sayar. 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. Yüksek bir file değerinin yanında düşük bir anon değeri görmek, container'ın ölmek üzere değil, disk üzerinde girdi/çıktı işlemi yapıyor olduğunu gösterir.

8GB RAM'li bir VPS'de ne kadar RAM'i tahsis edilmemiş 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 limitlerinin toplamını kalan 7GB'ın altında tutun. Sayısal değerlere karar vermeden önce gerçek yük altında bir gün boyunca her container için tepe anon değerini izleyin ve toplamı doldurulması gereken bir hedef değil, bir bütçe olarak değerlendirin.

Bu yazı işinize yaradı mı?