---
name: safe-volume-migration
description: Anonyme Docker-Volumes (Hash-Namen) sicher zu Bind-Mounts migrieren — für Postgres, Redis, und andere stateful Container.
version: 1.0.0
author: Hermes Agent (from Claude handover 2026-06-04)
license: MIT
metadata:
  hermes:
    tags: [docker, volumes, migration, postgres, redis, database, backup]
    related_skills: [zimaos-administration, zimaos-quirks]
---

# Safe Volume Migration

Wenn anonyme Docker-Volumes (mit Hash-Namen wie `76778d067173e5...`) zu benannten Volumes oder Bind-Mounts migriert werden müssen — z.B. für Postgres oder Redis.

## Warnung

- Diese Prozedur ist **destruktiv für die alten Volumes**
- Führe sie nur durch wenn du die alten Volumes danach löschen willst
- Ein Fehler bedeutet **Datenverlust** — Backup ist nicht optional

## Schritt-für-Schritt

### 1. Backup erstellen

**Postgres:**
```bash
docker exec <container> pg_dumpall -U <user> > /DATA/backups/pre-migration-$(date +%Y%m%d-%H%M%S).sql
```

**Redis:**
```bash
docker exec <container> redis-cli BGSAVE
docker cp <container>:/data/dump.rdb /DATA/backups/redis-dump-$(date +%Y%m%d-%H%M%S).rdb
```

**Generisch (Volume-Kopie):**
```bash
docker run --rm -v <hash-volume>:/from -v /DATA/backups:/to alpine cp -a /from /to/volume-backup
```

### 2. Abhängige Services stoppen

Services die auf die DB zugreifen **zuerst** stoppen:
```bash
# Beispiel: Hermes Gateway nutzt Postgres
systemctl stop hermes-gateway
```

### 3. Container stoppen

```bash
docker stop <container-name>
```

### 4. Daten kopieren (mit uid/gid-Erhalt)

```bash
# Postgres-Daten aus anonymem Volume in Bind-Mount-Ziel kopieren
# WICHTIG: cp -a erhält uid/gid — Postgres braucht uid 999!
docker run --rm \
  -v <hash-volume>:/from \
  -v /DATA/AppData/<app>/data/postgres:/to \
  alpine cp -a /from/. /to/

# Redis-Daten
docker run --rm \
  -v <hash-volume>:/from \
  -v /DATA/AppData/<app>/data/redis:/to \
  alpine cp -a /from/. /to/
```

### 5. Alten Container entfernen

```bash
docker rm <container-name>
```

### 6. Neuen Compose schreiben

Compose-File mit **Bind-Mounts** statt anonymen Volumes:
```yaml
services:
  postgres:
    image: pgvector/pgvector:pg15
    container_name: <name>-db
    volumes:
      - /DATA/AppData/<app>/data/postgres:/var/lib/postgresql/data
    # ... restliche Config identisch zum Original
```

### 7. Neuen Container starten

```bash
cd /var/lib/casaos/apps/<AppName>
docker --config /tmp/docker-config compose up -d
```

### 8. Verifizieren

**Postgres:**
```bash
# Healthcheck
docker exec <container> pg_isready -U <user>

# Tabellen zählen
docker exec <container> psql -U <user> -d <db> -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';"

# Stichproben-Queries
docker exec <container> psql -U <user> -d <db> -c "SELECT * FROM <table> LIMIT 3;"
```

**Redis:**
```bash
docker exec <container> redis-cli PING
# Erwartet: PONG
```

### 9. Abhängige Services neu starten

```bash
systemctl start hermes-gateway
# Dann prüfen ob alles funktioniert (Logs checken, Health-Endpunkt aufrufen)
```

### 10. Alte Volumes löschen (ERST NACH erfolgreicher Verifikation!)

```bash
docker volume rm <hash-volume-1>
docker volume rm <hash-volume-2>
```

## Pitfalls

- **Ohne `cp -a`** (statt `cp -r`) gehen uid/gid verloren — Postgres (uid 999) kann dann nicht auf seine Daten zugreifen und startet nicht
- **Reihenfolge:** Erst den abhängigen Service stoppen, DANN den DB-Container — nicht umgekehrt
- **Nicht `docker volume rm` vor der Verifikation:** Wenn die Migration fehlschlägt, kannst du das alte Volume als Backup nutzen
- **Postgres-Version beachten:** pg_dump von neuerer Version ist nicht abwärtskompatibel. Sicherstellen dass Ziel-Container die gleiche Major-Version hat
