Sunucunuzda bir uygulama aniden No space left on device hatası vermeye başladı, ama df -h çıktısında diskin yarısı boş görünüyor. Bu çelişkili tablo çoğu zaman tek bir şeye işaret eder: disk alanı değil, inode tükenmiştir. Dosya sistemi yeni bir dosya için yer bulsa bile o dosyayı kaydedecek boş bir inode bulamadığında aynı hata mesajını döndürür.

Bu rehberde inode kavramını, inode dolduğunu nasıl kanıtlayacağınızı, milyonlarca küçük dosyanın hangi dizinde biriktiğini nasıl bulacağınızı ve PHP oturum dosyaları, mail kuyruğu, önbellek ve Docker katmanları gibi tipik suçluları güvenle nasıl temizleyeceğinizi adım adım anlatıyoruz. Diskin gerçekten dolduğu (bayt olarak) durumlar için disk doldu hatası rehberine bakabilirsiniz.

Inode Nedir ve Neden Biter?

Linux dosya sistemlerinde her dosya ve dizin, içeriğinden ayrı olarak bir inode kaydına sahiptir. Inode; dosyanın sahibini, izinlerini, zaman damgalarını ve verinin diskte nerede durduğunu tutar. Dosya adı ise yalnızca dizin içindeki bir etikettir ve bir inode numarasını gösterir. Sonuç olarak 1 baytlık bir dosya da, 10 GB'lık bir dosya da tam olarak bir inode harcar.

Ubuntu sunucularda varsayılan dosya sistemi genellikle ext4'tür. ext4'te inode sayısı dosya sistemi oluşturulurken belirlenir. mke2fs belgesi, bayt başına inode oranının (-i) dosya sistemi oluşturulduktan sonra değiştirilemeyeceğini açıkça belirtir; aynı belge, dosya sistemi büyütüldüğünde inode sayısının bu orana göre arttığını da söyler. XFS'te ise durum farklıdır: inode'lara ayrılabilecek alanın üst sınırı maxpct ile yüzde olarak tanımlanır, bu yüzden ext4'teki kadar katı bir sınırla daha nadir karşılaşılır.

Pratikte inode, şu tür iş yüklerinde biter: süresi dolmuş ama silinmemiş milyonlarca oturum dosyası, her sayfa için ayrı dosya üreten önbellek eklentileri, teslim edilemeyen e-postalarla şişen mail kuyruğu, temizlenmeyen geçici dosyalar ve eski Docker imajlarının katmanları.

Adım 1: Inode Dolduğunu Doğrulayın

Adım 1: Inode Dolduğunu Doğrulayın: Inodes / itotal, IUsed / iused, IFree / iavail, IUse% / ipcent

df komutunun -i seçeneği, blok kullanımı yerine inode bilgisini listeler. Önce iki çıktıyı yan yana karşılaştırın:

bash
# Bayt olarak doluluk
df -h

# Inode olarak doluluk
df -i

# Yalnızca kök dosya sistemi, okunabilir sütunlarla
df --output=source,fstype,itotal,iused,iavail,ipcent,target /
SütunAnlamı
Inodes / itotalDosya sistemindeki toplam inode sayısı
IUsed / iusedKullanılan inode sayısı (dosya + dizin + bağlantı)
IFree / iavailBoş inode sayısı
IUse% / ipcentInode doluluk yüzdesi; %100 ise yeni dosya oluşturulamaz

df -h çıktısında Use% düşük, df -i çıktısında IUse% %100 veya ona çok yakınsa teşhis kesinleşmiştir. Hangi bağlama noktasının (örneğin / veya ayrı bir /var) dolduğuna dikkat edin; aramayı o dosya sistemiyle sınırlayacaksınız.

Adım 2: Çok Dosya İçeren Dizinleri Bulun

GNU du aracının --inodes seçeneği, boyut yerine inode kullanımını listeler. -x diğer dosya sistemlerine geçmeyi engeller, -d ise derinliği sınırlar. Kökten başlayıp en kalabalık dizine doğru inin:

bash
# Kök dosya sisteminde birinci seviye dizinlerin inode sayısı
sudo du --inodes -x -d 1 / 2>/dev/null | sort -n | tail -15

# En kalabalık çıkan dizinde bir seviye daha inin (örnek: /var)
sudo du --inodes -x -d 1 /var 2>/dev/null | sort -n | tail -15

Alternatif olarak find ile dosyaların bulunduğu dizinleri sayabilirsiniz. -printf '%h\n' her dosyanın üst dizinini yazdırır; ardından bu satırları sayarsınız. Bu yöntem, dosyaların tek bir dizinde mi yoksa derin bir ağaçta mı biriktiğini hemen gösterir:

bash
# /var altında en çok dosya içeren 20 dizin
sudo find /var -xdev -type f -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -n | tail -20

Tipik Suçlular ve Güvenli Temizlik

