Kısa cevap: Sürekli çalışan ClamAV daemon’ı (clamd) virüs veritabanını RAM’e açar ve tek başına ~1 GB, veritabanı güncellemesi (reload) anında ~1.6–1.8 GB tutabilir. Az RAM’li ve swap’siz bir sunucuda bu sıçrama, kernel’in OOM killer’ını tetikler; en büyük tüketiciyi — yani clamd’ı ya da bazen Nginx/MariaDB’yi — öldürür ve site erişilemez hale gelir. Kalıcı çözüm üç adımdır: swap ekle, clamd’a systemd cgroup bellek limiti koy (MemoryMax + OOMPolicy=stop) ve ConcurrentDatabaseReload no ile reload sıçramasını kes.
Yönetimini yeni devraldığımız bir sunucuda, bir sabah site “DNS hatası” ile erişilemez hale geldi. Sunucu daha önce başkası tarafından swap’siz ve servis kaynak limitleri konmadan kurulmuştu. İlk bakışta DDoS gibi görünen tablo, aslında az RAM’li sunucularda çok sık yaşanan ama genellikle gözden kaçan bir soruna çıktı: kontrolsüz çalışan ClamAV daemon’ı. Bu yazı, sorunu nasıl teşhis edip kalıcı olarak çözdüğümüzü — özellikle 4 GB ve altı RAM’li sunucular için — adım adım anlatıyor. Sunucu AlmaLinux + Virtualmin, ancak mantık tüm modern systemd dağıtımlarında geçerli.
Belirti: Cloudflare arkasında “DNS hatası”
Kullanıcılar siteye girmeye çalıştığında DNS hatası alıyordu, ama sunucu hâlâ ayaktaydı. Cloudflare arkasındaki bir site için bu tablo genellikle tek bir anlama gelir: Cloudflare origin sunucuya ulaşamıyor. Yani sorun DNS’te değil, origin tarafında bir servisin düşmesinde. İlk kontrol de bunu doğruladı:

