Sunucu Yönetimi & Linux

    Docker Volume ile Kalıcı Veri Yönetimi

    Docker volume ve bind mount ile konteyner silinse bile veriyi koruyup yedeklemenin pratik rehberi.

    13 dk okuma Güncellendi: 10 Temmuz 2026

    Docker'ı bir süre kullanan hemen herkes aynı acıyı bir kez yaşar: docker rm ile bir konteyneri sildiğinizde ya da imajı güncellemek için konteyneri baştan oluşturduğunuzda, içindeki tüm veri de yok olur. Veritabanınızdaki kayıtlar, WordPress'e yüklediğiniz görseller, uygulamanızın ürettiği dosyalar bir anda buhar olur. Bunun sebebi bir hata değil, Docker'ın tasarım felsefesidir: konteynerler doğaları gereği geçicidir (ephemeral). Bir konteynerin yazılabilir katmanı, o konteynerin ömrüne bağlıdır ve konteyner ölünce onunla birlikte gider.

    İşte tam bu noktada volume ve bind mount devreye girer. Bunlar, veriyi konteynerin geçici yaşam döngüsünden koparıp host sistem üzerinde kalıcı hale getiren mekanizmalardır. Doğru kurulmuş bir volume yapısıyla konteyneri yüz kere silip yeniden oluşturabilir, imajı güncelleyebilir, hatta sunucuyu değiştirebilirsiniz; verileriniz olduğu gibi durmaya devam eder. Bu rehberde named volume ile bind mount arasındaki farkı, docker volume komutlarını, veritabanı verisini nasıl güvenle sakladığınızı, volume yedekleme ve geri yükleme akışını ve en çok baş ağrıtan izin sorunlarını gerçek komut örnekleriyle ele alacağız. Docker'a yeni başladıysanız önce Docker nedir rehberimize göz atmanızı öneririm; buradaki konular onun üzerine inşa ediliyor.

    Konteynerlerde Veri Neden Kaybolur?#

    Docker imajları katmanlı (layered) bir yapıdadır. Bir konteyner çalıştırdığınızda Docker, salt okunur imaj katmanlarının en üstüne ince bir yazılabilir katman ekler. Konteyner çalışırken oluşturduğu her dosya bu yazılabilir katmana yazılır. Sorun şu ki bu katman konteynere aittir; konteyneri sildiğinizde katman da silinir. Yani docker run ile başlattığınız bir MySQL konteynerine yazdığınız tüm satırlar, docker rm dediğiniz anda geri dönüşü olmayacak şekilde kaybolur.

    Bu durumu somut görmek için basit bir deney yapabilirsiniz:

    # Konteyner içinde bir dosya oluştur
    docker run --name test-kutu -it alpine sh
    # konteyner içinde:
    echo "onemli veri" > /veri.txt
    exit
    
    # Konteyneri sil ve yeniden oluştur
    docker rm test-kutu
    docker run --name test-kutu -it alpine cat /veri.txt
    # Sonuç: dosya yok, "No such file or directory"
    

    Veri kaybının önüne geçmenin yolu, kalıcı olması gereken dizinleri konteynerin yazılabilir katmanından çıkarıp host üzerinde ayrı bir yerde tutmaktır. Docker bunun için üç yöntem sunar: named volume (Docker'ın yönettiği adlandırılmış hacimler), bind mount (host üzerindeki belirli bir dizinin doğrudan bağlanması) ve tmpfs mount (yalnızca RAM'de yaşayan, kalıcı olmayan geçici alan). Kalıcı veri için ilk ikisi işimize yarar; bu rehberde ağırlıklı olarak onları konuşacağız.

    Named Volume ile Bind Mount Arasındaki Fark#

    İki mekanizma da aynı amaca hizmet eder ama farklı senaryolarda parlar. Named volume, Docker'ın kendi yönettiği bir depolama alanıdır; genellikle /var/lib/docker/volumes/ altında tutulur ama siz bu yolla doğrudan uğraşmazsınız, sadece bir isimle referans verirsiniz. Bind mount ise host üzerindeki tam bir dizin yolunu (/home/user/veriler gibi) konteyner içindeki bir yola bağlar; bu dizini siz kontrol edersiniz.

    Aradaki farkı komut düzeyinde görelim:

    # Named volume: Docker yönetir, sadece isim veririz
    docker run -d --name db -v uygulama_verisi:/var/lib/mysql mysql:8
    
    # Bind mount: host'taki tam yolu belirtiriz
    docker run -d --name web -v /home/user/site:/usr/share/nginx/html nginx
    

    Modern Docker'da her ikisi için de daha okunaklı olan --mount sözdizimi tercih edilir:

    # Named volume (--mount ile)
    docker run -d --name db \
      --mount source=uygulama_verisi,target=/var/lib/mysql \
      mysql:8
    
    # Bind mount (--mount ile)
    docker run -d --name web \
      --mount type=bind,source=/home/user/site,target=/usr/share/nginx/html \
      nginx
    

    Hangisini ne zaman kullanacağınıza karar vermek için şu karşılaştırma tablosu iyi bir pusuladır:

    ÖzellikNamed VolumeBind Mount
    YönetimDocker yönetirSiz yönetirsiniz
    TaşınabilirlikYüksek (host yolundan bağımsız)Düşük (yol host'a bağımlı)
    Yedeklemedocker run ile kolayDoğrudan dosya sistemi araçlarıyla
    PerformansGenelde idealLinux'ta hızlı, Mac/Windows'ta yavaş
    İzin yönetimiDocker düzenlerHost UID/GID ile çakışabilir
    En iyi kullanımVeritabanları, üretim verisiGeliştirme, config dosyaları, canlı kod

    Pratik kural şudur: üretimde kalıcı veri için named volume, geliştirmede kaynak kodu veya config'i canlı bağlamak için bind mount. Örneğin bir veritabanının veri dizinini named volume'da tutar, ama nginx.conf gibi elle düzenlemek istediğiniz bir yapılandırma dosyasını bind mount ile bağlarsınız. Bir sunucu üzerinde bu iki yaklaşımı sık sık aynı docker-compose.yml içinde yan yana kullanırsınız.

    docker volume Komutlarıyla Hacim Yönetimi#

    Named volume'lar Docker'ın CLI'ı üzerinden yönetilir. Günlük hayatta işinize yarayacak temel komutları görelim.

    Yeni bir volume oluşturmak:

    docker volume create uygulama_verisi
    

    Aslında çoğu zaman bunu elle yapmanıza gerek kalmaz; docker run -v uygulama_verisi:/veri dediğinizde Docker, o isimde bir volume yoksa otomatik oluşturur. Yine de production'da volume'ları açıkça oluşturmak, isimlendirme düzenini korumanız açısından iyi bir alışkanlıktır.

    Mevcut volume'ları listelemek:

    docker volume ls
    # DRIVER    VOLUME NAME
    # local     uygulama_verisi
    # local     wordpress_db
    

    Bir volume'un ayrıntılarını, özellikle host üzerindeki gerçek yolunu (Mountpoint) görmek için inspect kullanılır:

    docker volume inspect uygulama_verisi
    

    Çıktı şuna benzer:

    [
        {
            "CreatedAt": "2026-07-10T09:14:22Z",
            "Driver": "local",
            "Labels": null,
            "Mountpoint": "/var/lib/docker/volumes/uygulama_verisi/_data",
            "Name": "uygulama_verisi",
            "Options": null,
            "Scope": "local"
        }
    ]
    

    Buradaki Mountpoint, verinin fiziksel olarak durduğu yerdir. Acil durumda buraya doğrudan erişebilirsiniz ama günlük işlerde bu yolu elle kurcalamak önerilmez; Docker'ın yönetimini bozabilirsiniz.

    Kullanılmayan volume'ları temizlemek disk alanı geri kazanmak için kritik. Hiçbir konteynerin kullanmadığı (dangling) volume'ları silmek:

    docker volume prune
    

    Belirli bir volume'u silmek:

    docker volume rm eski_volume
    

    Dikkat: docker volume rm geri alınamaz ve içindeki tüm veriyi yok eder. Bir volume'u herhangi bir konteyner kullanıyorsa Docker silmeyi reddeder; önce ilgili konteyneri durdurup kaldırmanız gerekir. Bu yüzden prune komutunu üretim sunucusunda çalıştırmadan önce iki kez düşünün; yanlışlıkla hâlâ ihtiyaç duyulan bir veriyi silmek çok kolaydır.

    Veritabanı Verisini Kalıcı Tutmak#

    Volume'ların en kritik kullanım alanı veritabanlarıdır. Bir MySQL, PostgreSQL ya da MongoDB konteynerini volume olmadan çalıştırırsanız, konteyneri her yeniden oluşturmada bütün veritabanınız sıfırlanır. Her veritabanı imajının, verisini yazdığı belirli bir dizin vardır; işiniz o dizini bir volume'a bağlamaktır.

    En sık kullanılan veri dizinleri şunlardır:

    VeritabanıKalıcı tutulması gereken dizin
    MySQL / MariaDB/var/lib/mysql
    PostgreSQL/var/lib/postgresql/data
    MongoDB/data/db
    Redis (kalıcı)/data

    MySQL'i named volume ile kalıcı çalıştırmak:

    docker run -d \
      --name mysql-db \
      -e MYSQL_ROOT_PASSWORD=guclu_bir_parola \
      -e MYSQL_DATABASE=uygulama \
      -v mysql_verisi:/var/lib/mysql \
      -p 3306:3306 \
      mysql:8
    

    Artık bu konteyneri silip aynı -v mysql_verisi:/var/lib/mysql ile yeniden oluşturduğunuzda tüm veritabanı olduğu gibi geri gelir. Aynı yaklaşımı docker-compose.yml ile daha temiz biçimde ifade edebilirsiniz. Örneğin WordPress + MySQL kurulumu:

    services:
      db:
        image: mysql:8
        environment:
          MYSQL_ROOT_PASSWORD: root_parola
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wp
          MYSQL_PASSWORD: wp_parola
        volumes:
          - db_verisi:/var/lib/mysql
        restart: unless-stopped
    
      wordpress:
        image: wordpress:latest
        depends_on:
          - db
        environment:
          WORDPRESS_DB_HOST: db
          WORDPRESS_DB_USER: wp
          WORDPRESS_DB_PASSWORD: wp_parola
          WORDPRESS_DB_NAME: wordpress
        volumes:
          - wp_icerik:/var/www/html/wp-content
        ports:
          - "8080:80"
        restart: unless-stopped
    
    volumes:
      db_verisi:
      wp_icerik:
    

    Bu yapılandırmada iki ayrı volume tanımladık: veritabanı için db_verisi ve yüklenen medya/eklenti/tema için wp_icerik. Böylece hem konteynerleri hem de WordPress imajını güncelleyebilir, verinizi hiç kaybetmezsiniz. Kendi altyapınızı kurmak yerine yönetilen bir çözüm istiyorsanız WordPress hosting paketlerimiz bu tür yedekleme ve güncelleme yükünü sizin adınıza üstlenir. Docker'la üretim yükü taşıyacaksanız da tam yetkili bir VDS sunucu ya da sanal sunucu üzerinde çalışmak, kaynak izolasyonu ve kök erişimi açısından çok daha rahat bir zemin sağlar.

    Volume Yedekleme ve Geri Yükleme#

    Named volume kullanmanın güzel yanı, verinizin tek bir mantıksal birim altında toplanmasıdır; bu da yedeklemeyi basitleştirir. Klasik yöntem, geçici bir yardımcı konteyner çalıştırıp volume'u ona bağlamak ve içeriğini bir tar arşivine paketlemektir.

    Bir volume'un tamamını sıkıştırılmış arşive yedeklemek:

    docker run --rm \
      -v mysql_verisi:/veri \
      -v $(pwd):/yedek \
      alpine \
      tar czf /yedek/mysql_verisi_2026-07-10.tar.gz -C /veri .
    

    Bu komutu adım adım okuyalım: geçici bir alpine konteyneri açılıyor, yedeklenecek volume /veri olarak bağlanıyor, geçerli host dizini /yedek olarak bağlanıyor ve tar ile /veri içeriği host'taki .tar.gz dosyasına yazılıyor. --rm sayesinde iş bitince konteyner kendini siliyor. Sonuçta elinizde taşınabilir tek bir yedek dosyası kalıyor.

    Geri yükleme (restore) işlemi bunun tersidir. Önce hedef volume'u oluşturur, sonra arşivi içine açarsınız:

    docker volume create mysql_verisi
    
    docker run --rm \
      -v mysql_verisi:/veri \
      -v $(pwd):/yedek \
      alpine \
      sh -c "cd /veri && tar xzf /yedek/mysql_verisi_2026-07-10.tar.gz"
    

    Veritabanları için önemli bir not: MySQL/PostgreSQL gibi motorlar çalışırken dosyalarını doğrudan tar'lamak, yazma anında yakalanan tutarsız bir kopya üretebilir. En güvenli yöntem, veritabanı konteynerini durdurup öyle yedeklemek ya da veritabanının kendi dışa aktarma aracını kullanmaktır. MySQL için mantıksal (logical) yedek şöyle alınır:

    docker exec mysql-db \
      sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases' \
      > tam_yedek_2026-07-10.sql
    

    Geri yüklemek de aynı derecede basittir:

    docker exec -i mysql-db \
      sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' \
      < tam_yedek_2026-07-10.sql
    

    Bu iki yaklaşımı birleştirmek en sağlıklısıdır: veritabanları için mysqldump/pg_dump tipi mantıksal yedekler, medya ve dosya volume'ları için ise tar arşivleri. Aldığınız yedekleri düzenli olarak uzak bir konuma taşımayı da unutmayın; sunucunun kendisi çökerse yanındaki yedek de gider. Yedekleri başka bir sunucuya senkronlamanın en verimli yolu için rsync ile yedekleme rehberimizi okuyabilir, tüm bu süreci otomatik hale getirmek için de yedekleme hizmetimize göz atabilirsiniz. İşi tümüyle kurumsallaştıracaksanız sunucu yönetim paketleri bu döngüyü sizin adınıza kurar ve izler.

    İzin Sorunları ve Veri Kaybını Önleme#

    Volume'larla çalışırken en sık karşılaşılan hata mesajı Permission denied'dır. Bunun kaynağı, konteyner içindeki sürecin çalıştığı kullanıcı (UID/GID) ile host üzerindeki dosya sahipliğinin uyuşmamasıdır. Örneğin bir bind mount ile host'taki bir dizini bağlarsınız ama konteynerdeki uygulama www-data (UID 33) ile çalışır, host'taki dizin ise sizin kullanıcınıza (UID 1000) aittir; sonuçta uygulama dizine yazamaz.

    Sorunu görmek ve teşhis etmek için dosya sahipliğine bakabilirsiniz:

    ls -ln /home/user/site
    # örn: drwxr-xr-x 2 1000 1000 ... -> host kullanicisi
    docker exec web id
    # örn: uid=33(www-data) gid=33(www-data) -> konteyner kullanicisi
    

    Çözüm için birkaç yol vardır. En temizi, host dizininin sahipliğini konteynerin beklediği UID/GID'ye ayarlamaktır:

    # Konteynerdeki uygulama UID 33 ile calisiyorsa
    sudo chown -R 33:33 /home/user/site
    

    Alternatif olarak konteyneri belirli bir kullanıcıyla çalıştırabilirsiniz. --user bayrağı, konteyner içindeki süreci sizin belirlediğiniz UID/GID ile başlatır:

    docker run -d \
      --user 1000:1000 \
      -v /home/user/site:/app \
      benim-uygulamam
    

    Named volume'larda izin sorunu genelde daha az yaşanır çünkü Docker, volume ilk kez boş bir konteynere bağlandığında imajdaki hedef dizinin sahiplik ve izinlerini volume'a kopyalar. Bu yüzden veritabanı gibi hassas servislerde named volume tercih etmek, sizi bir dizi izin baş ağrısından kurtarır.

    Veri kaybını önlemek için birkaç kritik alışkanlık edinin. Öncelikle volume'ları asla düşünmeden prune etmeyin; kullanılmıyor görünen bir volume aslında haftada bir çalışan bir işe ait olabilir. İkincisi, üretimdeki her volume için düzenli, otomatik ve uzak konuma taşınan yedek kurun; test edilmemiş yedek, yedek sayılmaz, ara sıra geri yükleme provası yapın. Üçüncüsü, imaj güncellerken veri dizininin gerçekten volume'a bağlı olduğunu doğrulayın; yeni sürümde veri yolu değişmişse veriniz sessizce yeni bir boş katmana yazılıyor olabilir. Son olarak, kritik verilerde restart: unless-stopped gibi politikalarla servisin ayakta kalmasını sağlayın ama bunun bir yedekleme yerine geçmediğini unutmayın.

    Docker Compose ile Sürdürülebilir Bir Yapı Kurmak#

    Tek tek docker run komutlarıyla volume yönetmek küçük denemelerde iş görür ama üretimde her şeyi docker-compose.yml içinde tanımlamak çok daha sürdürülebilirdir. Compose ile hem volume'ları hem de konteynerleri versiyon kontrolüne alabilir, sunucular arasında birebir aynı kurulumu taşıyabilirsiniz.

    Aşağıdaki örnek, bir uygulama sunucusu, PostgreSQL veritabanı ve Redis önbelleğinin volume'larla nasıl birlikte tanımlandığını gösterir:

    services:
      app:
        image: kayit/uygulamam:latest
        depends_on:
          - postgres
          - redis
        volumes:
          - ./config/app.ini:/app/config.ini:ro
          - uploads:/app/uploads
        restart: unless-stopped
    
      postgres:
        image: postgres:16
        environment:
          POSTGRES_PASSWORD: guclu_parola
        volumes:
          - pg_verisi:/var/lib/postgresql/data
        restart: unless-stopped
    
      redis:
        image: redis:7
        command: redis-server --appendonly yes
        volumes:
          - redis_verisi:/data
        restart: unless-stopped
    
    volumes:
      pg_verisi:
      redis_verisi:
      uploads:
    

    Bu yapıda dikkat edilecek üç ayrıntı var. Birincisi, app.ini bir bind mount olarak ve :ro (read-only) bayrağıyla bağlandı; yapılandırma dosyasını host'ta düzenlersiniz ama konteyner ona yazamaz. İkincisi, veritabanı ve önbellek verisi named volume'larda; bunlar sunucudaki fiziksel yolu bilmeden yönetiliyor. Üçüncüsü, Redis'e appendonly yes verildi ki önbellek de disk üzerinde kalıcı olsun. Bu düzeni bir kez kurduğunuzda docker compose up -d ile başlatır, docker compose down ile durdurursunuz; down komutu volume'lara dokunmaz, veriniz güvende kalır (yalnızca docker compose down -v derseniz volume'lar da silinir, bu yüzden -v bayrağına çok dikkat edin).

    Kendi uygulamanızı bu şekilde konteynerize etmeden hazır ve tek tıkla kurulabilir servisler istiyorsanız App Center üzerinden n8n, Ghost, Nextcloud gibi uygulamaları kalıcı veri yapılandırması hazır biçimde kurabilir; daha büyük ölçekli, tam kök erişimli senaryolarda ise sunucu çözümlerimizi değerlendirebilirsiniz.

    Sıkça Sorulan Sorular#

    Named volume mu bind mount mu kullanmalıyım?#

    Genel kural olarak üretimdeki kalıcı veri (özellikle veritabanları) için named volume, geliştirme sırasında kaynak kodu ya da yapılandırma dosyalarını canlı bağlamak için bind mount tercih edin. Named volume Docker tarafından yönetildiği için taşınabilir ve izin sorunlarına daha az açıktır; bind mount ise host'taki tam yolu doğrudan gördüğünüz, dosyaları elle düzenleyebildiğiniz durumlarda avantajlıdır. Çoğu gerçek projede ikisini aynı Compose dosyasında yan yana kullanırsınız.

    docker compose down komutu verilerimi siler mi?#

    Hayır, düz docker compose down konteynerleri ve ağları kaldırır ama named volume'lara dokunmaz, verileriniz olduğu gibi kalır. Veriyi silen komut docker compose down -v (veya --volumes) bayrağıdır; bu bayrak Compose dosyasında tanımlı tüm volume'ları da yok eder. Bu yüzden üretimde -v bayrağını asla refleksle eklemeyin, ne yaptığınızdan emin olmadan kullanmayın.

    Bir volume'u iki farklı konteyner aynı anda kullanabilir mi?#

    Evet, aynı named volume birden fazla konteynere bağlanabilir ve bu bazen kullanışlıdır; örneğin bir uygulama konteyneri dosya yazarken bir yedekleme konteyneri aynı volume'u salt okunur bağlayıp yedek alabilir. Ancak veritabanı gibi tek yazıcı bekleyen servislerde aynı veri dizinine iki motorun aynı anda yazması ciddi bozulmalara yol açar. Paylaşımlı kullanımda hangi konteynerin yazma, hangisinin okuma yaptığını netleştirin ve gerekirse :ro bayrağıyla salt okunur bağlayın.

    Volume verim tam olarak sunucunun neresinde duruyor?#

    Named volume'lar varsayılan olarak /var/lib/docker/volumes/<volume_adi>/_data altında tutulur; kesin yolu docker volume inspect <volume_adi> komutunun çıktısındaki Mountpoint alanında görebilirsiniz. Yine de bu dizini elle kurcalamak, dosya eklemek veya silmek önerilmez çünkü Docker'ın yönetimini bozabilir ve tutarsızlığa yol açabilirsiniz. Veriye erişmeniz gerekiyorsa yardımcı bir konteyner çalıştırıp volume'u ona bağlayarak çalışmak daha güvenlidir.

    Çalışan bir veritabanı volume'unu doğrudan tar ile yedeklemek güvenli mi?#

    Tam güvenli değildir; MySQL ya da PostgreSQL çalışırken dosyalarını doğrudan tar'lamak, yazma anında yakalanan tutarsız bir kopya üretebilir ve geri yüklemede bozuk veri riski doğar. En sağlıklı yöntem ya konteyneri kısa süreliğine durdurup öyle arşivlemek ya da veritabanının kendi mantıksal dışa aktarma aracını (mysqldump, pg_dump) kullanmaktır. Medya ve statik dosya volume'ları için ise tar arşivi gayet güvenlidir, çünkü onlarda sürekli yazan bir motor yoktur.

    İzin (Permission denied) hatalarını nasıl çözerim?#

    Bu hata neredeyse her zaman konteyner içindeki sürecin UID/GID'si ile host'taki dosya sahipliğinin uyuşmamasından kaynaklanır. Önce docker exec <konteyner> id ile konteynerin hangi kullanıcıyla çalıştığını, ls -ln ile de host dizininin sahibini öğrenin. Ardından ya host dizininin sahipliğini chown ile konteynerin beklediği UID'ye ayarlayın ya da konteyneri --user bayrağıyla doğru kullanıcı kimliğinde başlatın. Named volume kullandığınızda bu sorun büyük ölçüde ortadan kalkar, çünkü Docker ilk bağlamada imajdaki sahiplik ve izinleri volume'a kopyalar.

    Kapanış#

    Docker'da veri kaybının panzehiri tek bir alışkanlıkta gizli: kalıcı olması gereken her dizini bilinçli olarak bir volume'a ya da bind mount'a bağlamak. Named volume ile bind mount arasındaki farkı kavradığınızda, docker volume komutlarını rahatça kullandığınızda ve veritabanı verinizi hem fiziksel hem mantıksal yedeklerle güvenceye aldığınızda, konteynerleri artık gönül rahatlığıyla silip yeniden oluşturabilirsiniz; verileriniz her seferinde yerinde durur. Buna bir de izin yönetimini ve düzenli, uzak konuma taşınan yedeklemeyi eklediğinizde üretime hazır, sürdürülebilir bir kurulum elde edersiniz.

    Bu altyapıyı sağlam bir zemin üzerinde kurmak isterseniz Clou.TR yanınızda. Tam kök erişimi ve kaynak izolasyonu isteyen Docker yükleri için VDS ve sanal sunucu paketlerimize, hazır uygulamaları kalıcı veriyle tek tıkla kurmak için App Center çözümümüze, yedekleme ve bakımı bize devretmek için yedekleme ve sunucu yönetim hizmetlerimize göz atabilirsiniz. Doğru yapılandırılmış bir volume mimarisi, huzurlu uykuların ilk şartıdır; gerisini biz hallederiz.

    DockerVolumeVeri

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.