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

VPS uzerinde Docker kullanirken dikkat edilmesi gerekenler

VPS ortaminda Docker calistirirken karsilasilan RAM yetersizligi, UFW kurallarini bypass eden portlar, yeniden baslatma sorunlari ve disk dolmasi gibi kritik riskleri ogrenin.

VPS üzerinde Docker çalıştırmanın farkları

VPS üzerinde çalışan Docker, dizüstü bilgisayarınızdaki ile aynı motoru ve aynı imajları kullanır; bu nedenle bildiğiniz tüm komutlar geçerliliğini korur. Değişen şey, çevresindeki ortamdır. Dizüstü bilgisayarlar boş bellek alanına, kimsenin taramadığı bir güvenlik duvarına ve asla dolmayacak kadar büyük bir diske sahiptir. Kiralanan bir sunucunun ise sabit bir bellek sınırı, açıldığı andan itibaren taranmaya başlanan bir genel IP adresi ve Docker'ın sormadan dolduracağı bir kök dosya sistemi vardır.

Küçük bir sunucuda sorunların çoğuna dört temel fark neden olur:

  • Bellek sınırlıdır ve çekirdek, bellek yetersizliği durumunda bir süreci sonlandırarak (kill) sorunu çözer.
  • Docker kendi güvenlik duvarı kurallarını yazdığı için, dışarıya açılan bir port UFW (uncomplicated firewall) kurallarını doğrudan bypass eder.
  • Konteynerler, önceden belirtilmediği sürece yeniden başlatma sonrasında otomatik olarak ayağa kalkmaz.
  • İmajlar, konteynerler, volume'lar ve build cache, disk dolana kadar büyümeye devam eder.

Aşağıdaki her bölüm, karşılaşılan hatayı, göreceğiniz hata mesajını ve sorunu derinlemesine çözen kılavuzu tanımlar. Henüz bir compose dosyası yazmadıysanız, önce VPS üzerinde Docker Compose temelleri sayfasını okuyun ve ardından buraya dönün. Bu sayfa, bir stack'i halihazırda ayağa kaldırabildiğiniz varsayımıyla hazırlanmıştır.

Bir Docker container ne kadar RAM kullanır?

Çoğu kişinin beklediğinden daha az. Bir container, sanal makine değil, cgroup (kontrol grubu) içindeki bir süreçtir; dolayısıyla konuk çekirdek veya sabit bir bellek tahsisi yoktur. Maliyet, içerideki sürecin eriştiği bellek miktarı kadardır. Sanal makinelerle oluşturulan aynı yığının sığmayacağı bir ortamda, tam bir yığının 2 GB içinde çalışabilmesinin nedeni budur.

Aşağıdaki rakamlar, Ubuntu 24.04 üzerinde varsayılan yapılandırmayla çalışan standart imajların, başlatıldıktan birkaç dakika sonra docker stats ile okunan tipik boşta çalışma değerleridir. Bunlar planlama için bir başlangıç noktasıdır, iş yükünüzün bir kıyaslaması değildir. Herhangi bir rakama güvenmeden önce kendi sunucunuzda docker stats --no-stream komutunu çalıştırın.

ChartTypical idle container memory, stock images, megabytes
The data behind this chart
[
  {
    "label": "nginx",
    "idle_mb": 8,
    "budget_mb": 64
  },
  {
    "label": "Redis 7",
    "idle_mb": 12,
    "budget_mb": 128
  },
  {
    "label": "Traefik v3",
    "idle_mb": 40,
    "budget_mb": 128
  },
  {
    "label": "PostgreSQL 16",
    "idle_mb": 45,
    "budget_mb": 512
  },
  {
    "label": "Uptime Kuma",
    "idle_mb": 95,
    "budget_mb": 256
  },
  {
    "label": "MariaDB 11",
    "idle_mb": 190,
    "budget_mb": 512
  },
  {
    "label": "Nextcloud (Apache)",
    "idle_mb": 210,
    "budget_mb": 768
  }
]