Mem: 3.5 Gi toplam → 3.1 Gi kullanımda → yalnızca 148 Mi boş (389 Mi available)Swap: 0 B — hiç swap yok
Sunucu 31 gündür açıktı ve belleği neredeyse doluydu. Swap’siz, belleği dolu bir sistem klasik bir OOM adayıdır: RAM tükendiğinde kernel’in başvurabileceği hiçbir tampon yoktur, doğrudan process öldürmeye geçer.
Teşhis: dmesg her şeyi söyledi
Tahmin yürütmek yerine kernel günlüğüne baktık. dmesg çıktısında, günlerce neredeyse her sabah aynı saat bandında tekrar eden kayıtlar vardı:
Out of memory: Killed process 1124725 (clamd) total-vm:1110092kB, anon-rss:987260kB ... UID:989
Out of memory: Killed process 685966 (clamd) total-vm:1842848kB, anon-rss:1642568kB ... UID:989
Out of memory: Killed process 2312039 (clamd) total-vm:2047836kB, anon-rss:1841544kB ... UID:989
Öldürülen process her seferinde clamd — yani sürekli çalışan ClamAV daemon’ı (UID 989, clamav kullanıcısı). Standalone clamscan değil; doğrudan daemon’ın kendisi. Bu ayrım önemli, çünkü çözümün doğru hedefe (servise) uygulanması buna bağlı.
anon-rss değerleri (gerçek fiziksel RAM kullanımı) net bir desen gösteriyordu:
- Normal hal: clamd ~990 MB tutuyor (anon-rss ≈ 987–993 MB).
- Reload anı: birkaç olayda kullanım 1.64–1.84 GB‘ye fırlamış (ör. anon-rss 1642568 kB, 1841544 kB).
Sebep şu: virüs veritabanı (main.cvd, daily.cvd, bytecode.cvd) RAM’e hash tabloları olarak açılır. Disk üzerinde küçük görünen bu dosyalar bellekte ~1 GB yer kaplar. Üstelik ConcurrentDatabaseReload (ClamAV’da varsayılan olarak açıktır), güncelleme anında eski ve yeni veritabanı aynı anda yüklendiği için kullanım geçici olarak neredeyse iki katına, ~1.6–1.8 GB’ye çıkar. Yani hiçbir ek ayar yapmasanız bile bu sıçramayı varsayılan davranış olarak yaşarsınız. OOM tam bu sıçrama anlarında tetikleniyordu: swap’siz, ~148 MB boş alanı olan sistemde reload sıçraması son damla oluyor, OOM killer en büyük tüketiciyi kesiyor, systemd yeniden başlatıyor ve döngü tekrar ediyordu.
Çözüm: üç adımda kalıcı düzeltme
1. Swap eklendi
Swap’siz çalışan bir sunucu, RAM dolduğunda doğrudan servis kayıplarına uğrar; kernel’in nefes alacağı bir tampon yoktur. 2 GB swap dosyası oluşturuldu ve vm.swappiness=10 ile kernel’in mümkün olduğunca RAM’i tercih edip swap’a yalnızca gerektiğinde geçmesi sağlandı (düşük swappiness, swap’a geçme eğilimini azaltır; tamamen engellemez).
Aşağıdaki komut yeni bir swap dosyası oluşturur. Sunucunuzda zaten swap varsa (swapon --show ile kontrol edin) bu adımı atlayın; aksi halde mevcut /swapfile üzerine yazma riski olur.
# Önce mevcut swap var mı?
swapon --show
# Yoksa 2 GB swap oluştur:
dd if=/dev/zero of=/swapfile bs=1M count=2048
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
echo 'vm.swappiness=10' >> /etc/sysctl.conf
sysctl -p
Asıl nefesi açan adım büyük ölçüde budur: swap, OOM killer’a alan verdiği için servisleri öldüren döngü kırıldı. Ama swap tek başına yeterli değil — clamd’ı bir sınır içine almak da gerekir.
2. systemd drop-in ile bellek limiti
systemd’nin cgroup mekanizması kullanılarak clamd@scan servisine bir bellek tavanı konuldu. Bu sayede clamd limiti aşarsa OOM killer’ın gelişigüzel başka bir servisi (Nginx, PHP-FPM, MariaDB) öldürmesi yerine, OOMPolicy=stop ile yalnızca clamd durur — sistemin geri kalanı ayakta kalır. Yani amaç clamd’ı hızlandırmak değil, izole etmek.
mkdir -p /etc/systemd/system/[email protected]/
cat > /etc/systemd/system/[email protected]/override.conf << 'EOF'
[Service]
MemoryMax=700M
MemorySwapMax=1G
OOMPolicy=stop
EOF
systemctl daemon-reload
systemctl restart clamd@scan
Buradaki sayılar ölçüme dayanıyor: bu sunucuda clamd çalışırken ~692 MB kullanıyordu, dolayısıyla MemoryMax=700M daemon’ı boğmayan ama net bir tavan koyan bir tabandır. MemorySwapMax=1G ise reload anındaki ~1.6 GB’lik geçici sıçramaya tampon sağlar; o fazlalık kısa süreliğine swap’e taşınır ve kill önlenir.
MemorySwapMax yalnızca cgroup v2 (birleşik hiyerarşi) altında çalışır; cgroup v1’de sessizce yok sayılır. OOMPolicy ise systemd 243+ ister. AlmaLinux/RHEL 9, Rocky 9 ve güncel Debian/Ubuntu bunları varsayılan olarak karşılar (bu vakadaki sunucu da öyle). Ama AlmaLinux/RHEL 8 varsayılan olarak cgroup v1 ve systemd 239’dur — orada MemorySwapMax etkisiz kalır ve reload sıçraması MemoryMax‘ı aşıp cgroup içinde OOM tetikler. Kontrol: stat -fc %T /sys/fs/cgroup çıktısı cgroup2fs ise v2’desiniz; systemctl --version ile de sürümü görün. v1’deyseniz ya cgroup v2’ye geçin ya da MemorySwapMax‘a güvenmeden MemoryMax‘ı reload tepe değerinin üzerine (ör. 2G) çekin.
Bu kombinasyon az RAM’li sunucular için tasarlandı. Rahat RAM’iniz varsa MemoryMax=2G koyup (clamd’ın reload tepe değerinin üzerinde) clamd’ı tamamen RAM’de çalıştırmak, swap’e hiç ihtiyaç duymadan yeterlidir; tarama hızı da etkilenmez.
3. ConcurrentDatabaseReload kapatıldı
Asıl OOM sıçramalarının kaynağı reload anındaki çift veritabanı yüküydü. /etc/clamd.d/scan.conf dosyasına tek satır eklemek bunu kesiyor:
ConcurrentDatabaseReload no
Bu davranış ClamAV’ın kendi dokümantasyonunda da açıkça belirtilir: özellik aktifken reload sırasında geçici olarak ikinci bir tarama motoru yüklenir (daha fazla RAM); no yapıldığında ise reload anında taramalar kısa süre bloke edilerek daha düşük RAM kullanımı elde edilir. Takas budur: reload sırasında clamd kısa bir süre tarama yapamaz. Düşük RAM’li bir sunucuda bu, RAM’in iki katına çıkıp OOM tetiklemesine kıyasla kabul edilebilir bir takastır. Bu tek ayar bile çoğu durumda krizin büyük kısmını çözer.
Sonuç: ölçülmüş fark

