Volumes vs Bind Mounts vs tmpfs
Şimdiye kadar container'ları başlatıp durdurduk, network'ler kurduk, servisler arası iletişimi öğrendik. Ama hiç şunu düşündün mü: container'ı sildiğinde içindeki veriler ne oluyor?
Cevap seni üzebilir: hepsi siliniyor.
Evet, container'ın içinde bir veritabanı çalıştırdıysan, tablolar oluşturduysan, veri girdiysen — container'ı sildiğin an hepsi uçar. İşte bu derste Docker'ın en kritik konularından birini öğreneceğiz: veri kalıcılığı. Verini container'ın ömrüne bağımlı olmaktan nasıl kurtarırsın?
Veri Kaybını Gözlerimizle Görelim
Sözle anlatmak yerine hemen deneyelim. Bir PostgreSQL container'ı başlatıp içine veri yazacağız, sonra container'ı sileceğiz:
docker run -d --name mydb -e POSTGRES_PASSWORD=secret postgres:16Birkaç saniye bekleyelim ki PostgreSQL başlasın, sonra bir tablo oluşturup veri ekleyelim:
docker exec -it mydb psql -U postgres -c "
CREATE TABLE users (id SERIAL, name TEXT);
INSERT INTO users (name) VALUES ('Ahmet'), ('Elif'), ('Mehmet');
SELECT * FROM users;
" id | name
----+--------
1 | Ahmet
2 | Elif
3 | MehmetGüzel, üç kullanıcımız var. Şimdi container'ı silelim:
docker rm -f mydbVe aynı isimle yeni bir container başlatalım:
docker run -d --name mydb -e POSTGRES_PASSWORD=secret postgres:16Veriye bakalım:
docker exec -it mydb psql -U postgres -c "SELECT * FROM users;"ERROR: relation "users" does not existTablo bile yok! Üç kullanıcımız, tablomuz — hepsi gitti. Container silindiğinde içindeki her şey silinir çünkü container'ın dosya sistemi geçicidir (ephemeral).
Bu, kiralık bir daireye benzer. Taşınırken eşyalarını getirirsin, çıkarken alırsın. Ama daire yıkılırsa ve eşyaların hâlâ içindeyse — hepsi gider. Container da kiralık daire gibi: silinen container'ın içindeki veriler de silinir.
Volume ile Hayat Kurtarma
Peki bu sorunu nasıl çözeriz? Volume kullanarak. Volume, container'ın dışında yaşayan bir depolama alanıdır. Container silinse bile volume ve içindeki veri sapasağlam kalır.
Şimdi aynı senaryoyu volume ile tekrarlayalım:
docker run -d --name mydb \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16Dikkat et, -v pgdata:/var/lib/postgresql/data ekledik. Bu ne demek? "pgdata adında bir volume oluştur (veya varsa kullan) ve bunu container'ın /var/lib/postgresql/data dizinine bağla." PostgreSQL tüm verilerini bu dizine yazar, yani artık verilerimiz volume'da güvende.
Tekrar veri ekleyelim:
docker exec -it mydb psql -U postgres -c "
CREATE TABLE users (id SERIAL, name TEXT);
INSERT INTO users (name) VALUES ('Ahmet'), ('Elif'), ('Mehmet');
"Şimdi container'ı silelim:
docker rm -f mydbContainer gitti. Ama volume duruyor:
docker volume lsDRIVER VOLUME NAME
local pgdatapgdata hâlâ burada. Şimdi aynı volume ile yeni bir container başlatalım:
docker run -d --name mydb \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16Ve veriyi kontrol edelim:
docker exec -it mydb psql -U postgres -c "SELECT * FROM users;" id | name
----+--------
1 | Ahmet
2 | Elif
3 | MehmetTüm veriler yerinde! Container öldü ama veri yaşıyor. İşte volume'un büyüsü bu.
Üç Veri Saklama Yöntemi
Docker'da veri saklamanın üç farklı yolu var. Her birinin kullanım amacı farklı. Şimdi bunları tanıyalım, sonraki derslerde her birini derinlemesine inceleyeceğiz.
┌─────────────────────────────────────────────────────────────┐
│ Docker Host │
│ │
│ 1. Named Volume 2. Bind Mount 3. tmpfs │
│ Docker yönetir Sen yönetirsin RAM'de │
│ /var/lib/docker/volumes/ İstediğin bir yer Geçici │
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌──────────────┐ │
│ │ pgdata │ │ ./src:/app │ │ /tmp │ │
│ │ redis-data │ │ ./conf:/etc │ │ (RAM disk) │ │
│ └───────┬─────────┘ └───────┬────────┘ └──────┬────────┘ │
│ └────────────────────┴───────────────────┘ │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ Container │ │
│ └───────────────────┘ │
└─────────────────────────────────────────────────────────────┘1. Named Volume — Kalıcı Veri İçin
Named Volume, Docker tarafından yönetilen depolama alanıdır. Bir isim verirsin (mesela pgdata), Docker nereye koyacağını bilir (/var/lib/docker/volumes/pgdata/_data/). Sen detayla uğraşmazsın.
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16Ne zaman kullanırsın? Veritabanı verileri, upload edilen dosyalar, cache verileri — kalıcı olması gereken her şey. Production'da en çok bu yöntemi kullanacaksın.
2. Bind Mount — Geliştirme İçin
Bind mount, host'taki bir dizini doğrudan container'a bağlar. Dosyayı host'ta değiştirirsin, container'da anında yansır. Hot reload geliştirme ortamı için vazgeçilmez.
docker run -d -v $(pwd)/src:/app/src node:20-alpineNe zaman kullanırsın? Geliştirme ortamı, konfigürasyon dosyaları, init script'leri. Kod yazarken anlık değişiklikleri görmek istediğinde.
3. tmpfs — Geçici ve Hızlı
tmpfs, RAM üzerinde çalışan geçici bir depolama. Container durduğunda veriler kaybolur — ama çok hızlıdır.
docker run -d --tmpfs /app/tmp:rw,size=100m myappNe zaman kullanırsın? Geçici dosyalar, session verileri, hassas bilgiler (diske yazılmasını istemediğin şifreler, token'lar).
Bu üç yöntemin karşılaştırmasını şöyle özetleyebiliriz:
| Özellik | Named Volume | Bind Mount | tmpfs |
|---|---|---|---|
| Yöneten | Docker | Sen | Kernel |
| Konum | Docker'ın iç dizini | İstediğin yer | RAM |
| Kalıcı mı? | ✅ Evet | ✅ Evet | ❌ Hayır |
| Performans | İyi | Host FS'ye bağlı | En hızlı |
| Docker CLI ile yönetim | ✅ | ❌ | ❌ |
| En iyi kullanım | DB, kalıcı veri | Geliştirme | Geçici dosyalar |
Volume Yönetimi
Docker volume'ları yönetmek için birkaç komut var. Hepsini tanıyalım.
Volume oluşturma:
docker volume create mydataAslında bunu genelde ayrıca yapmana gerek yok — docker run -v mydata:/data dediğinde Docker otomatik oluşturur. Ama label eklemek gibi ekstra ayarlar yapmak istiyorsan önceden oluşturmak mantıklı:
docker volume create --label project=myapp --label backup=daily mydataVolume'ları listeleme:
docker volume lsDRIVER VOLUME NAME
local pgdata
local redis-data
local mydataVolume detaylarını görme:
docker volume inspect pgdata[
{
"Name": "pgdata",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/pgdata/_data",
"Labels": {},
"Scope": "local"
}
]Mountpoint alanı, Docker'ın veriyi host'ta fiziksel olarak nerede tuttuğunu gösterir. Ama bu dizine doğrudan erişmek iyi bir pratik değil — Docker CLI üzerinden yönetmek her zaman daha güvenli.
Volume silme:
docker volume rm mydataEğer volume bir container tarafından kullanılıyorsa silmenize izin vermez. Önce container'ı silmen gerekir.
Kullanılmayan volume'ları temizleme:
docker volume prune⚠️ Bu komut, hiçbir container'a bağlı olmayan tüm volume'ları siler. Production'da çalıştırmadan önce docker volume ls --filter dangling=true ile nelerin silineceğini mutlaka kontrol et.
-v vs --mount — Hangi Sözdizimi?
Docker volume'ları bağlamak için iki sözdizimi var. İkisi de aynı işi yapar ama --mount daha güvenli:
Eski sözdizimi (-v):
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16Kısa ve pratik. Ama bir tuzağı var: eğer volume adını yanlış yazarsan, Docker sessizce yeni bir volume oluşturur veya yanlış bir bind mount yapar. Hata vermez.
Modern sözdizimi (--mount):
docker run -d \
--mount type=volume,source=pgdata,target=/var/lib/postgresql/data \
postgres:16Daha uzun ama daha açık. Ve en önemlisi, yanlış yazdığında hata verir. Mesela pgdata volume'u yoksa -v sessizce oluşturur, ama --mount hata verir ve seni uyarır.
Read-only mount yapmak da basit:
# -v ile
docker run -d -v pgdata:/data:ro myapp
# --mount ile
docker run -d --mount type=volume,source=pgdata,target=/data,readonly myapp💡 İpucu: Docker Compose dosyalarında -v kısa sözdizimi yaygın ve kabul edilebilir. Ama docker run komutlarında --mount tercih et — sessiz hatalardan korunursun.
Gerçek Dünya Örnekleri
Şimdi volume'ları pratikte nasıl kullanacağımızı gerçek senaryolarla görelim.
PostgreSQL — Kalıcı Veritabanı
docker run -d \
--name postgres \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
-v pgdata:/var/lib/postgresql/data \
-v $(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql:ro \
-p 5432:5432 \
postgres:16Burada iki farklı volume kullanımı var:
pgdata:/var/lib/postgresql/data— Named volume. Veritabanı verileri burada kalıcı olarak saklanır.$(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql:ro— Bind mount, read-only. Başlangıç SQL dosyası. PostgreSQL ilk başladığında bu dizindeki SQL dosyalarını çalıştırır.
Nginx — Statik Site + Custom Config
docker run -d \
--name web \
-v $(pwd)/html:/usr/share/nginx/html:ro \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
-v nginx-logs:/var/log/nginx \
-p 80:80 \
nginx:alpineÜç volume:
HTML dosyaları — bind mount, read-only (container değiştiremez)
Nginx konfigürasyonu — bind mount, read-only
Log dosyaları — named volume (kalıcı, sonradan analiz için)
Dikkat et, konfigürasyon dosyalarını :ro ile mount ettik. Bu bir güvenlik önlemi — container'ın yanlışlıkla veya kötü niyetli olarak konfigürasyonu değiştirmesini önler.
Full-Stack Geliştirme Ortamı
# Backend — kaynak kodu bind mount, node_modules izole
docker run -d --name api \
-v $(pwd)/api/src:/app/src \
-v api-node-modules:/app/node_modules \
-w /app -p 3000:3000 \
node:20-alpine sh -c "npm install && npm run dev"
# Database — verileri named volume'da
docker run -d --name db \
-v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:16Backend'de ilginç bir pattern var: kaynak kodu bind mount ile bağlı (hot reload için) ama node_modules ayrı bir named volume'da. Neden? Çünkü host'taki node_modules ile container'daki node_modules uyumsuz olabilir — özellikle macOS/Windows'ta geliştirip Linux container'da çalıştırıyorsan.
Volume Backup — Verini Yedekle
Volume'da verin güvende — container silinse de kalır. Ama ya disk bozulursa? Ya yanlışlıkla docker volume prune çalıştırırsan? Backup stratejisi şart.
Temel backup yöntemi basit: geçici bir container oluştur, volume'u bağla, tar dosyasına sıkıştır:
docker run --rm \
-v pgdata:/source:ro \
-v $(pwd)/backups:/backup \
alpine \
tar czf /backup/pgdata-backup-$(date +%Y%m%d).tar.gz -C /source .Ne yaptık?
Geçici bir Alpine container oluşturduk (
--rmile bitince otomatik silinecek)pgdatavolume'ünü/source'a read-only bağladıkHost'taki
backups/dizinini/backup'a bağladık/source'un tüm içeriğini tar.gz dosyasına sıkıştırdık
Restore etmek de benzer:
docker volume create pgdata-restored
docker run --rm \
-v pgdata-restored:/target \
-v $(pwd)/backups:/backup:ro \
alpine \
sh -c "cd /target && tar xzf /backup/pgdata-backup-20250215.tar.gz"Volume backup konusunu bir sonraki derslerde çok daha detaylı işleyeceğiz — otomatik backup script'leri, veritabanına özel backup yöntemleri ve disaster recovery planı dahil.
Volume'lar Arası Veri Paylaşımı
Birden fazla container aynı volume'u kullanabilir. Bu güçlü ama dikkatli kullanılması gereken bir özellik:
docker volume create shared-html
# İçerik üretici — HTML dosyaları oluşturur
docker run -d --name content-gen \
-v shared-html:/output \
alpine sh -c 'while true; do
echo "<h1>Güncelleme: $(date)</h1>" > /output/index.html
sleep 60
done'
# Web sunucu — üretilen HTML'leri servis eder
docker run -d --name web \
-v shared-html:/usr/share/nginx/html:ro \
-p 8080:80 \
nginx:alpinecontent-gen her dakika HTML dosyasını günceller, web bunu servis eder. Web container'ı volume'u :ro (read-only) olarak bağlamış — sadece okuyabilir, yazamaz. Bu güvenlik açısından iyi bir pratik.
⚠️ Dikkat: Birden fazla container aynı volume'a yazarsa, veri tutarsızlığı (race condition) riski var. Özellikle veritabanı dosyalarını iki container'a aynı anda yazdırma — veri bozulur!
macOS ve Windows'ta Performans Notu
Eğer macOS veya Windows'ta geliştirme yapıyorsan, bind mount'larla performans sorunu yaşayabilirsin. Çünkü bu işletim sistemlerinde Docker bir VM içinde çalışır ve host ile container arasında dosya çevirisi yapılır.
Özellikle Node.js projelerinde node_modules dizini (onbinlerce dosya) bind mount ile bağlandığında npm install normalden 5-8 kat yavaş olabilir.
Çözüm: node_modules gibi büyük dizinleri anonymous volume ile izole et:
docker run -v $(pwd):/app -v /app/node_modules node:20-alpine npm startİkinci -v /app/node_modules bir anonymous volume oluşturur. Host'taki node_modules ile sync olmaz, container kendi node_modules'ünü kullanır — çok daha hızlı.
macOS'ta Docker Desktop ayarlarından VirtioFS seçeneğini aktif etmek de performansı önemli ölçüde artırır.
Ne Zaman Hangi Yöntemi Kullanmalıyım?
Bunu basit bir karar ağacıyla özetleyeyim:
Veritabanı verileri? → Named Volume. PostgreSQL, MySQL, MongoDB, Redis — hepsinin veri dizinini named volume'a bağla.
Geliştirme ortamında kaynak kodu? → Bind Mount. Hot reload için host'taki kodu container'a bağla.
Konfigürasyon dosyaları? → Bind Mount + Read-only. nginx.conf, postgresql.conf gibi dosyaları :ro ile mount et.
Geçici dosyalar? → tmpfs. Session verileri, geçici cache, diske yazılmaması gereken hassas veriler.
node_modules, vendor gibi büyük bağımlılık dizinleri? → Anonymous Volume veya Named Volume. Host ile sync olmasına gerek yok, container kendi kopyasını kullansın.
Bu Derste Ne Öğrendik?
Bu derste Docker'ın en kritik konularından birini öğrendik — veri kalıcılığı:
Container silindiğinde içindeki veriler kaybolur — kalıcı veri için volume kullan.
Named Volume: Docker yönetir, veritabanları ve kalıcı veri için ideal.
Bind Mount: Host dizinini bağlar, geliştirme ortamı ve konfigürasyon için ideal.
tmpfs: RAM'de, geçici ve hassas veriler için.
Volume'ları
docker volumekomutlarıyla yönet ve düzenli yedekle.Konfigürasyon dosyalarını her zaman read-only (
:ro) mount et.
Bir sonraki derste Named Volume'ları derinlemesine inceleyeceğiz — volume pre-population, container'lar arası paylaşım, Compose entegrasyonu ve ileri düzey pattern'lar.
AI Asistan
Sorularını yanıtlamaya hazır