İki sütun farklı işlevlere sahiptir. idle_mb, container hiçbir işlem yapmazken kullandığı miktardır. budget_mb ise planlama yaparken ayırmanız gereken miktardır, çünkü gerçek kullanım boşta çalışma durumundan farklıdır. PostgreSQL boşta 45 MB civarında çalışır ancak bağlantılar, sıralama işlemleri ve önbellek aktif olduğunda 512 MB talep eder. Planlamayı bütçe sütununa göre yapın. Hata ayıklama işlemlerini ise boşta çalışma sütununa göre gerçekleştirin.

7 satırlık tablonun yapısına dikkat edin. nginx boşta 8 MB, Nextcloud ise 210 MB tüketir. Uygulamalarınızın önündeki proxy neredeyse ücretsizdir. Sunucu boyutunu belirlemenizi gerektiren unsurlar veritabanı ve PHP uygulamasıdır.

docker stats hakkında bir uyarı: bellek değeri, container'ın kendi dosya okumalarıyla çektiği sayfa önbelleğini (page cache) de içerir; bu nedenle başlatıldıktan sonra bir süre yükselir ve ardından dengelenir. Bir sızıntı olduğundan emin olmadan önce değeri bir saat boyunca izleyin.

VPS boyutlandırma: 2 GB, 4 GB ve 8 GB RAM ile neler yapılabilir?

Öncelikle ana makinenin (host) payını ayırın. Kernel, systemd, journald, sshd ve Docker daemon, container'larınızla aynı RAM'i kullanır; dockerd ve containerd bu alanın yaklaşık 100 MB'ını kaplar. Ayrıca sayfa önbelleği (page cache) ve imaj derleme veya veritabanı yedeği alma gibi ani yük artışları için boş belleğe ihtiyacınız vardır.

ChartRAM budget by plan size, megabytes
The data behind this chart
[
  {
    "plan": "2 GB VPS",
    "total_mb": 2048,
    "host_reserve_mb": 768,
    "container_mb": 1280
  },
  {
    "plan": "4 GB VPS",
    "total_mb": 4096,
    "host_reserve_mb": 1024,
    "container_mb": 3072
  },
  {
    "plan": "8 GB VPS",
    "total_mb": 8192,
    "host_reserve_mb": 1536,
    "container_mb": 6656
  }
]

host_reserve_mb işletim sistemini, Docker daemon'ı ve sunucunun yük altında yanıt verebilir kalmasını sağlayan payı kapsar. Geriye kalan kısım container_mb değeridir ve harcayabileceğiniz tek miktar budur. Bu yedek pay, plan büyüdükçe artar; en küçük sunucuda 768 MB iken en büyük sunucuda 1536 MB olur. Çünkü daha büyük bir sunucu daha fazla container çalıştırır, daha fazla log yazar ve daha fazla sayfa önbelleğine ihtiyaç duyar.

2 GB'lık bir plan, container'lar için 1280 MB alan bırakır. Bunun 512 MB'ını PostgreSQL'e, 128 MB'ını ise Traefik'e ayırırsanız, yarısı zaten tükenmiş olur. Geriye kalan kısım, her biri yaklaşık 256 MB olan iki küçük uygulamaya yeter. Bu, gerçek ve işlevsel bir sunucudur; ancak Nextcloud ve üzerine bir arama kümesi (search cluster) kurmak için yeterli alan değildir.

4 GB'lık bir plan 3072 MB alan bırakır; bu miktar bir veritabanı, bir reverse proxy, üç uygulama ve bir izleme (monitoring) container'ının aynı anda sığabileceği alandır. Bu, önemsediğiniz işler için kullanmaya değer en küçük boyuttur, çünkü boşta kalan bellek hatalı bir dağıtımı (deploy) tolere etmenizi sağlar.