Tipik Suçlular ve Güvenli Temizlik: 1. PHP oturum dosyaları, 2. Mail kuyruğu (Postfix), 3. Önbellek ve geçici dosya dizinleri, 4. Docker imajları ve build önbelleği

1. PHP oturum dosyaları

PHP'nin varsayılan files oturum işleyicisi her oturum için ayrı bir sess_* dosyası oluşturur. PHP belgesine göre çöp toplama (GC), session.gc_probability / session.gc_divisor olasılığıyla oturum başlatılırken tetiklenir; varsayılan değerler 1/100 ve session.gc_maxlifetime için 1440 saniyedir. Ubuntu'nun php-common paketi ise oturum temizliği için ayrıca /usr/lib/php/sessionclean betiğini, phpsessionclean.timer birimini ve /etc/cron.d/php dosyasını kurar. Bu zamanlayıcı çalışmıyorsa veya uygulama oturumları paketin bilmediği özel bir dizine yazıyorsa dosyalar birikir.

bash
# Oturum dizinini öğrenin (FPM ve CLI ayarları farklı olabilir)
php -i | grep -i 'session.save_path'
grep -R 'session.save_path' /etc/php/*/fpm/ 2>/dev/null

# Ubuntu'nun temizlik zamanlayıcısı çalışıyor mu?
systemctl list-timers | grep -i phpsessionclean
systemctl status phpsessionclean.timer

# Dizindeki dosya sayısı (yolu kendi sunucunuzdakiyle değiştirin)
sudo find /var/lib/php/sessions -type f | wc -l

Ubuntu paketlerinde oturum dizini çoğunlukla /var/lib/php/sessions olur, ancak yukarıdaki komutla kendi sunucunuzdaki değeri mutlaka doğrulayın. Uzun süredir değiştirilmemiş oturum dosyalarını silmek için find kullanın; rm * milyonlarca dosyada Argument list too long hatası verir:

bash
# Önce ne silineceğini sayın: 1 günden eski oturum dosyaları
sudo find /var/lib/php/sessions -xdev -type f -name 'sess_*' -mmin +1440 | wc -l

# Emin olduktan sonra silin
sudo find /var/lib/php/sessions -xdev -type f -name 'sess_*' -mmin +1440 -delete

Kalıcı çözüm için uygulama özel bir session.save_path kullanıyorsa ya o dizini temizleyen bir zamanlanmış görev ekleyin ya da oturumları Redis veya veritabanı gibi bir arka uca taşıyın. PHP belgesi, save_path içinde alt dizin seviyesi (N;/yol) kullanıldığında otomatik çöp toplamanın yapılmayacağını da not eder. PHP-FPM havuz ayarları için PHP-FPM yapılandırma rehberine, zamanlanmış görev yazmak için cron job rehberine bakabilirsiniz.

2. Mail kuyruğu (Postfix)

Sunucudaki bir form, cron görevi veya ele geçirilmiş bir betik teslim edilemeyen binlerce e-posta üretirse, her ileti kuyrukta ayrı dosya olarak durur. Postfix kuyruğunun yerini postconf ile öğrenebilir, içeriğini postqueue -p (geleneksel mailq karşılığı) ile listeleyebilirsiniz:

bash
# Kuyruk dizini
postconf queue_directory

# Kuyruğu listele (son satır toplam ileti sayısını verir)
sudo postqueue -p | tail -1

# Alt kuyruklardaki dosya sayısı
sudo sh -c 'cd "$(postconf -h queue_directory)" && for q in incoming active deferred hold; do echo "$q $(find $q -type f | wc -l)"; done'

Önce iletileri kimin ürettiğini bulun; aksi halde temizlik birkaç saat sonra tekrar gerekir. Ertelenmiş (deferred) kuyruktaki tüm iletileri silmek için postsuper belgesinde örneklenen komutu kullanın. Güvenlik önlemi olarak ALL büyük harfle yazılmalıdır:

bash
# Yalnızca ertelenmiş kuyruğu boşalt
sudo postsuper -d ALL deferred

3. Önbellek ve geçici dosya dizinleri

Sayfa önbelleği eklentileri, görsel küçültme önbellekleri, framework görünüm/oturum önbellekleri, /tmp ve /var/tmp altındaki artıklar çok sayıda küçük dosya üretir. Bu dizinlerin çoğu uygulama tarafından yeniden oluşturulabilir, ancak hangi dizinin güvenle silinebileceğini uygulamanın kendi belgesinden kontrol edin.

bash
# Belirli bir önbellek dizininde 7 günden eski dosyaları say, sonra sil
sudo find /var/www/site/cache -xdev -type f -mtime +7 | wc -l
sudo find /var/www/site/cache -xdev -type f -mtime +7 -delete

# Boş kalan alt dizinleri de kaldır
sudo find /var/www/site/cache -xdev -mindepth 1 -type d -empty -delete

Laravel gibi framework'lerde dosya tabanlı oturum ve görünüm önbelleği storage/framework altında tutulur; bu tür uygulamaları sunucuya taşırken dikkat edilecekler için Laravel VPS deploy rehberine bakın.

4. Docker imajları ve build önbelleği

Docker kullanıyorsanız, kullanılmayan imaj katmanları, durdurulmuş konteynerler ve build önbelleği /var/lib/docker altında çok sayıda dosya tutar. Önce neyin ne kadar yer kapladığına bakın, ardından yalnızca kullanılmayan nesneleri temizleyin:

bash
# Docker disk kullanımı özeti
docker system df

# Durdurulmuş konteynerler, kullanılmayan ağlar, sarkan imajlar ve build önbelleği
docker system prune

# Hiçbir konteynerin kullanmadığı tüm imajlar (dikkatli kullanın)
docker image prune -a

5. Silinmiş ama hâlâ açık dosyalar

Bir süreç tarafından açık tutulan dosya silindiğinde dizin girişi kaybolur, ancak inode ve veri blokları süreç dosyayı kapatana kadar serbest kalmaz. Bu durum daha çok bayt cinsinden doluluğu etkiler, ama nedenini anlamak için iyi bir kontroldür:

bash
# Bağlantı sayısı sıfır olan (silinmiş) ama açık dosyalar
sudo lsof +L1

Çıktıdaki süreci yeniden başlattığınızda alan ve inode serbest kalır. Servisleri güvenle yeniden başlatmak için systemd servis yönetimi yazısına bakın.

Adım 3: Sonucu Doğrulayın

Temizlikten sonra df -i komutunu tekrar çalıştırın ve IUse% değerinin düştüğünü görün. Ardından hata veren servisi yeniden başlatıp uygulama loglarını kontrol edin; bazı servisler disk yazamadığı sırada bozuk geçici dosya bırakmış olabilir.

bash
df -i /
sudo systemctl restart php8.3-fpm   # örnek: kendi servisinizle değiştirin
sudo journalctl -p err --since "15 min ago"

Tekrarı Nasıl Önlersiniz?

  • İzleme kurun: Disk doluluğunun yanında inode doluluğunu da izleyin ve örneğin %80 üzerinde uyarı alın. Temel kurulum için sunucu izleme temelleri ve Prometheus ve Grafana yazılarına bakın.
  • Temizlik görevlerini otomatikleştirin: Uygulamanın kendi ürettiği oturum ve önbellek dosyaları için find ... -mtime +N -delete çalıştıran bir cron veya systemd timer ekleyin.
  • Logları döndürün: Tarih damgalı log dosyalarını sonsuza dek biriktiren uygulamalar için logrotate yapılandırmasını kurun.
  • Küçük dosya üreten yapıları değiştirin: Oturumları Redis veya veritabanına, sayfa önbelleğini bellek tabanlı bir çözüme taşımak inode baskısını ortadan kaldırır.
  • Kapasiteyi planlayın: ext4'te inode oranı sonradan değiştirilemez; disk büyütüldüğünde inode sayısı da artar. Çok sayıda küçük dosya saklayacak bir iş yükü için ayrı bir disk planlıyorsanız mke2fs belgesindeki -i ve -N seçeneklerini önceden inceleyin.

Sık Sorulan Sorular

Disk boş görünüyor ama "No space left on device" alıyorum, neden?

En sık neden inode tükenmesidir; df -i ile kontrol edin. IUse% normalse, ayrı bir bağlama noktasının (örneğin /tmp veya bir Docker volume'ü) dolu olup olmadığına ve silinmiş ama açık dosyalara (lsof +L1) bakın.

Inode sayısını sonradan artırabilir miyim?

ext4'te bayt başına inode oranı oluşturulduktan sonra değiştirilemez. Ancak disk (ve dosya sistemi) büyütüldüğünde inode sayısı bu orana göre artar. Diğer seçenek, verileri yedekleyip dosya sistemini uygun parametrelerle yeniden oluşturmaktır; bu, veri kaybı riski taşıyan bir işlemdir.

rm ile silmeye çalışınca "Argument list too long" hatası alıyorum.

Kabuk, * ifadesini milyonlarca dosya adına genişletip komut satırı sınırını aşar. Bunun yerine find /yol -type f -delete kullanın; dosyaları tek tek işler ve sınıra takılmaz.

Inode dolması load average'ı artırır mı?

Dolaylı olarak evet: milyonlarca dosyayı tarayan find veya yedekleme işleri disk G/Ç bekleyişini artırır. Yüksek load ile düşük CPU kullanımını birlikte görüyorsanız load average rehberine bakın.

Yazılım ve hizmet adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen projeler veya şirketlerle ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Disk doldu hatası · logrotate ile log yönetimi · Load average nedir · OOM killer çözümü · CPU %100 tespiti · Linux sunucu komutları · Sunucu izleme temelleri