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

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 yalnızca bind mount işlemlerinde ortaya çıkar

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

Docker, boş bir named volume'ü image içinde zaten içeriği olan bir dizinin üzerine bağladığında, o içeriği image tarafından belirlenen sahiplik ve izinlerle birlikte volume içine kopyalar. Resmi Postgres image'ı, /var/lib/postgresql/data dizinini kendi postgres kullanıcısının sahipliğinde sunar; bu nedenle volume de aynı sayısal kimliğe (numeric id) sahip olur ve veritabanı başlatılır.

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

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

Bunun çözümü, sayısal değerleri eşleştirmektir. Bind mount üzerindeki sahiplik, isimle değil sayısal kullanıcı kimliği (uid) ile karşılaştırılır; çünkü container'ın kendi /etc/passwd dosyası vardır. Container içindeki app isimli bir kullanıcı, host tarafında hiçbir anlam ifade etmez. Uid 1000, her iki tarafta da uid 1000 anlamına gelir.

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

docker compose exec web id komutu, container sürecinin fiilen hangi uid ile çalıştığını gösterir. Host dizinini bu numarayla eşleştirin veya serviste user: "1000:1000" kullanarak container'ı kendi numaranıza sabitleyin. 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 dizininde chown komutunu kullanmak daha güvenlidir; çünkü bazı image'lar entrypoint'i root olarak başlatır, yetkilerini düşürür ve alt dizinlerde belirli bir sahiplik bekler.

Bilinmesi gereken iki tuzak daha vardır. SELinux (security-enhanced Linux) uygulayan Fedora, RHEL ve benzeri sistemlerde, bind mount işlemleri yeniden etiketlenene kadar reddedilir. Bu nedenle, container'lar arasında paylaşılan bir yol için :z, yalnızca tek bir container'ın kullanması gereken bir yol için ise - ./data:/data:Z şeklinde yazılan :Z parametresini ekleyin. Ayrıca, bir dizin yerine tek bir dosyanın bind mount edilmesi, bir düzenleyici dosyayı yerinde değiştirmek yerine yenisiyle değiştirdiğinde hata verir; çü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 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ırmalar, görünür olmaları ve sürüm kontrolüne tabi tutulmaları gerektiği için :ro dizinine bağlanan bir bind mount içinde yer almalıdır. Asla elle müdahale etmediğiniz uygulama verileri ise, Docker izinleri doğru şekilde yapılandırdığı ve veriler ana makine yoluna bağımlı olmadığı için named volume içinde tutulmalıdır.

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 bilinçli olarak ayarlayın. Kendi sunucusunu barındıran çoğu yapı bu yöntemi benimser: veritabanları ve önbellekler için named volume, yapılandırmalar ve önemsediğiniz büyük dizinler için ise bind mount. 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 ekler yedekleme yapabileceğiniz bir yola kaydedilir.

FAQ

Bind mount ile named volume arasındaki fark nedir?

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

Neden bind mount kullanırken "permission denied" hatası alıyorum ama named volume kullanırken almıyorum?

Boş bir named volume, imajdan türetildiği için imajın belirlediği sahiplik haklarını devralır ve container kullanıcısı buraya yazabilir. Bind mount ise ana makine dizinini olduğu gibi gösterir; eğer Docker bu dizini oluşturmak zorunda kaldıysa, dizinin sahibi root olur. Container'ın kullandığı sayısal kimliği görmek için docker compose exec <service> id komutunu çalıştırın, ardından ana makine dizini için sudo chown -R <uid>:<gid> komutunu kullanın veya servis üzerinde user: "1000:1000" ayarını yapın.

Docker, named volume birimlerini diskte nerede saklar?

Varsayılan local sürücüsü ile bu birimler /var/lib/docker/volumes/<volume>/_data altında yaşar ve docker volume inspect <volume> komutu tam Mountpoint yolunu yazdırır. Bir şeyi kontrol etmeniz gerekirse burayı okuyun, ancak yalnızca bir container aracılığıyla yazma işlemi yapın. Çünkü ana makinede root olarak düzenleme yapmak, sahiplik bilgilerini container'ın beklemeyeceği şekilde değiştirir.

Named volume yedeğini nasıl alırım?

Hem birimin hem de bir ana makine dizininin bağlı olduğu 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şivleme yapın. Veritabanı için canlı dosyaları kopyalamak yerine veritabanının kendi aracını kullanarak 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 birimlerimi siler mi?

docker compose down komutu container'ları ve ağları kaldırır, ancak named volume birimlerini yerinde bırakır. docker compose down -v komutu ise projede tanımlanan her named volume birimini kalıcı olarak siler. Bind mount birimleri hiçbir komutla silinmez, çünkü bu dizin Docker'a değil ana makineye aittir.