Docker volume, container'ların ürettiği ve kullandığı veriyi container'ın kendisinden bağımsız olarak saklayan, Docker tarafından oluşturulup yönetilen kalıcı bir depolama alanıdır. docker volume create ile kendiniz oluşturabilir ya da bir container başlatırken adını vererek Docker'ın otomatik oluşturmasını sağlayabilirsiniz. Container silinse bile volume ve içindeki veri yerinde kalır; aynı volume'u yeni bir container'a bağladığınızda kaldığınız yerden devam edersiniz.

Bu yazıda bir Linux VPS üzerinde volume'ları günlük olarak nasıl yöneteceğinizi ayrıntılı biçimde ele alıyoruz: create, ls, inspect, rm komutları, --mount ile -v sözdizimi arasındaki fark, boş ve dolu volume davranışı, salt okunur bağlama, volume-subpath, anonim volume'lar ve Compose içindeki uzun sözdizimi. Volume'u yedeklemek ve başka sunucuya taşımak ayrı bir konu olduğu için Docker volume yedekleme ve taşıma yazısında anlatılıyor. Tek servisli bir Compose dosyasında bind mount ile named volume arasındaki temel farkı ise Docker Compose başlangıç rehberi zaten gösteriyor; burada o temelin üstüne çıkıyoruz.

Volume nedir, container katmanından farkı ne?

Bir container içinde oluşturulan her dosya varsayılan olarak, imajın salt okunur katmanlarının üstünde duran yazılabilir container katmanına yazılır. Bu katman her container'a özeldir ve container silindiğinde onunla birlikte yok olur. Ayrıca bu katmandaki veriyi host'a ya da başka bir container'a taşımak kolay değildir. Docker belgeleri bu yüzden kalıcı olması gereken verinin yazılabilir katmanın dışına, bir bağlama (mount) üzerinden yazılmasını önerir.

Docker'ın sunduğu bağlama türleri volume, bind mount, tmpfs, image mount ve (Windows'ta) named pipe'tır. Hangisini seçerseniz seçin veri container içinden aynı görünür: bir dizin ya da tek bir dosya. Fark, verinin nerede durduğu ve kimin yönettiğidir. Volume'da veri host üzerinde Docker'ın kendi depolama dizininde durur; Linux'ta bu genellikle /var/lib/docker/volumes/<ad>/_data yoludur. Bind mount ise host'taki istediğiniz bir yolu doğrudan container'a bağlar; ayrıntıları Docker bind mount rehberi yazısında bulabilirsiniz.

Performans tarafında da belirgin bir fark vardır. Yazılabilir katmana yazmak, birleşik (union) bir dosya sistemi sağlayan storage driver üzerinden geçer ve bu ek soyutlama katmanı yazma hızını düşürür. Volume ise doğrudan host dosya sistemine yazar, bu yüzden belgelere göre host dosya sistemine doğrudan erişimle aynı ham dosya performansını verir. Üstelik volume, onu kullanan container'ın boyutunu büyütmez. Storage driver'ın iç işleyişini merak ediyorsanız Docker storage driver ve overlay2 yazısına bakabilirsiniz.

Volume ne zaman doğru seçimdir?

Volume ne zaman doğru seçimdir? Yedeklemek ve taşımak bind mount'a göre daha kolaydır., Docker CLI komutları ya da Docker API ile yönetilebilir., Hem Linux hem Windows...

Belgeler volume'u, container'ların ürettiği ve kullandığı veriyi kalıcı kılmak için tercih edilen mekanizma olarak tanımlıyor. Volume'un öne çıktığı durumlar şunlardır:

  • Yedeklemek ve taşımak bind mount'a göre daha kolaydır.
  • Docker CLI komutları ya da Docker API ile yönetilebilir.
  • Hem Linux hem Windows container'larında çalışır.
  • Birden fazla container arasında daha güvenli biçimde paylaşılabilir.
  • Yeni bir volume'un içeriği bir container ya da build tarafından önceden doldurulabilir.
  • Uygulamanız yüksek performanslı disk G/Ç gerektiriyorsa uygundur; veritabanı dizinleri tipik örnektir.

