Docker Tutorial #7: Docker Volumes – Daten persistent speichern
Das Problem: Container sind temporär
Wenn Sie einen Container löschen, verschwinden alle Daten darin:
docker run -d --name mysql1 -e MYSQL_ROOT_PASSWORD=test mysql:8.0
# Datenbank nutzen, Daten erstellen...
docker rm -f mysql1
# Alle Daten sind weg! ❌
Lösung: Docker Volumes
Was sind Volumes?
Volumes sind Docker-verwaltete Speicherbereiche außerhalb des Container-Dateisystems. Daten in Volumes bleiben erhalten, auch wenn Container gelöscht werden.
Drei Arten von Volume-Mounts
- Named Volumes (empfohlen)
- Bind Mounts (Development)
- tmpfs Mounts (temporär)
Named Volumes
Docker verwaltet die Volumes automatisch unter /var/lib/docker/volumes/.
Volume erstellen
# Volume erstellen
docker volume create mysql-data
# Volume verwenden
docker run -d \
--name mysql \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=geheim \
mysql:8.0
Volume-Befehle
# Alle Volumes anzeigen
docker volume ls
# Volume-Details
docker volume inspect mysql-data
# Volume löschen
docker volume rm mysql-data
# Alle ungenutzten Volumes löschen
docker volume prune
Vorteile Named Volumes
✅ Docker verwaltet Speicherort
✅ Einfach zu sichern
✅ Funktioniert auf allen Plattformen
✅ Bessere Performance als Bind Mounts
✅ Kann zwischen Containern geteilt werden
Praxisbeispiel: MySQL mit persistenten Daten
# Volume erstellen
docker volume create mysql-production
# MySQL starten
docker run -d \
--name mysql-prod \
-v mysql-production:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=sicher123 \
-e MYSQL_DATABASE=meineshop \
-p 3306:3306 \
mysql:8.0
# Daten hinzufügen...
# Container löschen
docker rm -f mysql-prod
# Neuen Container mit denselben Daten
docker run -d \
--name mysql-prod-2 \
-v mysql-production:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
# Daten sind wieder da! ✅
Bind Mounts
Bind Mounts verknüpfen ein Host-Verzeichnis direkt mit einem Container-Verzeichnis. Ideal für Development.
Syntax
docker run -v /host/pfad:/container/pfad image
# Oder modern:
docker run --mount type=bind,source=/host/pfad,target=/container/pfad image
Praxisbeispiel: PHP-Entwicklung
# Aktuelles Verzeichnis mounten
docker run -d \
--name php-dev \
-v $(pwd):/var/www/html \
-p 8080:80 \
php:8.2-apache
# Code-Änderungen sind sofort sichtbar!
Read-Only Mounts
# Nur lesend mounten
docker run -v $(pwd)/config:/app/config:ro nginx
Vorteile Bind Mounts
✅ Direkter Zugriff auf Host-Dateisystem
✅ Echtzeit-Synchronisation
✅ Perfekt für Development
✅ Code-Änderungen sofort verfügbar
⚠️ Nachteile:
- Performance-Probleme auf Windows/Mac
- Pfad-Abhängigkeiten
- Sicherheitsrisiko (Container sieht Host-Dateien)
tmpfs Mounts
Temporärer Speicher im RAM. Daten verschwinden beim Container-Stopp.
docker run -d \
--name app \
--tmpfs /tmp \
--tmpfs /run \
myapp
Verwendung:
- Sensible temporäre Daten
- Session-Speicher
- Cache
Docker Compose mit Volumes
Named Volumes
version: '3.8'
services:
db:
image: mysql:8.0
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: geheim
redis:
image: redis:alpine
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
Bind Mounts für Development
version: '3.8'
services:
app:
build: .
volumes:
# Source Code
- ./src:/app/src
# Config
- ./config.json:/app/config.json:ro
# Node modules nicht überschreiben
- /app/node_modules
ports:
- "3000:3000"
Volume mit Optionen
volumes:
mysql_data:
driver: local
driver_opts:
type: none
device: /mnt/storage/mysql
o: bind
Volumes teilen zwischen Containern
# Container 1: Erstellt Daten
docker run -d \
--name producer \
-v shared-data:/data \
alpine sh -c "echo 'Hallo' > /data/nachricht.txt; sleep 3600"
# Container 2: Liest Daten
docker run --rm \
-v shared-data:/data \
alpine cat /data/nachricht.txt
# Ausgabe: Hallo ✅
Volumes sichern und wiederherstellen
Backup erstellen
# Volume sichern
docker run --rm \
-v mysql-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/mysql-backup.tar.gz /data
Backup wiederherstellen
# Neues Volume erstellen
docker volume create mysql-data-neu
# Backup einspielen
docker run --rm \
-v mysql-data-neu:/data \
-v $(pwd):/backup \
alpine sh -c "cd /data && tar xzf /backup/mysql-backup.tar.gz --strip 1"
Automatisiertes Backup-Script
#!/bin/bash
# backup-volumes.sh
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Alle Volumes sichern
for volume in $(docker volume ls -q); do
echo "Sichere $volume..."
docker run --rm \
-v $volume:/data \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/${volume}_${DATE}.tar.gz /data
done
echo "Backup abgeschlossen!"
Volume von einem Container zu anderem migrieren
# Container 1 Daten
docker run -d --name old-app -v data:/data alpine sleep 3600
# Daten zu neuem Container
docker run -d --name new-app -v data:/data alpine sleep 3600
# Daten sind verfügbar! ✅
Praktisches Beispiel: WordPress-Upgrade
version: '3.8'
services:
wordpress:
image: wordpress:6.4-php8.2
ports:
- "8080:80"
volumes:
- wp_content:/var/www/html/wp-content
- wp_uploads:/var/www/html/wp-content/uploads
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
MYSQL_ROOT_PASSWORD: root
volumes:
wp_content:
wp_uploads:
db_data:
Upgrade:
# Aktuelle Version
docker compose up -d
# Später: Neue WordPress-Version
# Ändern Sie im docker-compose.yml:
# image: wordpress:6.5-php8.3
# Upgrade durchführen
docker compose down
docker compose up -d
# Daten bleiben erhalten! ✅
Volume-Performance optimieren
Delegated Mounts (macOS/Windows)
services:
app:
volumes:
# Schneller auf macOS/Windows
- ./src:/app/src:delegated
Modi:
consistent: Sync (langsam, aber konsistent)cached: Host → Container verzögertdelegated: Container → Host verzögert (schnellste Option)
Volumes vs. Bind Mounts Performance
Linux:
- Beide ähnlich schnell
macOS/Windows:
- Named Volumes: ~5x schneller als Bind Mounts
- Für große Code-Basen: Besser Named Volumes + rsync
Externe Volumes
Volumes, die außerhalb von Compose existieren:
volumes:
shared_data:
external: true
Erstellen:
docker volume create shared_data
Volume-Treiber
NFS-Volume
volumes:
nfs_data:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.100,rw
device: ":/mnt/nfs_share"
CIFS/SMB-Volume (Windows-Share)
volumes:
smb_data:
driver: local
driver_opts:
type: cifs
o: username=user,password=pass
device: "//192.168.1.100/share"
Best Practices
1. Named Volumes für Daten
# ✅ Gut
volumes:
- db_data:/var/lib/mysql
# ❌ Schlecht (Daten gehen bei Rebuild verloren)
# Keine Volume-Definition
2. .dockerignore für Bind Mounts
node_modules
.git
*.log
.env
3. Volumes dokumentieren
volumes:
mysql_data:
# Produktions-Datenbank
# Täglich um 3 Uhr gesichert
driver: local
4. Regelmäßige Backups
# Cron-Job täglich um 3 Uhr
0 3 * * * /home/user/backup-volumes.sh
5. Volumes aufräumen
# Wöchentlich ungenutzte Volumes löschen
docker volume prune -f
Troubleshooting
Volume ist nicht leer
# Volume-Inhalt anzeigen
docker run --rm -v volume-name:/data alpine ls -la /data
Berechtigungsprobleme
# Als bestimmter User ausführen
docker run --rm \
-v $(pwd):/data \
-u $(id -u):$(id -g) \
alpine chown -R $(id -u):$(id -g) /data
Volume-Größe prüfen
# Größe aller Volumes
docker system df -v
# Einzelnes Volume
docker run --rm -v volume-name:/data alpine du -sh /data
Zusammenfassung
- Named Volumes: Für Produktions-Daten
- Bind Mounts: Für Development
- tmpfs: Für temporäre Daten
- Immer Volumes für persistente Daten
- Regelmäßige Backups durchführen
- Volume-Performance beachten

Author: Andreas Lang
Andreas Lang konzentriert sich seit zwei Jahrzehnten auf die Webentwicklung und Webdesign mit dem Schwerpunkt PHP, Laravel und Javascript und betreut seine Kunden mit Herz und Seele in allen Bereichen von Entwicklung, Design, Suchmaschinenoptimierung, IT-Recht, IT-Sicherheit etc.