8 GB'lık bir plan, 8192 MB'lık toplam kapasitesinden 6656 MB alan bırakır ve bu noktada sınır genellikle bellekten CPU'ya veya disk verimliliğine kayar. Bir container sınıfı, yükten ziyade yapılandırmasına göre bellek tüketir: yerel bir model sunucusu, KV önbelleğini bağlam penceresiyle (context window) orantılı olarak rezerve eder; bu nedenle Ollama'nın num_ctx değerini artırmak, tek bir istek gelmeden önce bütçeye gigabaytlarca yük ekleyebilir. Eğer hesaplamalarınız stack'inizin sığmadığını gösteriyorsa, ince ayar yapmak yerine daha büyük bir plana geçin: bir VPS'in gerçek maliyeti, fazladan gigabaytların aylık bazda neye denk geldiğini açıklar.

Hesaplamaların tutarlı kalması için iki kurala uyun. Her servise bir bellek sınırı koyun; böylece kontrolden çıkan bir süreç tüm sunucuyu çökertemez. Ayrıca bütçenizin en üst kısmını harcamadan bırakın, çünkü docker compose build ve pg_dump en kötü anlarda belleğe ihtiyaç duyar. Docker Compose'da bellek sınırları, gerekli sözdizimini ve dikkat edilmesi gereken noktaları içerir.

Kapsayıcım neden 137 hata koduyla sonlanıyor?

Çünkü çekirdek (kernel) onu sonlandırdı. 137 değeri, 128 artı 9'dur ve 9 numaralı sinyal SIGKILL sinyalidir. Kapsayıcı, kendisine tanınan miktardan daha fazla bellek talep etti ve out of memory (OOM) killer tarafından sonlandırıldı.

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

Tahmin etmek yerine nedeni doğrulayın:

docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory"

"OOMKilled": true ifadesi, kapsayıcının kendi cgroup sınırına ulaştığını gösterir ve çekirdek günlüğü, seçilen süreci ismen belirtir:

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

Bu iyi senaryodur, çünkü hasar tek bir kapsayıcı ile sınırlı kalmıştır. Kötü senaryo ise hiçbir sınırı olmayan bir kapsayıcıdır. Bir sınır olmadığında kapsayıcının tavanı tüm makinedir; bu nedenle bir servisteki bellek sızıntısı ana makineyi kaynak açlığına sürükler ve çekirdek, tüm sistem genelinde boyuta göre bir kurban seçer. Günlük satırı Memory cgroup önekini kaybeder ve Out of memory: Killed process 2417 (postgres) şeklinde görünür. Çekirdeğin seçtiği süreç genellikle veritabanınız olurken, sızıntıya neden olan kapsayıcı çalışmaya devam eder. Bu yüzden her servise bir sınır koymak, herhangi bir sınırın tam değerinden daha önemlidir.

Swap kullanımı zamanlamayı değiştirir, aritmetiği değil. Çoğu VPS imajı swap olmadan gelir. swapon --show komutuyla kontrol edin; swap yoksa hiçbir çıktı vermez. Bir swap dosyası, çekirdeğe soğuk sayfaları taşıyabileceği bir alan sağlar; bu da sorunu fark etmeniz için size dakikalar kazandırır.

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

free -h komutu artık Swap satırında sıfır olmayan bir toplam göstermelidir. Swap, RAM eklemez. Sürekli bellek baskısı altındaki bir sunucu, SSH ile bağlanıp düzeltme yapamayacağınız kadar yavaşlar; bu nedenle swap alanını bir alarm tamponu olarak görün ve boyutlandırmayı düzeltin.

UFW neden yayınladığım Docker portunu engellemiyor?

Trafik, UFW'nin koruduğu zincire asla ulaşmadığı için engelleme gerçekleşmez. Bir portu -p 5432:5432 ile veya bir compose ports: girdisiyle yayınladığınızda, daemon nat tablosuna bir DNAT (hedef ağ adresi çevirisi) kuralı ve kendi DOCKER zincirine bir kabul kuralı yazar. Bir konteynere yönelik paket, ana makineye teslim edilmek yerine o konteynere iletilir; bu nedenle paket FORWARD yolunda işlenir ve UFW'nin yazdığı INPUT kurallarından asla geçmez.