Volume'un iyi bir seçim olmadığı durum da açıkça belirtilmiş: dosyalara host tarafından da erişmeniz gerekiyorsa, örneğin bir yapılandırma dosyasını sunucuda düzenleyip container'ın hemen görmesini istiyorsanız bind mount kullanın. Kalıcı olmasını istemediğiniz geçici durum verisi için ise tmpfs daha uygundur; bu türü Docker tmpfs ve image mount yazısı anlatıyor.

ÖzellikVolumeBind mounttmpfs
Verinin yeriDocker depolama diziniHost üzerinde seçtiğiniz yolHost belleği
Container silininceKalırKalırContainer durunca silinir
Host'tan doğrudan erişimDesteklenmezEvetHayır
Docker CLI ile yönetimdocker volume komutlarıYokYok
Boş hedefe ilk bağlamadaContainer içeriği volume'a kopyalanırHost içeriği hedefi örterHedef örtülür
Container'lar arası paylaşımEvetEvetHayır

Volume oluşturma, listeleme, inceleme ve silme

Bind mount'tan farklı olarak volume'u herhangi bir container'dan bağımsız olarak oluşturup yönetebilirsiniz. Temel komutlar dört tanedir:

bash
docker volume create my-vol
docker volume ls
docker volume inspect my-vol
docker volume rm my-vol

docker volume inspect çıktısı volume'un sürücüsünü, etiketlerini, host üzerindeki bağlama noktasını ve kapsamını gösterir. Belgelerdeki örnek çıktı şöyledir:

bash
docker volume inspect my-vol
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

Burada Driver alanı local görünüyor; ek bir sürücü belirtmediğinizde Docker yerel sürücüyü kullanır. Mountpoint değeri verinin host üzerindeki yeridir, ancak yukarıdaki uyarıda belirttiğimiz gibi bu dizine doğrudan dokunmamalısınız. docker volume create komutu -d (--driver) ile sürücü, -o (--opt) ile sürücüye özel seçenek ve --label ile etiket alabilir. Etiketler özellikle temizlik sırasında işe yarar, çünkü docker volume prune filtreleri etiketlere göre çalışabilir. Listeleri biçimlendirmek ve süzmek için kullanılan --format ve --filter ayrıntıları Docker format ve filter kullanımı yazısında.

--mount ile -v arasındaki fark

docker run ile volume bağlamanın iki yolu vardır: --mount ve --volume (kısaca -v). İkisi de aynı volume'u bağlayabilir, ama belgeler genel olarak --mount kullanılmasını öneriyor. Sebebi, --mount sözdiziminin daha açık olması ve tüm seçenekleri desteklemesidir.

bash
docker run --mount type=volume,src=<volume-name>,dst=<mount-path>
docker run --volume <volume-name>:<mount-path>

--mount virgülle ayrılmış anahtar=değer çiftlerinden oluşur ve anahtarların sırası önemli değildir. -v ise iki nokta üst üste ile ayrılmış üç alandan oluşur ve bu alanların sırası doğru olmak zorundadır: önce volume adı, sonra container içindeki yol, isteğe bağlı olarak da seçenekler. Anonim volume'da ilk alan yazılmaz. Aşağıdaki üç durumda --mount zorunludur:

  • Volume sürücüsüne seçenek geçmek istiyorsanız (örneğin NFS ayarları).
  • Volume'un yalnızca bir alt dizinini bağlamak istiyorsanız (volume-subpath).
  • Volume'u bir Swarm servisine bağlıyorsanız.
--mount seçeneğiAnlamı
source, srcNamed volume için volume adı; anonim volume için yazılmaz
destination, dst, targetContainer içinde bağlanacağı yol
volume-subpathVolume içindeki, önceden var olması gereken alt dizin
readonly, roSalt okunur bağlar
volume-nocopyVolume boşsa hedefteki veriyi volume'a kopyalamaz
volume-optSürücü seçeneği; birden çok kez yazılabilir

