Sunucuya bağlanıyorsunuz, df -h yazıyorsunuz ve kök bölümün yüzde 97 dolu olduğunu görüyorsunuz. Uygulamalarınız hâlâ ayakta ama yeni bir imaj çekemiyorsunuz, log yazamıyorsunuz, veritabanı no space left on device hatası vermeye başlamış. du ile arıyorsunuz ve suçlu ortaya çıkıyor: /var/lib/docker tek başına 120 GB. İçine giriyorsunuz, overlay2 klasöründe rastgele harflerden oluşan binlerce dizin var ve hiçbiri size hangi imaja ya da konteynere ait olduğunu söylemiyor.
Bu noktada verilebilecek en pahalı karar, o dizinleri elle silmeye başlamaktır. overlay2 altındaki klasörler bağımsız dosya yığınları değil, çalışan konteynerlerin dosya sistemi katmanlarıdır ve Docker'ın veritabanı bu klasörlerin varlığına göre yazılmıştır. Birini sildiğiniz anda ilgili konteyner artık başlamaz, hatta Docker daemon'ın kendisi tutarsız duruma düşebilir. Bu yazının en önemli cümlesi bu yüzden en başta duruyor: overlay2 içine elle dokunmayın.
Doğrusu ölçmekle başlamaktır. Docker'ın diskte tuttuğu alan beş kalemde toplanır: imajlar, konteynerler, volume'lar, build cache ve loglar. İlk dördünü docker system df tek komutla raporlar; beşincisi, yani konteyner logları, o tabloda hiç görünmez ve tam bu yüzden en sık gözden kaçan kalemdir. Hangisinin şiştiğini bilmeden yapılan temizlik ya yetersiz kalır ya da hâlâ ihtiyacınız olan veriyi siler. Aşağıda önce ölçüm, sonra en az riskliden en risklisine doğru sıralanmış bir temizlik akışı bulacaksınız. Docker kavramlarına yeniyseniz Docker nedir rehberi bu yazının varsaydığı temeli veriyor.
Diskin Nereye Gittiğini Önce Ölçün#
İlk komut her zaman docker system df olmalı. Bu komut, Docker'ın tuttuğu alanı kategorilere ayırıp her kategoride ne kadarının geri kazanılabilir olduğunu gösterir:
docker system df
Tipik bir çıktı:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 64 7 48.2GB 41.9GB (86%)
Containers 23 6 1.1GB 840MB (74%)
Local Volumes 31 5 52.4GB 38.1GB (72%)
Build Cache 412 0 18.6GB 18.6GB
Bu tabloyu okumanın kuralı basittir: TOTAL o türden kaç nesne olduğunu, ACTIVE kaçının hâlen kullanımda olduğunu, RECLAIMABLE ise kullanımda olmayan ve dolayısıyla silinebilecek alanı gösterir. Yukarıdaki örnekte 48 GB imajın 42 GB'ı boşta duruyor ve build cache'in tamamı geri kazanılabilir durumda. Bu çıktıya bakmadan docker system prune -a yazmak, bir odayı karanlıkta toplamak gibidir.
RECLAIMABLE toplamı ile diskteki gerçek eksiklik arasında büyük fark varsa suçlu büyük ihtimalle listede görünmeyen bir kalemdir: konteyner logları. Onları biraz sonra ele alacağız.
Docker'ın diski gerçekte nerede tuttuğunu da doğrulayın; varsayılan /var/lib/docker olsa da bu ayar değiştirilmiş olabilir:
docker info --format '{{.DockerRootDir}}'
du -sh /var/lib/docker/* 2>/dev/null | sort -h
Sunucu genelinde disk analizi yapmak için df, du ve ncdu üçlüsünü disk kullanımını df, du ve ncdu ile analiz etme yazısında ayrıntılı ele almıştık; Docker dışı bir kaynak da diski dolduruyor olabilir.
docker system df -v ile Suçluyu İsimlendirin#
Özet tablo hangi kategorinin şiştiğini söyler ama hangi imajın ya da hangi volume'un sorumlu olduğunu söylemez. -v bayrağı bunu verir:
docker system df -v
Çıktı üç bölüme ayrılır. Images bölümünde her imajın boyutu ve kaç konteyner tarafından kullanıldığı (CONTAINERS sütunu) yer alır; bu sütunda 0 yazan imajlar hiçbir konteyner tarafından kullanılmıyor demektir. Containers bölümünde her konteynerin yazılabilir katmanının boyutu görünür. Local Volumes bölümünde ise her volume'un boyutu ve LINKS sütununda kaç konteynere bağlı olduğu listelenir; LINKS değeri 0 olan volume'lar yetim kalmıştır.
Konteynerlerin kendi içlerine ne kadar yazdığını görmek için ayrı bir komut daha vardır:
docker ps -as --format 'table {{.Names}}\t{{.Size}}\t{{.Status}}'
SIZE sütununda iki değer görürsünüz: birincisi konteynerin yazılabilir katmanı, parantez içindeki virtual değeri ise imaj dahil toplam boyuttur. Yazılabilir katmanı gigabaytlara ulaşan bir konteyner, verisini volume yerine kendi içine yazıyor demektir; bu hem diski şişirir hem de konteyneri sildiğinizde veriyi kaybetmenize yol açar. Doğru yaklaşım için Docker volume ile veri yönetimi yazısına bakın.
/var/lib/docker İçinde Hangi Klasör Ne İşe Yarar?#
Elle silme cazibesine kapılmamak için burada ne olduğunu bilmek gerekir:
| Dizin | İçeriği | Elle silinir mi? |
|---|---|---|
overlay2/ | İmaj ve konteyner dosya sistemi katmanları | Asla — Docker komutlarıyla temizlenir |
containers/ | Konteyner meta verisi ve JSON log dosyaları | Sadece log dosyası sıfırlanabilir |
volumes/ | Named ve anonim volume verisi | Hayır — docker volume rm kullanın |
image/ | Katman ve imaj veritabanı | Asla — overlay2 ile eşleşmesi bozulur |
buildkit/ | Build cache verisi | Hayır — docker builder prune kullanın |
network/ | Ağ yapılandırması | Hayır |
overlay2 neredeyse her zaman en büyük klasördür ve bu normaldir; imajların da konteynerlerin de gerçek dosyaları orada durur. Yani overlay2 büyük olduğu için sorun yoktur, içinde kullanılmayan katmanlar biriktiği için sorun vardır. O katmanları hedefe yönelik silmenin tek güvenli yolu Docker'ın kendi prune komutlarıdır; Docker hangi katmanın hangi imaja ait olduğunu bilir, siz klasör isimlerine bakarak bilemezsiniz.
Temizlik Sırası: En Az Riskliden Başlayın#
Aşağıdaki sıra kasten böyle kurulmuştur. Her adım bir öncekinden biraz daha fazla şey siler, bu yüzden yer açıldığı anda durabilirsiniz. Her adımdan sonra docker system df ile ne kazandığınızı kontrol edin.
1. Durmuş konteynerleri silin (risk: yok)#
Çalışmayan konteynerler yazılabilir katmanlarıyla birlikte yer kaplamaya devam eder. Bunları silmek imajları etkilemez; konteyneri istediğiniz zaman aynı imajdan yeniden oluşturursunuz.
# Önce ne silineceğini görün
docker ps -a --filter status=exited --filter status=created
# Sonra silin
docker container prune
2. Dangling imajları silin (risk: yok)#
"Dangling" imaj, etiketi olmayan ara imajdır; genellikle aynı etiketle yeniden build aldığınızda eski sürüm etiketini kaybedip burada birikir. <none> olarak görünürler ve hiçbir işe yaramazlar.
docker images --filter dangling=true
docker image prune
3. Build cache'i temizleyin (risk: yok, maliyet: sonraki build yavaşlar)#
Sık build alan sunucularda en büyük şişkinlik genelde buradadır; BuildKit her katmanı gelecekteki build'leri hızlandırmak için saklar ve bu birikime kendiliğinden bir sınır koymaz.
# Kullanılmayan cache
docker builder prune
# Tamamı, üstelik yaş filtresiyle: 7 günden eskiyi at
docker builder prune --all --filter 'until=168h'
Yaş filtresi kullanmak, son günlerin cache'ini koruyup eskiyi atmanızı sağlar — günlük dağıtım yapan bir sunucuda en dengeli seçenek budur. Build cache'in en baştan az şişmesi için katman sırasının nasıl kurulması gerektiğini Dockerfile en iyi pratikleri yazısında bulabilirsiniz.
4. Kullanılmayan imajları silin (risk: düşük, yeniden indirme gerekir)#
docker image prune -a yalnızca dangling olanları değil, hiçbir konteyner tarafından kullanılmayan tüm imajları siler. Bu genelde en büyük kazancı sağlayan adımdır ama dikkatli olun: internet bağlantısı olmayan ya da özel bir registry'e bağımlı bir sunucuda geri getiremeyeceğiniz bir imajı silebilirsiniz.
# Kullanılmayan tüm imajlar
docker image prune -a
# Daha temkinli: 30 günden eski olanlar
docker image prune -a --filter 'until=720h'
Durdurulmuş ama silmek istemediğiniz konteynerlerin imajları da "kullanılmıyor" sayılmaz, çünkü konteyner ona bağlıdır. Bu yüzden 1. adımı 4. adımdan önce yapmak kazancı artırır. Uzun vadede imajların boyutunu küçültmek için Docker imaj boyutu optimizasyonu yazısındaki çok aşamalı build yaklaşımı en kalıcı çözümdür.
5. Volume'lara en son ve en dikkatli dokunun#
Burası veri silinen yerdir. Bir volume'un içinde veritabanınız, yüklenmiş dosyalarınız veya sertifikalarınız olabilir ve prune sonrası geri dönüşü yoktur. Önce ne olduğunu görün:
# Hiçbir konteynere bağlı olmayan volume'lar
docker volume ls -qf dangling=true
# Şüphelendiğiniz volume'un içine bakın
docker run --rm -v supheli_volume:/veri alpine ls -lah /veri
Silme komutlarının davranışı Docker 23.0 ile değişti ve bu ayrım önemlidir: docker volume prune artık varsayılan olarak yalnızca anonim volume'ları siler; isimli (named) volume'ları da silmek için --all gerekir. Yani eski alışkanlıkla yazılan komut daha az şey silecektir — bu iyi bir haber, ama --all eklerken ne yaptığınızı bilmelisiniz.
# Yalnızca anonim, kullanılmayan volume'lar
docker volume prune
# İsimli olanlar dahil: veri kaybı riski yüksek
docker volume prune --all
Compose ile kurulmuş bir yığında isimli volume'lar genellikle veritabanınızın kendisidir. --all yazmadan önce yedeğinizin olduğundan emin olun.
Tek komutla toplu temizlik#
Ne yaptığınızı bildiğinizde her şeyi tek satırda yapabilirsiniz, ama bayrakların anlamını karıştırmayın:
| Komut | Ne siler | Risk |
|---|---|---|
docker system prune | Durmuş konteyner, kullanılmayan ağ, dangling imaj, build cache | Düşük |
docker system prune -a | Yukarıdakiler + kullanılmayan tüm imajlar | Orta: imajlar yeniden indirilir |
docker system prune -a --volumes | Yukarıdakiler + kullanılmayan anonim volume'lar | Yüksek: veri silinir |
docker system prune -a --volumes --filter 'until=48h' | Aynısı, ama 48 saatten eski nesnelerle sınırlı | Orta |
Otomatik çalışan bir bakım script'inde --volumes bayrağını kullanmayın. Zaman kazandırdığı süre, bir gün yanlışlıkla silinen veritabanının maliyetiyle kıyaslanamaz.
overlay2 Klasörünü Elle Silmenin Sonucu#
Arama sonuçlarında sık karşılaşılan ve düzenli olarak sunucu bozan bir öneri vardır: "/var/lib/docker/overlay2 altındaki eski klasörleri silin". Bunu yapmayın.
Docker, hangi katmanın hangi imaja ve konteynere ait olduğunu /var/lib/docker/image/overlay2 altındaki veritabanında tutar. overlay2 içinden bir dizini elle sildiğinizde bu veritabanı hâlâ o dizinin var olduğuna inanır. Sonuç şu belirtilerle karşınıza çıkar: konteyner error while creating mount source path ya da failed to get digest hatasıyla başlamaz, docker images çalışır ama docker run çalışmaz, bazen daemon'ın kendisi ayağa kalkmaz. Üstelik bu bozulma çoğu zaman anında değil, konteyner bir sonraki kez yeniden başlatıldığında ortaya çıkar — yani sunucuyu bozan komutla arıza arasında saatler olabilir.
Bu duruma zaten düştüyseniz güvenilir tek onarım, etkilenen konteyner ve imajları Docker komutlarıyla silip yeniden oluşturmaktır:
docker rm -f bozuk_konteyner
docker rmi -f bozuk_imaj
docker pull bozuk_imaj
Hiçbiri işe yaramıyorsa ve o sunucudaki veri volume'larda tutuluyorsa, Docker servisini durdurup /var/lib/docker dizininin tamamını temiz bir başlangıç için silmek (imajları yeniden çekmek pahasına) parça parça silmekten daha güvenlidir. Ama bu, volume'larınızı da sileceği için yalnızca yedeğiniz varsa düşünülmelidir.
Konteyner Logları Diski Sessizce Dolduruyor#
docker system df toplamı ile diskteki gerçek kayıp arasında onlarca gigabaytlık fark varsa suçlu neredeyse kesin olarak JSON log dosyalarıdır. Docker varsayılan json-file sürücüsünde konteynerin ürettiği her satırı diske yazar ve varsayılan olarak hiçbir boyut sınırı yoktur. Hata döngüsüne girmiş ya da çok konuşkan bir uygulama bir haftada 40 GB log üretebilir.
En büyük log dosyalarını bulun:
du -h /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -h | tail -10
# Belirli bir konteynerin log dosyasının yolu
docker inspect --format '{{.LogPath}}' api
Acil durumda log dosyasını sıfırlamak, konteyneri durdurmadan yer açar. Dosyayı silmeyin — Docker açık dosya tanıtıcısını tutmaya devam eder ve yer geri gelmez; bunun yerine içeriğini boşaltın:
truncate -s 0 "$(docker inspect --format '{{.LogPath}}' api)"
Bu geçici bir müdahaledir. Kalıcı çözüm, log döndürmeyi daemon düzeyinde varsayılan hâline getirmektir. /etc/docker/daemon.json dosyasını düzenleyin:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Ardından systemctl restart docker çalıştırın. Bu ayarla her konteyner en fazla 30 MB log tutar. Kritik bir nokta var: kural yalnızca yeni oluşturulan konteynerlere uygulanır. Mevcut konteynerleriniz eski sınırsız ayarla çalışmaya devam eder, onları docker compose up -d --force-recreate ya da eşdeğeri bir komutla yeniden oluşturmanız gerekir. Konteyner başına ayar yapmak isterseniz Compose dosyasında da tanımlayabilirsiniz:
services:
api:
image: myapp:1.4
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Sunucudaki diğer log kaynaklarını da aynı disiplinle yönetmek için logrotate ile log yönetimi yazısı iyi bir tamamlayıcıdır.
Temizledim Ama Yer Açılmadı: Ne Kaçırdınız?#
Bazen prune komutları çalışır, çıktı gigabaytlarca alan geri kazanıldığını söyler, ama df -h neredeyse hiç değişmez. Üç olası sebep vardır.
Silinen ama hâlâ açık tutulan dosyalar. Bir süreç, silinmiş bir dosyayı hâlâ açık tutuyorsa alan serbest kalmaz. Kontrol edin:
lsof +L1 2>/dev/null | head -20
Listede Docker ile ilgili büyük dosyalar varsa ilgili konteyneri yeniden başlatmak ya da systemctl restart docker çalıştırmak alanı serbest bırakır.
Docker dışı bir kaynak. /var/lib/docker gerçekten küçüldüyse ama disk hâlâ doluysa suçlu başka yerdedir: /var/log, veritabanı dizinleri, eski yedekler veya paket önbelleği. Genel yaklaşım için disk dolu ve no space left hatasının çözümü yazısına bakın.
Inode tükenmesi. Disk alanı boş görünürken hâlâ dosya oluşturulamıyorsa inode'lar bitmiş olabilir; Docker'ın binlerce küçük katman dosyası bunu tetikleyebilir:
df -i
IUse% yüzde 100'e yakınsa çözüm alan açmak değil, çok sayıda küçük dosyayı temizlemektir — kullanılmayan imajları silmek burada doğrudan işe yarar.
Bakımı Alışkanlık Hâline Getirin#
Docker'ın diski doldurması bir arıza değil, bakımsızlığın doğal sonucudur; hiçbir prune komutu kendiliğinden çalışmaz. Haftalık bir zamanlanmış görev çoğu sunucu için yeterlidir:
# /etc/cron.weekly/docker-temizlik
#!/bin/sh
docker container prune -f
docker image prune -af --filter 'until=336h'
docker builder prune -af --filter 'until=168h'
Bu script'te --volumes bulunmadığına dikkat edin; otomatik hiçbir işin veri silme yetkisi olmamalıdır. Yanına basit bir disk eşiği uyarısı kurmak da faydalıdır: kullanım yüzde 80'i geçtiğinde haber alırsanız, yüzde 100'de veritabanı çökmesiyle karşılaşmazsınız. Ölçmeden silmeyin, volume'a en son dokunun ve overlay2 klasörünü hiç açmayın — bu üç kural, Docker kaynaklı disk sorunlarının neredeyse tamamını sorunsuz kapatır.
Sıkça Sorulan Sorular#
docker system prune neyi siler?#
Bayraksız hâli durmuş konteynerleri, kullanılmayan ağları, etiketsiz (dangling) imajları ve build cache'i siler; volume'lara ve kullanımdaki imajlara dokunmaz. -a eklerseniz hiçbir konteyner tarafından kullanılmayan tüm imajlar da silinir. --volumes eklerseniz kullanılmayan anonim volume'lar da gider ve bu adım veri kaybına yol açabilir. Komutu çalıştırmadan önce mutlaka docker system df ile ne kadar alanın gerçekten geri kazanılabilir olduğuna bakın.
/var/lib/docker/overlay2 klasörünü silebilir miyim?#
Hayır. O klasördeki dizinler imaj ve konteyner dosya sistemi katmanlarıdır ve Docker'ın kendi veritabanıyla eşleşir. Elle sildiğinizde veritabanı hâlâ o katmanların var olduğunu düşünür; konteynerler başlamaz, imajlar bozulur, bazen daemon hiç ayağa kalkmaz. Üstelik arıza çoğu zaman hemen değil, konteyner bir sonraki yeniden başlatmada ortaya çıkar. Katmanları temizlemenin tek güvenli yolu docker image prune ve docker builder prune komutlarıdır.
docker volume prune veri siler mi?#
Evet, silebilir. Hiçbir konteynere bağlı olmayan volume'ların içindeki her şey kalıcı olarak gider. Docker 23.0'dan itibaren komut varsayılan olarak yalnızca anonim volume'ları temizler; isimli volume'ları da silmek için --all gerekir. Compose ile kurulmuş yığınlarda isimli volume'lar genellikle veritabanının kendisidir, bu yüzden --all yazmadan önce docker volume ls -qf dangling=true ile listeyi görün ve yedeğinizden emin olun.
Docker log dosyaları neden bu kadar büyüyor?#
Çünkü varsayılan json-file log sürücüsünün hiçbir boyut sınırı yoktur; konteyner ne yazarsa diske gider ve dosya sonsuza kadar büyür. Sürekli hata basan veya çok ayrıntılı log üreten bir uygulama birkaç günde onlarca gigabayt üretebilir. Çözüm /etc/docker/daemon.json dosyasına max-size ve max-file seçeneklerini eklemektir. Bu ayar yalnızca yeni oluşturulan konteynerlere uygulanır, mevcut olanları yeniden oluşturmanız gerekir.
Temizlik yaptım ama df hâlâ dolu gösteriyor, neden?#
En sık nedeni silinmiş ama bir süreç tarafından hâlâ açık tutulan dosyalardır; lsof +L1 ile kontrol edip Docker servisini yeniden başlatmak alanı serbest bırakır. İkinci olasılık, dolan yerin Docker'la ilgisi olmamasıdır: /var/log, yedek dizinleri veya paket önbelleği aynı bölümü paylaşıyor olabilir. Üçüncüsü inode tükenmesidir; df -i çıktısında kullanım oranı yüzde 100'e yakınsa alan değil dosya sayısı sınırına çarpmışsınızdır.
Docker'ı başka bir diske taşıyabilir miyim?#
Evet. /etc/docker/daemon.json içine "data-root": "/yeni/yol" ekleyip Docker'ı durdurun, mevcut /var/lib/docker içeriğini izinleri koruyarak yeni konuma taşıyın (örneğin rsync -aHAX ile) ve servisi yeniden başlatın. Bu, sistem diskini rahatlatmanın kalıcı yoludur; özellikle imaj ve volume ihtiyacı büyüyen sunucularda kök bölümü tekrar tekrar temizlemekten daha sağlıklıdır. Taşımadan önce mutlaka yedek alın ve tüm konteynerlerin durduğundan emin olun.