Bunun sunucuda gerçekleştiğini şu şekilde izleyebilirsiniz:

sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432

UFW, 5432 DENY IN Anywhere çıktısını verebilir ancak nat tablosu aynı port için bir DNAT tcp ... to:172.18.0.2:5432 kuralı tutuyor olabilir. Başka bir makineden nc -vz your.server.ip 5432 komutu hala bağlantı kurar. Veritabanı herkese açık internet üzerindedir ancak güvenlik duvarı kapalı olduğunu söyler.

Çözüm, daha az port yayınlamaktır. Bir compose projesindeki konteynerler aynı ağı paylaşır ve birbirlerine servis adıyla ulaşırlar; bu nedenle yalnızca yanındaki uygulamaya hizmet veren bir veritabanının herhangi bir ports: girdisine ihtiyacı yoktur. Yerel erişim istediğinizde, yayını loopback arayüzüne bağlayın:

services:
  db:
    image: postgres:16
    ports:
      - "127.0.0.1:5432:5432"

docker compose up -d işleminden sonra, dışarıdan gelen nc -vz your.server.ip 5432 başarısız olur ve sunucu üzerindeki psql -h 127.0.0.1 -p 5432 çalışmaya devam eder. Sağlıklı ve küçük bir yığında yalnızca reverse proxy 80 ve 443 portlarında yayın yapar. Docker yayınlanan portları neden UFW'yi atlar konusu, bir portu yayınlamanız gereken ancak yine de filtrelemeniz gereken durumlar için DOCKER-USER zincirini ele alır; UFW güvenlik duvarı temelleri ise alt taraftaki ana makine kurallarını kapsar.

Yeniden başlatma sonrasında container'larım neden kayboluyor?

Çünkü onları geri getirecek bir komut bulunmuyor. Bir container, siz özellikle belirtmediğiniz sürece no yeniden başlatma politikasıyla oluşturulur; bu nedenle yeniden başlatma sonrasında container durdurulmuş halde kalır ve daemon bununla ilgilenmez. VPS üzerinde yeniden başlatmalar nadir değildir: unattended-upgrades kaynaklı çekirdek güncellemeleri, sağlayıcı bakımları ve yukarıda bahsedilen OOM süreci, bunların hepsinin yeniden başlatmayla sonuçlanmasına neden olur.

İki koşulun sağlanması gerekir. Daemon'ın açılışta başlaması şarttır:

systemctl is-enabled docker

Bu komut, standart bir Ubuntu kurulumunda enabled çıktısını verir. Ardından her servisin bir politikaya ihtiyacı vardır:

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

unless-stopped, yeniden başlatma sonrasında container'ı geri getirir ve sizin bilerek durdurduğunuz container'lara müdahale etmez. always ise daemon her yeniden başladığında, sizin kasıtlı olarak durdurduklarınızı da yeniden başlatır; bu durum hata ayıklama sırasında beklenmedik sonuçlar doğurabilir. Dosyayı düzenlemek tek başına yeterli değildir, çünkü yeniden başlatma politikası container oluşturulduğu anda belirlenir. Container'ın yeniden oluşturulması için docker compose up -d komutunu çalıştırın, ardından canlı değeri kontrol edin:

docker inspect my-app | grep -A3 RestartPolicy

Daha sonra sunucuyu bilerek yeniden başlatın ve proje dizininde docker compose ps komutunu çalıştırın. Planlı bir yeniden başlatmadan sağ çıkan bir stack, plansız olandan da sağ çıkar. Eğer stack'iniz bir sıralama garantisine veya açılışta tek seferlik bir işleme ihtiyaç duyuyorsa, systemd birimi daha iyi bir araçtır: Docker Compose'u açılışta başlatma rehberinde birim dosyası mevcuttur. Geri gelen bir container'ın gerçekten hizmet verip vermediğini anlamak için Compose sağlık kontrolleri ekleyin.