-v için geçerli seçenekler ise yalnızca ro (readonly) ve volume-nocopy'dir. Belgelerdeki iki örnek aynı volume'u iki sözdizimiyle salt okunur bağlar; birincisi ayrıca volume içindeki /foo alt dizinini seçer:

bash
docker run --mount type=volume,src=myvolume,dst=/data,ro,volume-subpath=/foo
docker run -v myvolume:/data:ro

Container başlatırken volume bağlamak

Container başlatırken volume bağlamak

Henüz var olmayan bir volume adıyla container başlatırsanız Docker volume'u sizin için oluşturur. Belgelerdeki örnek myvol2 adlı volume'u Nginx container'ında /app dizinine bağlar. Aşağıdaki iki komut aynı sonucu verir; ikisini art arda çalıştırmak için önce ilk container'ı ve volume'u silmeniz gerekir:

bash
docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest

docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

Bağlamanın doğru oluştuğunu docker inspect devtest çıktısının Mounts bölümünden kontrol edebilirsiniz. Belgelerdeki çıktıda türün volume, kaynağın volume dizini, hedefin /app olduğu ve RW alanının true yani okuma yazma olduğu görülür:

json
"Mounts": [
    {
        "Type": "volume",
        "Name": "myvol2",
        "Source": "/var/lib/docker/volumes/myvol2/_data",
        "Destination": "/app",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],

Container'ı durdurup silmek volume'u silmez; volume silme her zaman ayrı bir adımdır. Bu, sunucuda en sık karşılaşılan sürprizlerden biridir: container'ları yeniden oluştururken veriniz korunur, ama artık kullanmadığınız volume'lar da siz silene kadar diskte yer kaplamaya devam eder.

bash
docker container stop devtest
docker container rm devtest
docker volume rm myvol2

Boş ve dolu volume davranışı: veri neden kayboldu gibi görünür?

Volume'ların en çok kafa karıştıran özelliği, bağlandığı dizinde zaten dosya bulunan durumlardaki davranışıdır. Belgeler iki ayrı durumu tanımlıyor ve bu ikisini karıştırmak, "imajdaki dosyalarım nereye gitti" sorusunun ana kaynağıdır.

Birinci durum: içi dolu bir volume'u, container içinde zaten dosya bulunan bir dizine bağlarsanız dizindeki mevcut dosyalar bağlama tarafından örtülür. Bu, Linux'ta /mnt içine dosya kaydedip ardından aynı yere bir USB disk bağlamaya benzer; disk çıkarılana kadar eski dosyalar görünmez. Container'larda bir bağlamayı kaldırıp örtülen dosyaları yeniden göstermenin kolay bir yolu yoktur; en iyi seçenek container'ı bağlama olmadan yeniden oluşturmaktır.

İkinci durum: boş bir volume'u dosya bulunan bir dizine bağlarsanız, o dizindeki dosya ve klasörler varsayılan olarak volume'a kopyalanır. Var olmayan bir volume adı verdiğinizde Docker boş bir volume oluşturduğu için aynı kopyalama burada da gerçekleşir. Bu davranış, başka bir container'ın ihtiyaç duyduğu veriyi önceden doldurmanın pratik bir yoludur. Belgelerdeki örnek, Nginx'in varsayılan HTML içeriğini yeni nginx-vol volume'una taşır:

bash
docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest

Bu komuttan sonra nginx-vol, imajdaki /usr/share/nginx/html içeriğini barındırır ve bu volume'u bağlayan diğer container'lar da aynı içeriği görür. Buradaki kritik nokta şudur: kopyalama yalnızca volume boşken yapılır. İmajın yeni bir sürümünü çektiğinizde volume zaten dolu olduğu için yeni imajdaki dosyalar volume'a kopyalanmaz ve container eski içeriği görmeye devam eder. Uygulama kodunu volume'a koyup sonra imajı güncellediğiniz halde değişikliği göremiyorsanız sebep çoğunlukla budur.

Kopyalamayı istemiyorsanız volume-nocopy seçeneğini kullanın. Bu seçenekle hedef dizindeki veri, volume boş olsa bile volume'a aktarılmaz. Compose tarafındaki karşılığı volume.nocopy alanıdır; aşağıdaki Compose bölümünde örneğini göreceksiniz.

Salt okunur volume

Bazı container'ların volume'a yazması gerekir, bazılarının yalnızca okuması yeterlidir. Aynı volume'u birden çok container'a bağlayabilir, bir kısmında okuma yazma, bir kısmında salt okunur kullanabilirsiniz. Salt okunur bağlama için --mount tarafında readonly, -v tarafında :ro eklenir:

bash
docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
  nginx:latest

docker inspect nginxtest çıktısındaki Mounts bölümünde bu kez RW alanı false görünür. Bir sunucuda bu ayrım özellikle raporlama, yedekleme ya da yalnızca statik dosya sunan yardımcı container'lar için faydalıdır: veriyi yazan tek bir servis olur, diğerleri yanlışlıkla bile olsa veriyi değiştiremez.

volume-subpath ile alt dizin bağlamak

volume-subpath, volume'un tamamı yerine yalnızca bir alt dizinini container'a bağlamanızı sağlar. Belgelerin verdiği senaryo, birden fazla container'ın loglarını tek bir paylaşılan volume'da ama ayrı alt dizinlerde tutmaktır. Önemli kural: belirttiğiniz alt dizin, volume container'a bağlanmadan önce volume içinde var olmalıdır; yoksa bağlama başarısız olur. Bu yüzden örnek, alt dizinleri önce geçici bir Alpine container'ıyla oluşturur:

bash
docker volume create logs
docker run --rm \
  --mount src=logs,dst=/logs \
  alpine mkdir -p /logs/app1 /logs/app2
docker run -d \
  --name=app1 \
  --mount src=logs,dst=/var/log/app1,volume-subpath=app1 \
  app1:latest
docker run -d \
  --name=app2 \
  --mount src=logs,dst=/var/log/app2,volume-subpath=app2 \
  app2:latest

Bu kurulumda her container loglarını logs volume'unun kendi alt dizinine yazar ve diğer container'ın loglarına erişemez. volume-subpath yalnızca --mount ile kullanılabilir. Engine 29.5.0 sürüm notları, bir volume alt yolundaki dosyanın imajdaki mevcut bir dosyanın üzerine bağlanırken "not a directory" hatasıyla container oluşturmayı bozduğu bir sorunun düzeltildiğini kaydediyor; daha eski bir 29.x sürümündeyseniz bu davranışla karşılaşabilirsiniz.

Adlandırılmış ve anonim volume

Volume adlandırılmış ya da anonim olabilir. Anonim volume'a Docker, o host üzerinde benzersiz olması garanti edilen rastgele bir ad verir. -v /dbdata gibi yalnızca container yolunu yazdığınızda ya da --mount içinde source alanını boş bıraktığınızda anonim volume oluşur. Adlandırılmış volume gibi anonim volume da container silinince kalır; tek istisna container'ı --rm ile oluşturmanızdır. Bu durumda container'la ilişkili anonim volume'lar container çıkınca silinir, ama aynı komutta adını verdiğiniz volume'lar silinmez:

bash
docker run --rm -v /foo -v awesome:/bar busybox top

Bu komutta /foo anonim volume'u container kaldırıldığında silinir, awesome ise yerinde kalır. --rm hem ön planda hem -d ile arka planda çalışan container'larda çalışır. Bir ayrıntı: başka bir container bu volume'ları --volumes-from ile bağlamışsa volume tanımları kopyalanır ve anonim volume ilk container silindikten sonra da kalır.

Anonim volume'lar kendiliğinden yeniden kullanılmaz. Art arda oluşturduğunuz her container kendi anonim volume'unu yaratır; ikinci container birinci container'ın verisini görmez. Anonim bir volume'u paylaşmak istiyorsanız rastgele volume kimliğini kullanarak bağlamanız gerekir. Sunucuda bunun pratik sonucu şudur: bir veritabanını -v /var/lib/... gibi adsız bir bağlamayla çalıştırıp her güncellemede container'ı yeniden oluşturursanız her seferinde yeni ve boş bir anonim volume elde edersiniz. Kalıcı veri için her zaman adlandırılmış volume kullanın. Engine 29.2.0 sürüm notları ayrıca anonim salt okunur volume'lar üzerindeki bir kısıtlamanın kaldırıldığını belirtiyor.

Compose içinde volume: kısa ve uzun sözdizimi

Compose'da volume'u en üstteki volumes bölümünde tanımlar, servisin volumes alanında kullanırsınız. Bir volume'u birden çok servisin kullanabilmesi için her servise ayrı ayrı erişim vermeniz gerekir. Aşağıdaki dosyada db servisi volume'a yazar, rapor servisi aynı volume'u uzun sözdizimiyle salt okunur bağlar:

yaml
services:
  db:
    image: postgres:17
    volumes:
      - db-data:/var/lib/postgresql/data
  rapor:
    image: alpine:3.20
    command: ["sleep", "infinity"]
    volumes:
      - type: volume
        source: db-data
        target: /okuma
        read_only: true

volumes:
  db-data:

Bu dosyayı docker compose -p uygulama config ile doğruladığımızda, normalize edilmiş çıktının sonunda volume'un gerçek adı şöyle görünüyor:

bash
docker compose -p uygulama -f compose.yaml config
...
volumes:
  db-data:
    name: uygulama_db-data

Yani dosyada db-data yazsanız da Docker üzerinde oluşan volume'un adı proje adıyla öneklenir. docker volume ls çıktısında db-data yerine uygulama_db-data görmenizin sebebi budur. Projeyi başka bir klasöre taşırsanız ya da proje adını değiştirirseniz Compose farklı bir volume adı arar ve size boş bir veritabanı başlatır. docker compose up ilk çalıştırmada volume'u oluşturur, sonraki çalıştırmalarda aynı volume'u yeniden kullanır; volume'u Compose dışında elle silerseniz bir sonraki çalıştırmada yeniden oluşturulur.

Volume'u Compose dışında docker volume create ile oluşturduysanız external: true ile işaretleyin. Bu durumda Compose volume'u oluşturmaya çalışmaz, ada önek eklemez ve volume yoksa hata verir. Belgelere göre external volume'da name dışındaki tüm alanlar anlamsızdır ve Compose başka bir alan görürse dosyayı geçersiz sayar. Aşağıdaki örnek ayrıca servis tarafındaki uzun sözdiziminin volume seçeneklerini gösteriyor:

yaml
services:
  app:
    image: nginx:latest
    volumes:
      - type: volume
        source: site-data
        target: /usr/share/nginx/html
        volume:
          nocopy: true
          subpath: public

volumes:
  site-data:
    external: true

Burada nocopy: true, --mount tarafındaki volume-nocopy ile aynı işi görür; subpath: public ise volume'un yalnızca public alt dizinini bağlar. Uzun sözdiziminde ayrıca read_only alanı vardır. Volume adını ortama göre değiştirmek isterseniz name alanına değişken yazabilirsiniz; aşağıdaki dosya, aynı klasördeki .env dosyasında DATABASE_VOLUME=my_volume_001 varken doğrulandı ve config çıktısında volume adı my_volume_001 olarak çözüldü:

yaml
services:
  db:
    image: postgres:17
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:
    name: ${DATABASE_VOLUME}
    labels:
      com.example.description: "Veritabani verisi"

name alanındaki değer olduğu gibi kullanılır ve proje adıyla öneklenmez. Bu, volume adını projeden bağımsız olarak sabitlemenin en temiz yoludur. Değişkenlerin hangi sırayla çözüldüğünü Docker Compose değişken interpolasyonu yazısında bulabilirsiniz. labels ile eklediğiniz etiketler yalnızca named volume'lara uygulanır ve docker volume inspect ile görünür; Compose kendisi de com.docker.compose.project ve com.docker.compose.volume etiketlerini ekler.

Volume silme ve disk temizliği

Volume'lar asla kendiliğinden silinmez, çünkü otomatik silme veri kaybına yol açabilir. Tek bir volume'u docker volume rm ile silersiniz. Toplu temizlik için docker volume prune vardır, ama varsayılan davranışını bilmek önemlidir: belgelere göre bu komut varsayılan olarak yalnızca hiçbir container'ın kullanmadığı anonim volume'ları siler. Bir container'a bağlı olmayan adlandırılmış volume'lar için --all (-a) gerekir. Kapsamı daraltmak için etiket filtresi kullanabilirsiniz:

bash
docker volume prune
docker volume prune --filter "label!=keep"

İkinci komut keep etiketi taşımayan kullanılmayan volume'ları siler. Önemli veritabanı volume'larınıza keep gibi bir etiket koymak, ileride yapacağınız temizliklerde güvenlik payı sağlar. Disk dolması ve tüm prune komutlarının karşılaştırması için Docker prune ile disk temizleme yazısına bakın.

Sık yapılan hatalar

Sık yapılan hatalar: Proje adı değişince verinin kaybolduğunu sanmak, İmaj güncellemesinin volume'a yansımasını beklemek, Container silmenin volume'u da sildiğini düşünmek, Kalıcı...
  • Proje adı değişince verinin kaybolduğunu sanmak: Compose volume adını proje adıyla önekler. Klasör adı değiştiyse eski volume hâlâ duruyordur; docker volume ls ile kontrol edin ya da name veya external kullanın.
  • İmaj güncellemesinin volume'a yansımasını beklemek: İlk kopyalama yalnızca volume boşken olur. Uygulama dosyalarını volume'a değil imaja koyun.
  • Container silmenin volume'u da sildiğini düşünmek: Container silmek volume'u silmez; volume silme her zaman ayrı bir adımdır. --rm ile başlatılan container'larda bile adlandırılmış volume'lar kalır.
  • Kalıcı veriyi anonim volume'da tutmak: Her yeni container yeni bir anonim volume alır. Veritabanı gibi veriler için adlandırılmış volume kullanın.
  • volume-subpath alt dizinini önceden oluşturmamak: Alt dizin volume içinde yoksa bağlama başarısız olur.
  • Volume dizinini host'ta elle düzenlemek: Bu desteklenmez. Dosyaya erişmeniz gerekiyorsa bind mount kullanın ya da geçici bir container ile volume'u bağlayın.

Sık Sorulan Sorular

docker volume prune neden named volume'umu silmedi?

Çünkü güncel davranışta docker volume prune varsayılan olarak yalnızca kullanılmayan anonim volume'ları siler. Kullanılmayan adlandırılmış volume'ları da silmek için -a eklemeniz gerekir. Bu varsayılan, yanlışlıkla veri silmeyi önlemek için iyi bir korumadır.

--mount mu kullanmalıyım, -v mi?

Belgeler genel olarak --mount öneriyor, çünkü daha açık ve tüm seçenekleri destekliyor. Sürücü seçeneği, volume-subpath veya Swarm servisi gerektiğinde zaten --mount zorunludur. Kısa ve basit tek satırlık komutlarda -v da aynı sonucu verir.

Aynı volume'u iki container aynı anda kullanabilir mi?

Evet, bir volume aynı anda birden fazla container'a bağlanabilir ve bazılarında salt okunur, bazılarında okuma yazma olarak kullanılabilir. Ancak iki uygulamanın aynı dosyaya eşzamanlı yazmasının güvenli olup olmadığı uygulamanın kendisine bağlıdır; Docker bu konuda koordinasyon sağlamaz.

Volume'un diskte ne kadar yer kapladığını nasıl görürüm?

Docker'ın disk kullanımını docker system df ile izleyebilirsiniz. Volume ayrıntıları ve temizlik stratejileri Docker prune yazısında ele alınıyor.

Sunucu değiştirirken volume'u nasıl taşırım?

Volume'u geçici bir container ile tar arşivine alıp yeni sunucuda başka bir container ile açarsınız. Adım adım yöntem Docker volume yedekleme ve taşıma yazısında.

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 · Volume yedekleme ve taşıma · Bind mount · tmpfs ve image mount · Docker prune · Docker Compose rehberi