SSD Nodes Learn Hosting plans →
Rehberler Matt ConnorYazan Matt Connor · Güncellendi 2026-10-30

Docker Compose Bind Mount ve Named Volume Farkı

Docker Compose projelerinde bind mount ve named volume arasından hangisinin seçileceğini öğrenin. Veri yedekleme, dosya izinleri ve yapılandırma yönetimi detayları.

Bind mount veya named volume: kısa cevap

Docker Compose volume yapıları iki türdedir ve seçim, dosyaların sahipliğinin kime ait olduğuna göre yapılır. Kendi yazdığınız ve okuduğunuz dosyalar için (yapılandırmalar, şablonlar, statik siteler) bind mount kullanın. Uygulamanın sahip olduğu veriler için (veritabanı dosyaları, arama dizinleri, yüklenen medya) named volume kullanın. Bind mount, host üzerinde bir düzenleyici ile açabileceğiniz bir yola işaret eder. Named volume ise Docker'ın sizin için oluşturduğu ve takip ettiği bir depolama alanıdır; bu alana Docker üzerinden erişirsiniz.

Her ikisi de bir servis içerisinde aynı volumes: anahtarı altında görünür, bu yüzden birbirine karıştırılırlar. Aralarındaki fark, iki noktanın sol tarafıdır. . veya / ile başlayan bir sol taraf host yolunu belirtir, dolayısıyla bu bir bind mount'tur. Bunun dışındaki her şey bir isimdir, yani bir named volume'dur ve bu ismin en üst seviyedeki volumes: bloğunda da tanımlanmış olması gerekir.

Compose dosyasındaki iki sözdizimi

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: changeme
    volumes:
      - pgdata:/var/lib/postgresql/data
  web:
    image: nginx:1.27
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./site:/usr/share/nginx/html:ro

volumes:
  pgdata:

pgdata:/var/lib/postgresql/data bir adlandırılmış volume (named volume) türüdür. ./nginx.conf:/etc/nginx/nginx.conf bir bind mount işlemidir ve :ro bunu salt okunur (read-only) olarak bağlar; bu, bir container'ın asla üzerine yazmaması gereken yapılandırmalar için doğru varsayılandır. En üst düzeydeki volumes: girdisini unutursanız, Compose service "db" refers to undefined volume pgdata hatasıyla durur.

Servisi ayağa kaldırın ve Docker'ın neleri oluşturduğunu listeleyin:

docker compose up -d
docker volume ls

Volume'un adı pgdata değildir. Adı <project>_pgdata şeklindedir; burada proje adı, varsayılan olarak compose dosyasını içeren dizinin adından gelir. myapp adlı bir dizin, myapp_pgdata sonucunu verir. Bu durum önemlidir çünkü dizini yeniden adlandırmak size yeni ve boş bir volume oluşturur ve uygulama verilerini kaybetmiş gibi görünür. Veriler kaybolmamıştır: eski volume hala docker volume ls komutuyla listelenir. Dizin taşınabilirse, compose dosyasında name: ile ismi sabitleyin veya COMPOSE_PROJECT_NAME değişkenini ayarlayın. Bu tür ayarlar, Compose ortam dosyaları ve secret'lar ile ilgili diğer yapılandırmalarınızla birlikte tutulmalıdır.

İzin hataları neden çoğunlukla bind mount işlemlerinde ortaya çıkar

Bu, pratik açıdan en büyük farktır ve temelinde tek bir kural yatar: İlk kullanımda boş olan bir named volume, image içindeki verilerle doldurulur (seed edilir), ancak bind mount hiçbir zaman doldurulmaz.

Docker, boş bir named volume'u image içinde halihazırda içerik barındıran bir dizin üzerine bağladığında, bu içeriği image tarafından belirlenen sahiplik ve izinlerle volume içine kopyalar. Resmi Postgres image, /var/lib/postgresql/data dizinini kendi postgres kullanıcısına ait olacak şekilde sunar; bu nedenle volume, aynı sayısal kimliğe (numeric id) sahip olur ve veritabanı başlatılır.

Bu doldurma işlemi, bir named volume'un sahip olduğu tek avantajdır. Docker, volume'un nerede barındığını yönetir ancak container'lar burayı standart dosya sistemi işlemleriyle okuyup yazar; bu yüzden sahiplik ve izinler, tıpkı bir bind mount üzerinde olduğu gibi uygulanır. Image içinde bulunmayan bir yola bağlanan veya ilk kez farklı bir image ya da kullanıcı tarafından doldurulan bir named volume, image'dan herhangi bir sahiplik bilgisi almaz; bu durumda root olmayan bir süreç burada da reddedilebilir. Aşağıdaki id kontrolü her iki tür için de geçerlidir.

