Docker build cache, her Dockerfile talimatının sonucunu saklayıp bir sonraki derlemede talimat ve bağlı olduğu dosyalar değişmediyse yeniden kullanan mekanizmadır. Bir katman değiştiğinde o katman ve ondan sonraki tüm katmanlar yeniden derlenir. Önbellek varsayılan olarak builder'ın içinde yaşar; CI gibi her seferinde sıfırdan başlayan ortamlarda ise --cache-to ile dışa aktarılıp --cache-from ile geri alınır. Dışa aktarma için registry, local, inline ve gha gibi backend'ler kullanılır.
Bu yazıda önbelleğin ne zaman geçersiz kaldığını, Dockerfile'ı önbellek dostu yazmanın belgelerdeki yollarını, cache mount kullanımını, harici cache backend'lerini ve önbelleğin diski doldurmasını önleyen garbage collection ayarlarını anlatıyoruz. Katman önbelleğinin temel mantığı Dockerfile rehberinde de özetleniyor; burada daha derine iniyoruz. Konu haritası için Docker öğrenme rehberi.
Önbellek nasıl çalışır?
Belgelerdeki modelde Dockerfile'daki her talimat imajda bir katmana karşılık gelir ve katmanlar üst üste dizilmiş bir yığın gibi düşünülür. Bir katman değiştiğinde yeniden derlenmesi gerekir. Örneğin bir C programında main.c dosyasını değiştirdiğinizde, bu dosyayı kopyalayan COPY talimatı yeniden çalışmak zorundadır; Docker bu katmanın önbelleğini geçersiz sayar. Daha önemlisi, ondan sonra gelen tüm katmanlar da etkilenir. Sonraki adımlar aynı sonucu üretecek olsa bile yeniden çalışır. Önbellek optimizasyonunun neredeyse tamamı bu tek kuraldan türer.
Önbellek ne zaman geçersiz olur?

Builder her talimat için önbellekten yeniden kullanıp kullanamayacağını kontrol eder. Belgelerdeki temel kurallar şunlardır:
- Builder önce temel imajın önbellekte olup olmadığına bakar. Sonraki her talimat önbellekteki katmanlarla karşılaştırılır; tam eşleşen bir katman yoksa önbellek geçersiz olur.
- Çoğu talimat için talimatın metnini önbellekteki katmanla karşılaştırmak yeterlidir.
ADD,COPYve bind mount kullananRUN --mount=type=bindtalimatlarında builder, dosya meta verilerinden bir checksum hesaplar. İlgili dosyalardan birinin meta verisi değişmişse önbellek geçersiz olur.- Dosyanın değiştirilme zamanı (
mtime) bu hesaba katılmaz. Yalnızcamtimedeğiştiyse önbellek bozulmaz. - Bu üçü dışında builder container içindeki dosyalara bakmaz.
RUN apt-get -y updategibi bir komutta yalnızca komut metninin kendisi eşleştirilir.
Önbellek bir kez geçersiz olunca sonraki tüm komutlar yeni katman üretir ve önbellek kullanılmaz. Belgelerin buradan çıkardığı öneri açıktır: mümkün olduğunca talimatları seyrek değişenden sık değişene doğru sıralayın.
RUN önbelleği kendiliğinden yenilenmez
Son kuralın pratik bir sonucu vardır. RUN apk add curl gibi bir adım bir kez önbelleğe girdikten sonra, komut metni aynı kaldıkça yeniden çalışmaz. Belgelerin ifadesiyle imajı bir hafta sonra yeniden derlemek size yine aynı paketleri verir; yani önbellek, paket deposundaki güncellemeleri görmez. Bir RUN adımını yeniden çalıştırmaya zorlamak için belgeler üç yol sayar:
- Ondan önceki bir katmanı değiştirmek.
- Derlemeden önce önbelleği
docker builder pruneile temizlemek. --no-cacheya da--no-cache-filterseçeneklerini kullanmak.
--no-cache-filter önbelleği yalnızca belirli bir stage için geçersiz kılar. Aşağıdaki örnekte install adlı stage yeniden çalışır, diğer stage'ler önbellekten gelebilir:
# syntax=docker/dockerfile:1
FROM alpine AS install
RUN apk add curldocker build --no-cache-filter install .Güvenlik güncellemelerini düzenli almak isteyen bir sunucuda bu ayrım önemlidir: tüm projeyi --no-cache ile sıfırdan derlemek yerine yalnızca paket kuran stage'i yenileyebilirsiniz. Temel imajı güncel tutmak için --pull kullanımı base image seçimi yazısında anlatılıyor.
Secret'lar ve SOURCE_DATE_EPOCH
Build secret'larının içeriği önbelleğin parçası değildir; secret değerini değiştirmek önbelleği geçersiz kılmaz. Belgeler secret değiştiğinde yeniden çalıştırmayı zorlamak için, secret'la birlikte değiştirdiğiniz rastgele değerli bir build argümanı önerir, çünkü build argümanları önbelleği geçersiz kılar. Secret'ın kimliği ve bağlandığı yol gibi özellikleri ise checksum'a katılır; bunları değiştirmek önbelleği bozar. Secret kullanımının kendisi build secrets yazısında.
FROM alpine
ARG CACHEBUST
RUN --mount=type=secret,id=TOKEN,env=TOKEN \
some-command ...İkinci ince nokta SOURCE_DATE_EPOCH build argümanıdır. Bu argüman derleme sırasında oluşturulan dosyaların zaman damgalarını belirler ve WORKDIR talimatı önbellek geçerliliğini belirlerken onu dikkate alır. Değeri derlemeler arasında değişirse WORKDIR ve sonraki tüm talimatların önbelleği bozulur. Değeri her commit'in zaman damgasına bağlarsanız önbellek her commit'te kırılır; belgeler bunun beklenen davranış olduğunu söyler ve sık geçersizleşme olmadan tekrarlanabilir derleme için sabit bir değer önerir:
docker build --build-arg SOURCE_DATE_EPOCH=0 .Önbelleği iyi kullanmak için dört teknik