| Metrik | Önce | Sonra |
|---|---|---|
Boş RAM (free) |
148 MB | 303 MB |
Kullanılabilir RAM (available) |
389 MB | 846 MB |
| Swap | Yok | 2 GB aktif (~785 MB kullanımda) |
| clamd bellek kullanımı | ~990 MB normal, reload’da ~1.6 GB | ~692 MB, limit 700 MB + 1 GB swap tamponu |
| OOM kill | Günlerce, neredeyse her sabah | Düzeltme sonrası 0 |
| Site durumu | Erişilemez | Stabil |
Not: dmesg’deki son OOM kill, düzeltmenin uygulandığı sabahın hemen öncesine aitti. Düzeltme devreye girdikten sonra yeni bir kill kaydı oluşmadı.
Hangi sunucuda hangi reçete?
Bu vakanın asıl dersi tek bir sihirli sayı değil, sunucu profiline göre doğru yaklaşımdır:
- 4 GB ve altı RAM, swap’siz sunucular: Bu yazıdaki tam reçete — swap ekle,
MemoryMax≈700M+MemorySwapMax=1Gile clamd’ı sınırla,ConcurrentDatabaseReload noile reload sıçramasını kes. Az kaynakta ciddi bir ferahlama sağlar. - Daha bol RAM’li sunucular: Çoğu zaman daha basiti yeterli —
MemoryMax=2G(clamd’ın reload tepe değerinin üzerinde) veConcurrentDatabaseReload no. Swap’e gerek kalmaz, clamd tamamen RAM’de çalışır, tarama hızı da etkilenmez.
Özellikle kendi sunucusunda e-posta barındıran, gelen ekleri ClamAV ile tarayan sunucularda bu konfigürasyon kritik önem taşır — clamd zaten bu senaryoda sürekli açık durur. ClamAV gerekli bir bileşendir; ama kaynak limiti konmadan bırakılırsa tüm sistemi aşağı çekebilir. swap + cgroup izolasyonu (MemoryMax + OOMPolicy=stop) tam bu iş içindir: bir servisin tek başına sunucuyu çökertmesini engeller. Aynı disiplini bir sunucunun tüm katmanlarına yaymak istiyorsanız Virtualmin/Webmin sunucu sıkılaştırma rehberimize bakın; WordPress tarafında bellek ve önbellek ayarını PHP-FPM, OPcache ve Redis tuning, veritabanı belleğini ise MariaDB tuning yazısında ele aldık.
Sıkça sorulan sorular
clamd neden bu kadar çok RAM kullanıyor?
ClamAV daemon’ı başlarken virüs veritabanını (main, daily, bytecode) diskten okuyup RAM’e hash tabloları olarak açar. Disk üzerinde küçük görünen bu dosyalar bellekte ~1 GB yer kaplar. Bu, clamd’ın normal ve beklenen davranışıdır; tarama hızını sağlayan şey de budur.
ConcurrentDatabaseReload’u kapatmak güvenli mi?
Evet. Kapattığınızda tek fark, veritabanı güncellendiği kısa an boyunca taramaların bloke olmasıdır; güncelleme bitince normale döner. Karşılığında reload anındaki RAM kullanımının iki katına çıkması önlenir. Düşük RAM’li sunucularda bu takas neredeyse her zaman doğrudur.
MemoryMax=700M clamd’ı bozar mı?
Bozmaz, çünkü değer ölçüme dayanır: bu sunucuda clamd ~692 MB kullanıyordu, 700M net bir tavan koyar. Reload anındaki geçici sıçrama için MemorySwapMax=1G tampon sağlar. Kendi sunucunuzda önce systemctl status clamd@scan veya ps ile clamd’ın gerçek kullanımını ölçüp tavanı ona göre belirleyin.
Swap eklemek sunucuyu yavaşlatmaz mı?
vm.swappiness=10 ile kernel RAM’i tercih eder, swap’a yalnızca gerektiğinde geçer; yani gündelik performansı etkilemez. Swap burada bir “acil durum tamponu” görevi görür: RAM aniden dolduğunda servisleri öldüren OOM yerine, fazlalık kısa süreliğine diske taşınır.
ClamAV’ı tamamen kaldırsam olmaz mı?
Sunucu mail alıyorsa ClamAV gerçek bir güvenlik ihtiyacıdır; kaldırmak sorunu değil korumayı ortadan kaldırır. Doğru yaklaşım onu kaldırmak değil, kaynak sınırıyla (cgroup + swap) sistemin geri kalanından izole etmektir.
DKS pratikte ne yapıyor?
Az kaynaklı sunucularda “her servis eşit derecede tehlikeli değildir” — bir tanesi (burada clamd) tüm sistemi aşağı çekebilir. Biz bir sunucuyu devraldığımızda ClamAV, PHP-FPM ve MariaDB gibi bellek-aç servisleri baştan cgroup limitleriyle sınırlıyor, swap tamponunu standart kuruyor ve dmesg/OOM kayıtlarını izliyoruz. Bu sürekli izleme ve limitleme işini bakım hizmetimizde üstleniyoruz; sıfırdan doğru kurulum için ise sunucu kurulumu ve altyapı tarafında çalışıyoruz.
İlgili yazı
Aynı “systemd ile servis disiplini” temasında, Virtualmin’de --nofork/forking modu farkından kaynaklanan başka bir vakayı da inceledik: Usermin “Failed to open PID file” hatası ve çözümü.
Sitesi ara ara “erişilemez” olan, az RAM’li bir sunucunuz varsa sebebi çoğu zaman budur. Bizimle iletişime geçin; dmesg ve bellek profilini inceleyip kalıcı bir izolasyon planı çıkaralım.