Bind mount ise tam tersini yapar. Host üzerinde ne varsa container onu görür; sahiplik bilgisi de buna dahildir ve o yoldaki image içeriği gizlenir. Eğer host dizini mevcut değilse, Docker daemon bunu oluşturur. Daemon root yetkileriyle çalıştığı için, root:root sahipliğinde bir dizin elde edersiniz. Root olmayan bir kullanıcıyla çalışan container süreci, bu dizine yazamaz:

PermissionError: [Errno 13] Permission denied: '/data/app.db'

Çözüm, sayıların eşleşmesini sağlamaktır. Bind mount üzerindeki sahiplik, isimle değil sayısal kullanıcı kimliği (uid) ile karşılaştırılır; çünkü container kendi /etc/passwd dosyasına sahiptir. Container içindeki app isimli bir kullanıcının host üzerinde bir anlamı yoktur. Daemon, kullanıcı isim alanı eşleme (user-namespace remapping - userns-remap, /etc/docker/daemon.json içinde veya dockerd komut satırında ayarlanır) ile çalışmadığı sürece, uid 1000 her iki tarafta da uid 1000 anlamına gelir. Eşleme, her container kimliğini host üzerinde ikincil bir aralığa kaydırır. Docker'ın 231072 ile başlayan örnek aralığında, container uid 0, host uid 231072'ye karşılık gelir; dolayısıyla container uid 1000, host uid 232072 olur.

id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./data

docker compose exec web id, container sürecinin fiilen hangi uid ile çalıştığını yazdırır. Eşleme yoksa, host dizinini bu numarayla eşleştirin veya serviste user: "1000:1000" kullanarak container'ı kendi numaranıza sabitleyin. Eşleme açıkken, user: container'ı host numaranıza sabitleyemez; çünkü belirlediğiniz kimlik de kaydırılır: user: "1000:1000", bu örnek aralıkta yine host uid 232072'ye denk gelir. Bunun yerine host dizininin sahipliğini (chown) eşlenmiş host kimliğine göre değiştirin (kendi aralığınız /etc/subuid ve /etc/subgid içindedir). Docker belgeleri, tek bir container için eşlemeyi devre dışı bırakmanın yolu olarak yalnızca --userns=host üzerinde docker container create, run veya exec seçeneklerini belirtir. Kendi yazdığınız bir uygulama için user: değerini sabitlemek daha temiz bir yöntemdir. Yazmadığınız bir image için host dizininin sahipliğini değiştirmek daha güvenlidir; çünkü bazı image'lar giriş noktasını (entrypoint) root olarak başlatır, yetkileri düşürür ve altında belirli bir sahiplik bekler.

Bilmeniz gereken iki tuzak daha vardır. Fedora, RHEL ve SELinux (security-enhanced Linux) zorlaması olan diğer sistemlerde, bir bind mount yeniden etiketlenene kadar reddedilir. Bu nedenle, container'lar arasında paylaşılan bir yol için :z veya yalnızca bir container'ın kullanması gereken bir yol için :Z (- ./data:/data:Z olarak yazılır) ekleyin. Ayrıca, bir dizin yerine tek bir dosyanın bind mount edilmesi, bir düzenleyici dosyayı yerinde yazmak yerine değiştirerek yenilediğinde bozulur; çünkü mount işlemi orijinal inode'u takip eder. Siz yeniden başlatana kadar container eski içeriği görmeye devam eder. Dosya sık düzenleniyorsa, üst dizini mount edin.

Performans: farkın gerçek olduğu yer

Linux sunucularda her iki tür de aynı çekirdek yolunu izler; bu nedenle verimlilik farkı, seçim yapmanızı gerektirecek kadar küçük değildir. Varsayılan local sürücüsünü kullanan adlandırılmış birimler (named volumes), Docker'ın geri kalanıyla aynı dosya sisteminde, /var/lib/docker/volumes/ altında yaşar; bind mount ise işaret ettiğiniz her yerde bulunabilir.

Fark, konteynerlerin bir sanal makine içinde çalıştığı macOS ve Windows için Docker Desktop üzerinde ortaya çıkar. Oradaki bir bind mount, ana makine dosya sisteminden sanal makineye bir dosya paylaşım katmanı üzerinden geçer. Node.js bağımlılık ağacı veya PHP framework önbelleği gibi çok sayıda küçük dosya işlemi içeren iş yükleri bu durumda gözle görülür biçimde yavaşlar. Adlandırılmış birimler sanal makine içinde kalır ve bu maliyete katlanmaz. Birçok geliştirme compose dosyasının kaynak dizini bind-mount etmesinin ancak node_modules üzerinde adlandırılmış bir birim tanımlamasının nedeni budur.