Katmanları doğru sıralayın
Değişiklik kendisinden sonraki adımları yeniden derlettiği için pahalı adımları başa, sık değişen adımları sona koymak gerekir. Belgelerdeki JavaScript örneğinde tüm proje önce kopyalanıp sonra bağımlılıklar kuruluyor; bu durumda herhangi bir dosyadaki değişiklik bağımlılıkların her seferinde yeniden kurulmasına yol açar. Çözüm COPY adımını ikiye bölmektir: önce yalnızca paket yönetimi dosyalarını kopyalayıp bağımlılıkları kurmak, sonra kaynak kodu kopyalamak.
# syntax=docker/dockerfile:1
FROM node
WORKDIR /app
COPY package.json yarn.lock .
RUN npm install
COPY . .
RUN npm buildContext'i küçük tutun
Context, builder'a gönderilen dosya ve klasörlerin kümesidir. Küçük bir context hem aktarılan veriyi azaltır hem de ilgisiz bir dosya yüzünden önbelleğin bozulma ihtimalini düşürür. Bunun kolay yolu context'in kökünde bir .dockerignore dosyasıdır; kurallar alt klasörler dahil tüm context'e uygulanır. Söz dizimi build context yazısında anlatılıyor.
Kopyalamak yerine bind mount kullanın
Kaynak kod yalnızca bir çıktı üretmek için gerekiyorsa onu COPY ile imaja almak yerine RUN --mount=type=bind ile o adımın süresince bağlayabilirsiniz. Belgelere göre COPY ile eklenen dosyaların tamamı, son imajda kullanılmasalar bile BuildKit önbelleğine girer; bind mount edilen dosyalar ise ne son imajda ne de build önbelleğinde kalır, yalnızca komutun çıktısı kalır.
FROM golang:latest
WORKDIR /build
RUN --mount=type=bind,target=. go build -o /app/helloBind mount varsayılan olarak salt okunurdur; bu yüzden çıktı bağlama noktasının dışına, örnekteki gibi /app/hello yoluna yazılır. rw seçeneği yazmaya izin verir, ama yazılanlar adım bitince atılır. Hedef klasör boş değilse içeriği adım süresince gizlenir ve adım bitince geri gelir. COPY ile bu yaklaşımın karşılaştırması COPY ve ADD farkı yazısında de var.
Paket yöneticileri için cache mount
Normal önbellek katmanı ya tamamen kullanılır ya tamamen yeniden derlenir. Cache mount ise derlemeler arasında kalıcı ve birikimli bir klasör sağlar: katman yeniden derlense bile paket yöneticisi yalnızca yeni ya da değişen paketleri indirir. Belgeler araç başına örnekler verir; Python ve Apt için olanlar şöyledir:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txtRUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt update && apt-get --no-install-recommends install -y gccApt örneğindeki sharing=locked rastgele değildir. Belgelere göre Apt verisine özel erişim ister; bu seçenek aynı cache mount'u kullanan paralel derlemelerin birbirini beklemesini ve aynı dosyalara aynı anda dokunmamasını sağlar. Belgelerin genel uyarısı da budur: her paket yöneticisinin önbellek gereksinimi farklıdır, yanlış seçenekler beklenmedik davranışa yol açabilir; kullandığınız aracın belgelerine bakın.
Harici önbellek: --cache-to ve --cache-from
Varsayılan önbellek, kullandığınız builder'a özeldir ve builder'lar arasında paylaşılmaz. CI ortamlarında builder'lar çoğunlukla geçicidir ve çalıştırmalar arasında kalıcılık yoktur; belgeler bu yüzden harici önbelleği CI/CD için "neredeyse zorunlu" olarak niteler. Harici önbelleği kullanmak için önce --cache-to ile bir backend'e aktarır, sonraki derlemede --cache-from ile içe alırsınız. Her zaman açık olan yerel BuildKit önbelleğinin aksine, harici backend'lere aktarım ve onlardan alım açıkça istenmelidir.
docker buildx build --push -t registry.example.com/benim/web:latest \
--cache-to type=registry,ref=registry.example.com/benim/web:buildcache,mode=max \
--cache-from type=registry,ref=registry.example.com/benim/web:buildcache .| Backend | Nereye yazar | Not |
|---|---|---|
| inline | Önbelleği imajın içine gömer | Yalnızca image exporter ile çalışır; mode=max desteklemez |
| registry | Ayrı bir önbellek imajı olarak registry'ye | İmajdan bağımsız; multi-stage için mode=max |
| local | Yerel bir klasöre (OCI image layout) | Test veya kendi paylaşımlı depolamanız için |
| gha | GitHub Actions önbelleğine | Belgelerde beta olarak işaretli |
| s3 | AWS S3 bucket'ına | Belgelerde unreleased olarak işaretli |
| azblob | Azure Blob Storage'a | Belgelerde unreleased olarak işaretli |
Belgelerin önemli bir notu, varsayılan docker driver'ının inline, local, registry ve gha backend'lerini yalnızca containerd image store açıksa desteklediğidir; diğer backend'ler için farklı bir driver gerekir. Driver seçimi BuildKit ve Buildx yazısında anlatılıyor.
min ve max modu
--cache-to bir mode seçeneği alır ve inline dışındaki tüm backend'lerde desteklenir. Varsayılan olan min modunda yalnızca sonuç imaja giren katmanlar önbelleğe alınır; max modunda ara adımlar dahil tüm katmanlar önbelleğe girer. min önbelleği genelde daha küçüktür, içe ve dışa aktarma süresini ve depolama maliyetini düşürür; max ise daha fazla önbellek isabeti sağlama olasılığı yüksektir. Belgeler projenize göre ikisini denemenizi önerir. Mod yalnızca dışa aktarırken belirtilir; içe alırken ilgili parametreler otomatik algılanır.
Konum çakışması ve dal başına önbellek
Belgelerdeki genel kural şudur: her önbellek bir konuma yazar ve aynı konuma iki kez yazmak önceki önbelleğin üzerine yazmak demektir. Git dalı başına ayrı önbellek tutmak istiyorsanız farklı konumlar kullanmalısınız. BuildKit birden fazla önbellek kaynağından içe almayı desteklediği için yaygın bir kalıp, hem mevcut dalın hem de main dalının önbelleğini okumaktır:
docker buildx build --push -t registry.example.com/benim/web:latest \
--cache-to type=registry,ref=registry.example.com/benim/web-cache:ozellik-x \
--cache-from type=registry,ref=registry.example.com/benim/web-cache:ozellik-x \
--cache-from type=registry,ref=registry.example.com/benim/web-cache:main .Backend'lere özgü ayrıntılar
registry: ref değeri imajı gönderdiğiniz hedefle aynı olmamalıdır; farklı etiket, farklı imaj adı ya da farklı depo seçebilirsiniz. --cache-from hedefi yoksa içe alma adımı başarısız olur ama derleme devam eder. Varsayılan sıkıştırma gzip'tir; compression=zstd ve compression-level ile değiştirilebilir. ignore-error=true, önbellek dışa aktarımı başarısız olduğunda derlemenin hata vermesini engeller. Belgeler ayrıca Amazon ECR gibi image index desteklemeyen registry'ler için image-manifest parametresini anar ve BuildKit v0.21'den beri bunun varsayılan olarak açık olduğunu belirtir.
local: Önbelleği OCI image layout biçiminde bir klasöre yazar; dest dışa aktarma, src içe alma yoludur. Etiketlerle aynı klasörde birden fazla önbellek tutulabilir (varsayılan etiket latest). Bir etiketi güncellemek önceki manifest'in blob'larını silmez, klasör zamanla büyür; belgelere göre Buildx 0.35.0 ve sonrası dışa aktarırken reset=true ile hiçbir etiketin göstermediği blob'ları siler.
inline: En basit harici önbellektir ve önbelleği imajla aynı yere gönderir. Ek parametre almaz; --cache-to type=inline yerine BUILDKIT_INLINE_CACHE=1 build argümanı da kullanılabilir. İçe almak için --cache-from type=registry ile imajın kendisi gösterilir. Belgelere göre multi-stage derlemelerde diğer backend'ler kadar iyi ölçeklenmez ve imaj ile önbelleği ayırmaz.
gha: GitHub Actions içindeki derlemeler için önerilen backend'dir. Varsayılan scope değeri buildkit olduğundan birden fazla imaj derliyorsanız her derleme öncekinin önbelleğinin üzerine yazar; her imaja ayrı scope vermelisiniz. GitHub iş akışlarının kendisi GitHub Actions CI/CD rehberinde anlatılıyor.
Compose dosyasında cache_from ve cache_to
Compose Build belirtimi aynı söz dizimini cache_from ve cache_to listeleriyle sunar. Belirtime göre yalnızca ad yazmak type=registry,ref=AD kısaltmasıdır ve desteklenmeyen önbellek türleri derlemeyi engellemeden yok sayılır. Aşağıdaki dosya Docker Compose v5.5.1 ile docker compose config doğrulamasından geçti:
services:
web:
image: registry.example.com/benim/web:latest
build:
context: .
cache_from:
- type=registry,ref=registry.example.com/benim/web:buildcache
- type=local,src=.buildcache
cache_to:
- type=local,dest=.buildcache,mode=maxGarbage collection: önbelleğin diski doldurmasını önlemek

