VPS üzerinde Docker kullanırken dikkat edilmesi gerekenler
VPS üzerinde Docker çalıştırmanın yerel ortamdan farklarını öğrenin. RAM tükenmesi, UFW bypass sorunları, restart politikaları ve disk dolması gibi kritik riskleri inceleyin.
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 çalışma ortamıdır. Dizüstü bilgisayarlar boş belleğe, kimsenin taramadığı bir güvenlik duvarına ve doluluğunu asla kontrol etmediğiniz geniş bir diske sahiptir. Kiralık bir sunucu ise sabit bir bellek sınırına, açıldıktan dakikalar sonra taranmaya başlanan bir genel IP adresine ve Docker'ın sormadan dolduracağı bir kök dosya sistemine sahiptir.
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 (OOM 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 belirtmediğiniz 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 belirtir. Henüz bir compose dosyası yazmadıysanız, önce VPS üzerinde Docker Compose temelleri sayfasını okuyun ve ardından buraya geri dönün. Bu sayfa, bir stack'i nasıl ayağa kaldıracağınızı bildiğ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, bir cgroup (kontrol grubu) içindeki bir süreçtir; bu nedenle konuk çekirdeği 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 için, başlatıldıktan birkaç dakika sonra docker stats üzerinden okunan tipik boşta çalışma değerleridir. Bunlar, iş yükünüzün bir kıyaslaması değil, planlama için bir başlangıç noktasıdır. Herhangi bir rakama güvenmeden önce, bunlar dahil olmak üzere, kendi sunucunuzda docker stats --no-stream komutunu çalıştırın.
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 hizmet eder. idle_mb, container hiçbir işlem yapmazken kullanılan 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 devreye girdiğinde 512 MB talep eder. Planlamanızı 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ırın 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. Sunucunuzun 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 içeri aldığı sayfa önbelleğini (page cache) de kapsar; bu nedenle başlatma sonrasında bir süre yükselir ve ardından dengelenir. Bir şeyin bellek sızdırdığına karar vermeden önce durumu bir saat boyunca izleyin.
VPS boyutlandırma: 2 GB, 4 GB ve 8 GB RAM kapasitesine neler sığar?
Öncelikle ana bilgisayarın (host) payını toplam bellekten düşmelisiniz. Çekirdek, systemd, journald, sshd ve Docker daemon, container'larınızla aynı RAM'i kullanır ve dockerd ile containerd bu alanın yaklaşık 100 MB'ını kaplar. Ayrıca sayfa önbelleği (page cache) ve bir imaj derleme veya veritabanı yedeği alma sırasında oluşacak ani bellek artışları için boş belleğe ihtiyacınız vardır.
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 yük altında sunucunun yanıt verebilirliğini koruyan payı kapsar. Geriye kalan container_mb, harcayabileceğiniz tek miktardır. Bu yedek alan, plan büyüdükçe 768 MB'tan 1536 MB'a kadar artar; çü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, kapasitenin yarısı zaten tükenmiş olur. Geriye kalan kısım, her biri yaklaşık 256 MB olan iki küçük uygulama içindir. 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 herhangi bir iş için kullanılabilecek en küçük boyut budur, çünkü boşta kalan bellek hatalı bir dağıtımın (deploy) yaratacağı yükü absorbe eder.
8 GB'lık bir plan, toplam 8192 MB kapasitesinden 6656 MB alan bırakır ve bu noktada sınırlayıcı faktör genellikle bellekten CPU'ya veya disk verimliliğine kayar. 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 bölümü, 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 tek bir süreç tüm sunucuyu çökertemez. Ayrıca bütçenizin bir kısmını harcamadan bırakın, çünkü docker compose build ve pg_dump en kritik anlarda belleğe ihtiyaç duyar. Docker Compose içinde bellek sınırları bölümü, gerekli sözdizimini ve dikkat edilmesi gereken tuzakları içerir.
Konteynerim neden 137 koduyla sonlanıyor?
Çünkü çekirdek (kernel) onu sonlandırdı. 137 değeri, 128 artı 9'dur ve 9 numaralı sinyal SIGKILL sinyalidir. Konteyner, kendisine tanınan miktardan daha fazla bellek talep etti ve out of memory (OOM) killer süreci sonlandırdı.
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, konteynerin kendi cgroup limitine 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 konteynerle sınırlı kalmıştır. Kötü senaryo ise hiçbir limiti olmayan bir konteynerdir. Limit olmadığında tavan değer 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 boyutuna 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. Seçilen süreç genellikle veritabanınız olurken, sızıntıya neden olan konteyner çalışmaya devam eder. İşte bu yüzden her servise limit koymak, tek bir limitin tam değerinden daha önemlidir.
Swap kullanımı aritmetiği değil, zamanlamayı değiştirir. Çoğu VPS imajı swap olmadan gelir. swapon --show ile kontrol edin; swap yoksa hiçbir çıktı vermeyecektir. 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 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'ı bir alarm tamponu olarak görün ve boyutlandırmayı düzeltin.
UFW neden Docker ile dışarı açtığım portu engellemiyor?
Trafik, UFW'nin denetlediği zincire asla ulaşmadığı için bu durum yaşanır. Bir portu -p 5432:5432 veya bir compose ports: girdisi ile dışarı açtığınızda, daemon nat tablosuna bir DNAT (hedef ağ adresi çevirisi) kuralı ve kendi DOCKER zincirine bir kabul (accept) kuralı yazar. Bir container'a yönlendirilen paket, ana makineye teslim edilmek yerine doğrudan o container'a iletilir; bu nedenle paket FORWARD yolunda işlenir ve UFW'nin yazdığı INPUT kurallarından asla geçmez.
Bunun sunucuda nasıl 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ı tutmaya devam eder. Başka bir makineden yapılan nc -vz your.server.ip 5432 bağlantısı hala başarılı olur. Veritabanı herkese açık internet üzerindedir ancak güvenlik duvarı kapalı olduğunu belirtir.
Çözüm, daha az portu dışarı açmaktır. Aynı compose projesindeki container'lar bir ağı paylaşır ve birbirlerine servis adları üzerinden 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 ise, portu 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 yapılan nc -vz your.server.ip 5432 bağlantısı başarısız olur, ancak makine üzerindeki psql -h 127.0.0.1 -p 5432 bağlantısı çalışmaya devam eder. Sağlıklı ve küçük bir yığında yalnızca reverse proxy 80 ve 443 portları üzerinden dışarıya açılır. Docker ile dışarı açılan portlar neden UFW'yi atlar konusu, bir portu dışarı açmanız gereken ancak yine de filtrelemeniz gereken durumlar için DOCKER-USER zincirini açıklar; UFW güvenlik duvarı temelleri ise altta yatan ana makine kurallarını ele alır.
Yeniden başlatma sonrasında container'larım neden kayboldu?
Çünkü sistem onlara geri dönmelerini söylemedi. Bir container, siz bir politika belirlemediğiniz sürece no varsayılanı ile 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, her biri yeniden başlatma ile sonuçlanır.
İ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 bir container'a müdahale etmez. always ise daemon her yeniden başladığında, sizin kasıtlı olarak durdurduklarınız dahil tüm container'ları yeniden başlatır; bu durum hata ayıklama sırasında sürprizlere yol açabilir. 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ğ çıkacaktır. Eğer stack'iniz bir sıralama garantisine veya açılışta tek seferlik bir işleme ihtiyaç duyuyorsa, systemd unit daha iyi bir araçtır: Docker Compose'u açılışta başlatma bağlantısında gerekli unit 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 içinde değil, 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, apt üzerinden, journald'den ve docker pull'dan hata 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; her birinin yanında GERİ KAZANILABİLİR (RECLAIMABLE) sütunu bulunur. Kendi image'larını oluşturan 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 iki işlem de servisler çalışırken güvenlidir, çünkü kullanımda olan hiçbir şey silinmez. Güvenli olmayan işlem ise docker system prune --volumes komutudur; bu komut, şu anda hiçbir container tarafından referans verilmeyen tüm volume'ları siler. Hafta sonu için durdurduğunuz bir stack tam olarak bu durumdadır ve veritabanı volume'u da onunla birlikte silinir. Bu bayrağı kullanmadan önce bind mount'lar ve adlandırılmış volume'lar konusunu okuyun ve önce bir yedek alın.
Container logları daha sessiz bir büyüme kaynağıdır. 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. Bunu /etc/docker/daemon.json içinde her container için sınırlandırın:
{
"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 zamanlamayı iyi 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 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 dfvedf -h /komutlarını çalıştırın. İki komut, otuz saniye; bir kesintiye dönüşmeden çok önce eğilimi görürsünüz. - Küçük olduğundan emin olduklarınız dahil her servise bir bellek sınırı koyun. Bu sınır, sunucu genelindeki bir kesintiyi, yeniden başlatılan tek bir container sorununa dönüştürür.
- 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. VPS üzerinde restic yedekleri, bir zamanlama ve geri yükleme testini kapsar.
- Compose dosyasındaki image etiketlerini sabitleyin ve bunları seçtiğiniz bir günde güncelleyin.
latestile bir sonrakidocker compose pullkomutunda alacağınız sürüm, o sabah yayınlanan herhangi bir sürüm olacaktır.
Docker çalıştıran küçük bir VPS, dört değer aralık içinde kaldığında 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. İlk olarak 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 geriye 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 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 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. ufw deny 5432 aktif olsa bile ilgili port internetten erişilebilir kalır. Portları 127.0.0.1:5432:5432 ile loopback adresine yayınlayın, dahili servisleri yayınlamayın veya DOCKER-USER zinciri üzerinden 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 yapın. docker image prune -a ve docker builder prune komutları, kullanımda olan image ve önbellekleri atladığı için servisler çalışırken güvenle kullanılabilir. Hangi volume'lerin 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 verilerini siler.