Diğer gerçek fark, baytların nereye yazıldığıdır. /mnt/backup konumuna yapılan bir bind mount, veriyi o diske yerleştirir. Adlandırılmış bir birim ise /var/lib/docker konumunu barındıran dosya sistemi üzerinde yer alır; bu da bir VPS üzerinde genellikle kök (root) disktir. Adlandırılmış bir birim içinde büyüyen bir veritabanı, sistem günlüklerinizin bulunduğu diski doldurur. Bir olay haline gelmeden önce kontrol edin:

docker system df -v
df -h /var/lib/docker

docker system df -v, her birimi boyutuyla listeler ve artık hiçbir konteynerin referans vermediği birimleri işaretler.

Adlandırılmış bir volume'u inceleme

Adlandırılmış bir volume kara kutu değildir. Docker'a konumunu sorun:

docker volume inspect myapp_pgdata

Mountpoint alanı, genellikle /var/lib/docker/volumes/myapp_pgdata/_data olan gerçek bir ana makine yolunu verir. Bunu sudo ls ile okuyabilirsiniz; bu, hızlı bir kontrol için yararlıdır. Burayı dosyaları düzenleyeceğiniz bir yer olarak görmeyin. root kullanıcısı olarak buraya yazmak, yukarıda açıklanan sahiplik sorununu yeniden yaratır ve bu yol, diğer volume sürücülerinin paylaşmadığı local sürücüsüne özgü bir detaydır.

İçeriye bakmanın güvenli yolu, volume'u bağlayan tek kullanımlık bir container çalıştırmaktır:

docker run --rm -v myapp_pgdata:/vol alpine ls -la /vol

Bu yöntem her sürücü için çalışır, gerçek container'ın gördüğü izinlerin aynısını görür ve --rm sayesinde geride hiçbir şey bırakmaz.

Her türün yedeklenmesi

Bind mount normal bir dizindir, bu nedenle herhangi bir dosya düzeyi yedekleme aracı bunu doğrudan işleyebilir. Yedekleme aracını ana makine yoluna yönlendirmeniz yeterlidir. Named volume ise fazladan bir adım gerektirir, çünkü aracın hacmin içine erişmesi gerekir. Hacmi ve bir ana makine dizinini aynı kısa ömürlü container içine bağlayın ve ardından bir arşiv oluşturun:

docker run --rm \
  -v myapp_pgdata:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/pgdata.tar.gz -C /data .

Geri yükleme işlemi için bu süreci tersine çevirerek yeni bir hacme aktarın:

docker volume create myapp_pgdata_restored
docker run --rm \
  -v myapp_pgdata_restored:/data \
  -v "$PWD":/backup \
  alpine tar xzf /backup/pgdata.tar.gz -C /data

tar, container içinde root olarak çalıştığında sayısal sahiplik bilgilerini korur; bu da geri yüklenen hacmin uygulama tarafından kullanılabilir durumda kalmasını sağlar.

Her iki tür için de geçerli bir uyarı mevcuttur. Veritabanı çalışırken dosyalarını kopyalamak, hareketli bir hedefin arşivini almanıza neden olur ve bu durum veritabanının bozuk bir durumda geri yüklenmesine yol açabilir. Servisi önce durdurun veya docker compose exec -T db pg_dump -U postgres appdb > appdb.sql örneğinde olduğu gibi veritabanının kendi aracıyla dump alın. Bu işlem, compose dosyalarınızla birlikte normal bir şifreli restic yedekleme rutinine dahil edebileceğiniz düz bir dosya oluşturur.

Bind mount yapısını named volume yapısına taşıma

Bu işlem bir yeniden adlandırma değil, kopyalama işlemidir ve yaklaşık bir dakika sürer.

docker compose down
docker volume create myapp_pgdata
docker run --rm \
  -v "$PWD/data":/from \
  -v myapp_pgdata:/to \
  alpine sh -c 'cp -a /from/. /to/'

cp -a komutu sahiplik, izin modları ve zaman damgalarını korur; böylece eski dizini okuyabilen container kullanıcısı yeni volume içeriğini de okumaya devam edebilir. Ardından servisi pgdata:/var/lib/postgresql/data kullanacak şekilde değiştirin, üst düzey volumes: bloğuna pgdata ekleyin, docker compose up -d komutunu çalıştırın ve eski dizini silmeden önce uygulama günlüklerini kontrol edin. İşlemi tersine çevirmek için aynı komutu /from ve /to yer değiştirmiş şekilde kullanın.

Test sırasında bir noktayı unutmayın. docker compose down komutu named volume yapılarına dokunmaz, ancak docker compose down -v projenin tanımladığı tüm named volume yapılarını siler ve bu işlemin geri dönüşü yoktur. Bind mount yapıları her iki komuttan da etkilenmez, çünkü Docker bu dizinin sahibi değildir. Yaşam döngüsü komutları sizin için yeniyse, VPS için Docker Compose temel rehberi bu süreçleri adım adım açıklar.

