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.
Bu grafiğin arkasındaki veriler
[
{
"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ırmanız gerekir. Kernel, systemd, journald, sshd ve Docker daemon, container'larınızın çalıştığı aynı RAM'i kullanır; dockerd ve containerd bu alanın yaklaşık 100 MB'lık kısmı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.
Bu grafiğin arkasındaki veriler
[
{
"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 sürecini ve sunucunun yük altında yanıt verebilir kalmasını sağlayan payı kapsar. Geriye kalan container_mb miktarı, harcayabileceğiniz tek değerdir. 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. Bunun nedeni, daha büyük bir sunucunun daha fazla container çalıştırması, daha fazla log yazması ve daha fazla sayfa önbelleğine ihtiyaç duymasıdır.
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, kapasitenin yarısı zaten dolmuş olur. Geriye kalan kısım, yaklaşık 256 MB'lık iki küçük uygulama için yeterlidir. Bu, gerçek ve kullanışlı 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 kapasite bir veritabanı, bir reverse proxy, üç uygulama ve bir izleme (monitoring) container'ının aynı anda çalışması için yeterlidir. Önem verdiğiniz projeler için kullanılabilecek en küçük boyut budur, çünkü boşta kalan bellek hatalı bir dağıtımın (bad deploy) etkilerini absorbe eder.
8 GB'lık bir plan, 8192 MB'lık toplam kapasitesinden 6656 MB alan bırakır. Bu seviyede sınırlayıcı faktör genellikle bellek değil, CPU veya disk verimliliği olur. SSD Nodes daha küçük bir plan sunmamaktadır; en küçük planları 8 GB RAM ile başlar (fiyatlandırma sayfası 2026-10-11 tarihinde kontrol edilmiştir). Dolayısıyla bu sağlayıcı için geçerli olan satır budur; 2 GB ve 4 GB satırları, diğer sağlayıcılardan alınan daha küçük sunucuları tanımlar. Bir container sınıfı, yükten ziyade kendi yapılandırmasına göre bellek ayırır: 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, henüz tek bir istek gelmeden bütçeye gigabaytlarca yük ekleyebilir. Eğer hesaplamalarınız yığınınızın sığmadığını gösteriyorsa, ince ayar yapmak yerine daha büyük bir plana geçin: bir VPS'in gerçek maliyeti başlığı, 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 kritik anlarda belleğe ihtiyaç duyar. Docker Compose'da bellek sınırları başlığı, gerekli sözdizimini ve dikkat edilmesi gereken tuzakları 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 agoTahmin 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:1284416kBBu 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 -hfree -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 5432UFW, 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 dockerBu 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-stoppedunless-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 RestartPolicyDaha 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 sonrası geride kalan her anonim volume ve build cache'in her katmanı diskte kalmaya devam eder. Daha büyük bir disk yalnızca zaman kazandırır. SSD Nodes'un en küçük planındaki 160 GB SSD bile (fiyatlandırma sayfası 2026-10-11 tarihinde kontrol edilmiştir), her hafta image çeken ve build eden bir sunucuda aynı şekilde dolar; daha küçük bir root dosya sistemi ise bu durumun daha erken bir kesintiye yol açmasına neden olur.
Dolu bir disk, bir çökme gibi görünmez. Aynı saat içinde bir container'dan, apt'den, journald'den ve docker pull'ten no space left on device hatası alırsınız. PostgreSQL yazma işlemlerini 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ı build eden bir sunucuda, build cache genellikle en büyük satırdır.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a, hiçbir container tarafından kullanılmayan tüm image'ları kaldırır. docker builder prune, build cache'i temizler. Her ikisi de servisler çalışırken güvenlidir, çünkü kullanımda olan hiçbir şey silinmez. Güvenli olmayan komut docker system prune --volumes'dir. Bu komut, durdurulmuş container'ları ve ardından hiçbir container'ın kullanmadığı tüm anonim volume'ları kaldırır. Hafta sonu için durdurduğunuz bir stack tam olarak bu durumdadır; eğer veritabanı verilerini anonim bir volume'da tutuyorsa (bir image volume tanımladığında ve compose dosyanız oraya hiçbir şey mount etmediğinde Docker'ın oluşturduğu yapı budur), bu veriler silinir. İsimlendirilmiş volume'lar bu komuttan etkilenmez, ancak docker volume prune --all kullanılmayan isimlendirilmiş volume'ları da kaldırır. Herhangi bir flag kullanmadan önce bind mount'lar ile isimlendirilmiş volume'lar konusunu okuyun ve önce 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 içine gigabaytlarca veri yazar. Her container için /etc/docker/daemon.json dosyasında bir sınır belirleyin:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Bunu, container'larınızı yeniden başlatan sudo systemctl restart docker ile uygulayın, bu yüzden doğru zamanı seçin. Sınır, değişiklikten sonra oluşturulan container'lar için geçerlidir; 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/containersInspect çıktısı max-size ayarının yapıldığını göstermelidir. Eğer boşsa, o container değişiklikten önce oluşturulmuştur ve hala sınırsız bir ş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 dfvedf -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.
latestkullanıldığında, bir sonrakidocker compose pullkomutuyla 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 yaklaşık 100 MB 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 pay bırakmak için 768 MB ayırın; bu durumda container'lar için 1280 MB alan 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 herhangi bir rakama 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 da 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 imaj oluşturmak başarısız olur veya başka bir süreci sonlandırır; bu nedenle imajları başka bir yerde oluşturun ve hazır imajı çekin.
UFW bir Docker container'ını korur mu?
Yayınladığınız portlar için korumaz. Docker kendi DNAT ve yönlendirme kurallarını yazar; bu nedenle yayınlanan bir container portuna gelen paket, ana makineye iletilmek yerine doğrudan container'a yönlendirilir 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 çalışır mı?
Yalnızca bir yeniden başlatma politikası (restart policy) ile oluşturulmuşlarsa çalışırlar. 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 komutunun enabled çıktısını verdiğini doğrulayın. Ardından sunucuyu bilerek yeniden başlatın ve docker compose ps ile kontrol edin. Test etmediğiniz bir yeniden başlatma politikası, geçerli bir politika değildir.
Docker imajları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 imajlar ve önbellek atlandığı için servisler çalışırken güvenle kullanılabilir. Hangi birimlerin (volumes) referanssız olduğunu tam olarak bilmediğiniz sürece docker system prune --volumes komutundan kaçının; çünkü bu komut, o sırada durdurulmuş olan herhangi bir yığının anonim birimlerini siler ve docker volume prune --all komutu da kullanılmayan adlandırılmış birimleri kaldırır.