docker builder prune ve docker buildx prune tek seferlik temizlik yapar. Garbage collection (GC) ise belirli aralıklarla çalışır ve sıralı bir kural listesini izler: BuildKit daemon'u önbellek fazla büyüdüğünde ya da önbellek kayıtlarının süresi dolduğunda temizlik yapar. Kurallar en özelden en genele doğru sırayla değerlendirilir; önceki kural yeterli yer açmadıysa sonrakine geçilir. Belgelere göre varsayılan kurallar yaklaşık olarak şöyledir:
- Yeniden üretilebilen önbelleği (yerel klasör ve Git context'leri, cache mount'lar) 48 saattir kullanılmıyorsa sil.
- 60 günden uzun süredir bir derlemede kullanılmamış önbelleği sil.
- Önbellek boyut sınırını aşan paylaşılmamış önbelleği sil (başka kaynakların, genellikle imaj katmanlarının kullanmadığı blob'lar).
- Boyut sınırını aşan her türlü önbelleği sil.
Yapılandırma yeri driver'a göre değişir. Varsayılan docker driver'ında GC, Docker daemon yapılandırma dosyasındaki builder.gc anahtarıyla ayarlanır. En kolay ayar defaultKeepStorage değeridir: GC'yi fazla agresif buluyorsanız artırın, yer kazanmak istiyorsanız azaltın. Belgelerin Docker Desktop için verdiği varsayılan değer 20GB'tır:
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
}
}Daha fazla kontrol için kuralları kendiniz yazabilirsiniz. Belgelerdeki daha muhafazakâr örnek: önbellek 50GB'ı aşarsa 60 günden eski kullanılmayan kayıtları, sonra paylaşılmamış kayıtları sil; 100GB'ı aşarsa her şeyi sil.
{
"builder": {
"gc": {
"enabled": true,
"policy": [
{ "reservedSpace": "50GB", "keepDuration": ["1440h"] },
{ "reservedSpace": "50GB" },
{ "reservedSpace": "100GB", "all": true }
]
}
}
}daemon.json değişikliğinin nasıl uygulanacağı daemon.json yazısında anlatılıyor. Belgelerin küçük ama önemli bir notu filtre söz dizimiyle ilgilidir: daemon.json içinde eşitlik tek = ile (type=source.local), buildkitd.toml içinde çift == ile (type==source.local) yazılır.
docker dışındaki driver'larda GC buildkitd.toml dosyasıyla ayarlanır. Üç üst düzey eşik vardır: reservedSpace (önbellek için her zaman ayrılabilecek en az alan; varsayılanı disk alanının yüzde 10'u ya da 10GB, hangisi düşükse), maxUsedSpace (BuildKit'in kullanabileceği en fazla alan; varsayılanı yüzde 60 ya da 100GB, hangisi düşükse) ve minFreeSpace (boş kalması gereken alan; varsayılanı 20GB). reservedSpace en yüksek önceliğe sahiptir; diğer eşikler daha düşük bir değer gösterse bile önbellek bu değerin altına indirilmez. Özel kurallar tanımladığınızda bu global değerler uygulanmaz.
[worker.oci]
gc = true
reservedSpace = "10GB"
maxUsedSpace = "20GB"Belgelere göre reservedSpace ve maxUsedSpace birlikte verildiğinde GC sonrası önbellek boyutu bu iki eşik arasında kalır. Yukarıdaki örnekte temizlikten sonra önbellek 20GB'ın altında, ama en az 10GB olur. Disk dolduğunda yapılacak tek seferlik temizlik için docker prune yazısına bakın.
Sık yapılan hatalar
- RUN adımının paketleri güncellediğini sanmak: komut metni değişmedikçe önbellek kullanılır;
--no-cache-filterya da--pullile bilinçli yenileyin. - Tüm projeyi bağımlılık kurulumundan önce kopyalamak: her kod değişikliği bağımlılıkları yeniden kurdurur.
- Birden fazla imajı aynı önbellek konumuna yazmak: son yazan öncekini siler; registry'de farklı
ref, gha'da farklıscopekullanın. - Varsayılan driver'da containerd image store kapalıyken harici önbellek beklemek: bu durumda
docker-containerbuilder gerekir. - SOURCE_DATE_EPOCH'u her commit'e bağlayıp önbelleğin neden hiç tutmadığını aramak: bu belgelenmiş beklenen davranıştır.
Sık Sorulan Sorular
Dosyaya touch yapmak COPY önbelleğini bozar mı?
Hayır. Belgelere göre önbellek checksum'u hesaplanırken mtime dikkate alınmaz; yalnızca değiştirilme zamanı değişen dosyalar önbelleği geçersiz kılmaz.
mode=max her zaman daha mı iyi?
Belgeler kesin bir tercih vermez. max ara katmanları da sakladığı için daha fazla isabet sağlama olasılığı yüksektir, ama önbellek büyür ve aktarım süresi artar. Projenizde ikisini deneyip ölçmeniz önerilir.
Önbellek bulunamazsa derleme durur mu?
Registry ve local backend belgelerine göre --cache-from kaynağı yoksa içe alma adımı başarısız olur ama derleme devam eder. Compose belirtimi de desteklenmeyen önbellek türlerinin derlemeyi engellemediğini söyler.
Build önbelleği ne kadar yer kaplıyor?
docker buildx du builder'ların disk kullanımını gösterir. Varsayılan builder için sınırı defaultKeepStorage ile, diğer builder'lar için buildkitd.toml eşikleriyle belirlersiniz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Docker öğrenme rehberi · BuildKit ve Buildx · Docker prune ile disk temizleme · Docker build secrets · Build context ve .dockerignore · GitHub Actions CI/CD