VPS diskim neden dolu?

Docker, siz aksini söyleyene kadar her şeyi saklar. Çektiğiniz her image etiketi, durdurulmuş her container, yeniden oluşturma işlemlerinden arta kalan her anonim volume ve build cache katmanlarının tamamı diskte kalır. Bu plan boyutlarında standart olan 40 GB veya 80 GB boyutundaki bir root dosya sisteminde, bu durum yıllar yerine aylar içinde bir kesintiye yol açar.

Disk doluluğu bir çökme gibi görünmez. Aynı saat içinde bir container'dan, no space left on device hatası, apt çıktısı, journald ve docker pull üzerinden hata mesajları alırsınız. PostgreSQL yazma işlemlerini kabul etmeyi durdurur. Sunucu hala ayaktadır, bu da durumu bir yeniden başlatma döngüsünden daha zor fark edilir kılar.

Silmeden önce kontrol edin:

docker system df
df -h /

docker system df, toplam alanı image'lar, container'lar, yerel volume'lar ve build cache olarak ayırır ve her birinin yanında GERİ KAZANILABİLİR (RECLAIMABLE) sütununu gösterir. Kendi image'larını oluşturan bir sunucuda, build cache genellikle en büyük alanı kaplayan kalemdir.

docker image prune -a
docker builder prune
docker system df

docker image prune -a, hiçbir container tarafından kullanılmayan tüm image'ları kaldırır. docker builder prune ise build cache'i temizler. Her iki işlem de servisler çalışırken güvenlidir çünkü kullanımda olan hiçbir veri silinmez. Güvenli olmayan işlem ise, şu anda hiçbir container tarafından referans verilmeyen tüm volume'ları silen docker system prune --volumes komutudur. Hafta sonu için durdurduğunuz bir stack tam olarak bu durumdadır ve veritabanı volume'u bu işlemle birlikte silinir. Bu bayrağı kullanmadan önce bind mount'lar ve named volume'lar arasındaki farklar konusunu okuyun ve mutlaka yedek alın.

Container logları daha sessiz bir büyüme gösterir. Varsayılan json-file sürücüsünün boyut sınırı yoktur; bu nedenle çok fazla çıktı üreten bir container, /var/lib/docker/containers dizinine gigabaytlarca veri yazabilir. Bunu /etc/docker/daemon.json dosyasında her container için sınırlandırın:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Bu ayarı, container'larınızı yeniden başlatan sudo systemctl restart docker komutu ile uygulayın, bu yüzden doğru zamanı seçin. Sınırlandırma, değişiklikten sonra oluşturulan container'lar için geçerli olur; bu nedenle çalışanları docker compose up -d --force-recreate ile yeniden oluşturun ve doğrulayın:

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

Inspect çıktısı, max-size değerinin ayarlandığını göstermelidir. Eğer boşsa, o container değişiklikten önce oluşturulmuştur ve hala sınırsız şekilde yazmaya devam ediyordur.

Küçük bir Docker sunucusunu sağlıklı tutma alışkanlıkları

Bunların hiçbiri bir kontrol paneli veya öğrenmeniz gereken bir araç gerektirmez.

  • Her ayın ilk günü docker system df ve df -h / komutlarını çalıştırın. İki komut, otuz saniye; bir kesintiye dönüşmeden çok önce eğilimi görmenizi sağlar.
  • Küçük olduğundan emin olduğunuz servisler dahil her servise bir bellek sınırı koyun. Bu sınır, sunucu genelindeki bir kesintiyi, sadece yeniden başlatılan bir container durumuna indirger.
  • Sunucuyu başka bir yerden izleyin; böylece çekirdek müdahale etmeden önce bellek veya disk baskısından haberdar olursunuz. Uptime Kuma, bir container içinde çalışır ve boşta yaklaşık 95 MB bellek tüketir.
  • Container'ları değil, volume'ları yedekleyin. Container atılabilir, ancak volume değildir. restic backups on a VPS rehberi, bir zamanlama oluşturmayı ve geri yükleme testi yapmayı kapsar.
  • Compose dosyasındaki image etiketlerini sabitleyin ve bunları seçtiğiniz bir günde güncelleyin. latest kullanıldığında, bir sonraki docker compose pull komutuyla alacağınız sürüm, o sabah yayınlanan sürüm neyse odur.