Servis bazlı seçim yapma

Dosyayı kimin yazdığını sorgulayın. Bir metin düzenleyicide düzenlediğiniz ve git üzerinde sürümlediğiniz yapılandırma dosyaları, görünür olmalarını ve sürüm kontrolüne tabi tutulmalarını istediğiniz için bind mount ile :ro dizinine bağlanmalıdır. Elle hiçbir zaman açmadığınız uygulama verileri ise named volume içinde tutulmalıdır; çünkü Docker, izinleri imajdan otomatik olarak ayarlar ve veriler belirli bir ana makine yoluna bağımlı kalmaz.

Karma durum ise medya dosyalarıdır. Bir fotoğraf kütüphanesi hem uygulama tarafından yazılır hem de sizin tarafınızdan yönetilir; ayrıca genellikle belirli bir disk üzerinde tutulmasını gerektirecek kadar büyüktür. Bu tür verileri ilgili disk üzerindeki bir yola bind mount ile bağlayın ve sahiplik izinlerini bir kez olacak şekilde manuel olarak yapılandırın. Kendi kendine barındırılan (self-hosted) yığınların çoğunda benimsenen yöntem budur: veritabanları ve önbellekler için named volume, yapılandırma dosyaları ve önemsediğiniz büyük dizinler için ise bind mount kullanımı. VPS üzerinde çalışan Chatwoot gibi bir destek masası uygulaması tam olarak bu yapıyı kullanır; Postgres veritabanı bir named volume içinde tutulurken, yüklenen ek dosyalar yedekleme yapabileceğiniz bir dizin yolunda saklanır.

FAQ

Bind mount ile named volume arasındaki fark nedir?

Bind mount, host üzerindeki bir yolu container içine eşler; böylece her iki taraf da aynı dizini görür ve normal araçlarla düzenleme yapabilirsiniz. Named volume ise Docker tarafından oluşturulan ve yönetilen, isimle referans verilen ve en üst düzey volumes: bloğunda tanımlanan bir depolama alanıdır. Pratik ayrım sahiplik üzerinedir: yönettiğiniz yapılandırmalar için bind mount, uygulamanın yönettiği veriler için named volume kullanın.

Neden named volume ile değil de bind mount ile "permission denied" hatası alıyorum?

Boş bir named volume, imajdan verilerle doldurulur; bu nedenle genellikle imajın belirlediği sahipliği devralır ve container kullanıcısı buraya yazabilir. Yine de izinleri zorunlu tutar, bu yüzden imajın o yolda hiçbir içeriği yoksa aynı şekilde başarısız olabilir. Bind mount, host dizinini olduğu gibi gösterir ve eğer Docker bu dizini oluşturmak zorunda kaldıysa, onu root sahipliğinde oluşturur. Container'ın kullandığı sayısal kimliği görmek için docker compose exec <service> id komutunu çalıştırın, ardından host dizinini sudo chown -R <uid>:<gid> yapın veya daemon user-namespace remapping kullanmıyorsa serviste user: "1000:1000" ayarını yapın. Eğer daemon user-namespace remapping kullanıyorsa, bunun yerine eşlenmiş host kimliğine chown yapın; çünkü container kimlikleri host üzerinde kaydırılmıştır ve user: bunu değiştirmez.

Docker named volume'ları diskte nerede saklar?

Varsayılan local sürücüsü ile bunlar /var/lib/docker/volumes/<volume>/_data altında yaşar ve docker volume inspect <volume> tam Mountpoint yolunu yazdırır. Bir şeyi kontrol etmeniz gerekiyorsa burayı okuyun, ancak yalnızca bir container aracılığıyla yazın; çünkü host üzerinde root olarak düzenleme yapmak, sahipliği container'ın beklemediği şekilde değiştirir.

Bir named volume'u nasıl yedeklerim?

Volume'u ve bir host dizinini mount edilmiş kısa ömürlü bir container çalıştırın, ardından docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . ile birinden diğerine arşivleyin. Veritabanı için, canlı dosyaları kopyalamak yerine veritabanının kendi aracıyla dump alın; çünkü yazma işlemleri devam ederken alınan bir dosya kopyası, geri yükleme sırasında bozuk bir duruma yol açabilir.

docker compose down komutu volume'larımı siler mi?

docker compose down container'ları ve ağları kaldırır, named volume'ları yerinde bırakır. docker compose down -v ise projenin tanımladığı her named volume'u kalıcı olarak siler. Bind mount'lar hiçbir komutla silinmez; çünkü o dizin Docker'a değil, host'a aittir.

Bu yazı işinize yaradı mı?