Docker çalıştıran küçük bir VPS, dört değer aralıkta kaldığı sürece yıllarca sağlıklı kalır: bellek bütçesi, yayınlanan portların listesi, her servisteki yeniden başlatma politikası ve boş disk alanı. Geri kalan her şey, evde zaten çalıştırdığınız Docker ile aynıdır.

FAQ

Bir VPS üzerinde Docker çalıştırmak için ne kadar RAM gerekir?

Docker'ın kendisi düşük kaynak tüketir. Daemon ve containerd süreçleri toplamda 100 MB civarında yer kaplar; geri kalan gereksinim ise çalıştırdığınız container'lara bağlıdır. Öncelikle ana makinenin payını ayırın: 2048 MB kapasiteli bir sunucuda işletim sistemi, daemon ve yedek pay için 768 MB ayırın; bu durumda container'lar için 1280 MB kalır. 512 MB değerinde bir veritabanı, 128 MB değerinde bir reverse proxy ve iki küçük uygulama bu alana sığabilir. Yayınlanan rakamlara güvenmek yerine kendi yığınınızı docker stats --no-stream ile ölçün.

1 GB RAM'li bir VPS üzerinde Docker çalıştırabilir miyim?

Evet, bir veya iki hafif container için çalıştırabilirsiniz; ancak başlamadan önce bir swap dosyası ekleyin. 1 GB'lık bir sunucunun yaklaşık yarısı işletim sistemi ve Docker daemon çalışmaya başladığında dolar. Bu, küçük bir uygulama ve bir reverse proxy için yer bırakır ancak gerçek yük altındaki bir veritabanı için yeterli değildir. Bu boyuttaki bir sunucuda image oluşturmak başarısız olur veya başka bir süreci sonlandırır; bu nedenle image'ları başka bir yerde oluşturup hazır olanı çekin.

UFW bir Docker container'ını korur mu?

Yayınladığınız portlar için korumaz. Docker kendi DNAT ve forward kurallarını yazar; bu nedenle yayınlanmış bir container portuna yönelen paket, ana makineye teslim edilmek yerine doğrudan container'a iletilir ve UFW'nin yönettiği INPUT kuralları bu trafiği görmez. O port internete açıkken ufw deny 5432 aktif olabilir. Portları 127.0.0.1:5432:5432 ile loopback adresine yayınlayın, dahili servisleri yayınlamayın veya DOCKER-USER zincirinde filtreleme yapın.

VPS yeniden başlatıldığında container'larım tekrar başlar mı?

Yalnızca bir yeniden başlatma politikası (restart policy) ile oluşturulmuşlarsa başlarlar. Her servis için restart: unless-stopped ayarını yapın, container'ların bu ayarla yeniden oluşturulması için docker compose up -d komutunu çalıştırın ve systemctl is-enabled docker çıktısının enabled olduğunu doğrulayın. Ardından sunucuyu kasten yeniden başlatın ve docker compose ps ile durumu kontrol edin. Test etmediğiniz bir yeniden başlatma politikası, geçerli bir politika değildir.

Docker image'larını ne sıklıkla temizlemeliyim?

Çoğu küçük sunucu için aylık temizlik yeterlidir veya docker system df komutu ihtiyaç duyduğunuz boş alanı raporladığında temizlik yapabilirsiniz. docker image prune -a ve docker builder prune komutları, kullanımda olan image'lar ve önbellek atlandığı için servisler çalışırken güvenle kullanılabilir. Hangi volume'lerin referans dışı kaldığından emin değilseniz docker system prune --volumes komutundan kaçının; çünkü bu komut durdurulmuş durumdaki herhangi bir yığının verilerini siler.