Deployment und Backup mit aws s3 sync, ohne sich das --delete-Flag zum Verhängnis werden zu lassen
aws s3 sync überträgt nur geänderte Dateien zwischen einem lokalen Verzeichnis und einem S3-Bucket und ist damit das Standardwerkzeug für Deployment- und Backup-Skripte in Bash. Richtig eingesetzt spart es Zeit und Bandbreite, falsch eingesetzt löscht das --delete-Flag ganze Bucket-Inhalte in Sekunden. Dieser Artikel zeigt die Muster, die den Unterschied machen.
Inhaltsverzeichnis
- 1. Warum aws s3 sync statt aws s3 cp für Deployment und Backup
- 2. Grundsyntax und Funktionsweise: lokal zu S3 und S3 zu lokal
- 3. Deployment-Workflow: Build-Ordner nach S3 syncen
- 4. Risiken des --delete-Flags und wie man sie eingrenzt
- 5. Dry-Run vor dem echten Sync: --dryrun einsetzen
- 6. Backup-Workflow: Sync mit Exclude-Mustern und ohne --delete
- 7. Fehlerbehandlung bei Netzwerkabbrüchen und unterbrochenen Syncs
- 8. Logging und Benachrichtigung bei Sync-Fehlern
- 9. S3-Sync in CI/CD-Pipelines automatisieren
- 10. Zusammenfassung
- 11. FAQ
1. Warum aws s3 sync statt aws s3 cp für Deployment und Backup
aws s3 cp kopiert eine einzelne Datei oder mit --recursive ein ganzes Verzeichnis, überträgt dabei aber jedes Mal alle Dateien neu, unabhängig davon, ob sie sich seit dem letzten Lauf geändert haben. Bei einem Build-Ordner mit tausenden statischen Assets bedeutet das unnötige Bandbreite und Laufzeit, obwohl sich zwischen zwei Deployments oft nur eine Handvoll Dateien tatsächlich geändert hat.
aws s3 sync vergleicht stattdessen Quelle und Ziel anhand von Dateigröße und Änderungsdatum, in manchen Fällen zusätzlich anhand der ETag-Prüfsumme, und überträgt ausschließlich Dateien, die neu sind oder sich unterscheiden. Für Deployment-Skripte, die einen Build-Ordner regelmäßig auf S3 spiegeln, und für Backup-Skripte, die ein Verzeichnis inkrementell sichern, ist das der entscheidende Unterschied zwischen einem Sync-Lauf von Sekunden und einem von Minuten.
2. Grundsyntax und Funktionsweise: lokal zu S3 und S3 zu lokal
Die Syntax von aws s3 sync ist symmetrisch: aws s3 sync quelle ziel funktioniert sowohl von lokal nach S3 als auch umgekehrt von S3 nach lokal, und sogar zwischen zwei S3-Buckets, indem beide Argumente mit s3:// beginnen. Diese Symmetrie macht dasselbe Grundmuster für Deployment (lokal zu S3), Restore (S3 zu lokal) und Cross-Region-Replikation (S3 zu S3) wiederverwendbar.
Standardmäßig löscht sync keine Dateien im Ziel, die in der Quelle nicht mehr existieren, es fügt nur hinzu oder überschreibt. Das macht den Grundbefehl sicher genug für die meisten Backup-Szenarien, in denen alte Versionen erhalten bleiben sollen, aber ungeeignet für ein echtes Deployment, bei dem entfernte Dateien aus dem Build-Ordner auch aus dem Bucket verschwinden sollen.
#!/usr/bin/env bash
set -euo pipefail
# Lokal -> S3 (Deployment)
aws s3 sync ./dist/ s3://mironsoft-static-assets/frontend/
# S3 -> Lokal (Restore)
aws s3 sync s3://mironsoft-static-assets/frontend/ ./dist/
# S3 -> S3 (Replikation zwischen Buckets)
aws s3 sync s3://mironsoft-static-assets/frontend/ s3://mironsoft-static-assets-eu/frontend/
3. Deployment-Workflow: Build-Ordner nach S3 syncen
Ein typischer Deployment-Workflow baut zuerst das Frontend, synct anschließend den Build-Ordner nach S3 und invalidiert danach den CloudFront-Cache, damit Nutzer nicht noch tagelang alte Assets ausgeliefert bekommen. Damit alte, nicht mehr im Build enthaltene Dateien nicht als Datenleichen im Bucket verbleiben, kommt hier tatsächlich das --delete-Flag zum Einsatz, das den Bucket-Inhalt exakt an den lokalen Build-Ordner angleicht.
Wichtig ist, --delete ausschließlich mit einem eng gefassten Zielpfad zu verwenden, der nur die Deployment-Artefakte enthält, niemals mit einem Bucket-Root, unter dem auch andere, unabhängige Daten liegen könnten. Ein falsch gesetztes Präfix mit --delete kann sonst Daten löschen, die mit dem eigentlichen Deployment gar nichts zu tun haben.
#!/usr/bin/env bash
set -euo pipefail
readonly BUCKET="s3://mironsoft-static-assets/frontend/"
readonly DISTRIBUTION_ID="E1EXAMPLE12345"
npm run build
aws s3 sync ./dist/ "$BUCKET" \
--delete \
--cache-control "public, max-age=31536000, immutable" \
--exclude "*.html" \
--exclude "*.map"
# HTML-Dateien separat mit kurzem Cache-Header syncen
aws s3 sync ./dist/ "$BUCKET" \
--exclude "*" --include "*.html" \
--cache-control "public, max-age=60"
aws cloudfront create-invalidation \
--distribution-id "$DISTRIBUTION_ID" \
--paths "/*" > /dev/null
4. Risiken des --delete-Flags und wie man sie eingrenzt
Das --delete-Flag löscht im Zielverzeichnis jede Datei, die es in der Quelle nicht gibt, und genau das macht es gefährlich, sobald Quelle und Ziel nicht exakt das sind, was das Skript annimmt. Ein leerer oder falsch berechneter Quellpfad, etwa weil ein vorheriger Build-Schritt fehlgeschlagen ist und der Ordner leer geblieben ist, führt in Kombination mit --delete dazu, dass sync den kompletten Zielpfad leerräumt, ohne eine Warnung auszugeben.
Eine einfache Absicherung ist, vor jedem Sync mit --delete zu prüfen, ob das Quellverzeichnis tatsächlich Dateien enthält, und den Lauf sonst mit einer klaren Fehlermeldung abzubrechen, statt stillschweigend fortzufahren. Zusätzlich schützt S3-Bucket-Versionierung davor, dass gelöschte Objekte unwiederbringlich verschwinden, weil sync --delete bei aktivierter Versionierung nur einen Löschmarker setzt und ältere Versionen erhalten bleiben.
#!/usr/bin/env bash
set -euo pipefail
readonly SRC="./dist"
readonly DEST="s3://mironsoft-static-assets/frontend/"
# Absicherung: niemals mit --delete gegen ein leeres Quellverzeichnis syncen
file_count=$(find "$SRC" -type f | wc -l)
if [[ "$file_count" -eq 0 ]]; then
echo "FEHLER: $SRC ist leer, breche Sync mit --delete ab" >&2
exit 1
fi
aws s3 sync "$SRC" "$DEST" --delete
5. Dry-Run vor dem echten Sync: --dryrun einsetzen
Das --dryrun-Flag führt exakt dieselbe Vergleichslogik wie ein echter Sync aus, gibt aber nur aus, welche Dateien hoch- oder heruntergeladen und welche gelöscht würden, ohne tatsächlich etwas zu verändern. Vor jedem Deployment-Sync mit --delete lohnt sich ein Dry-Run, dessen Ausgabe kurz überflogen wird, um sicherzustellen, dass die Anzahl der zu löschenden Dateien in einer plausiblen Größenordnung liegt.
In automatisierten Pipelines lässt sich der Dry-Run zusätzlich programmatisch auswerten: Die Anzahl der Zeilen, die mit delete: beginnen, wird gezählt und mit einem Schwellenwert verglichen. Überschreitet die Anzahl geplanter Löschungen einen sinnvollen Prozentsatz der Gesamtdateien, bricht das Skript ab und verlangt eine manuelle Bestätigung, statt den destruktiven Sync automatisch durchzuführen.
#!/usr/bin/env bash
set -euo pipefail
readonly SRC="./dist"
readonly DEST="s3://mironsoft-static-assets/frontend/"
dryrun_output=$(aws s3 sync "$SRC" "$DEST" --delete --dryrun)
delete_count=$(grep -c "^delete:" <<< "$dryrun_output" || true)
echo "$dryrun_output"
echo "Geplante Loeschungen: $delete_count"
if [[ "$delete_count" -gt 50 ]]; then
echo "FEHLER: zu viele geplante Loeschungen, breche ab" >&2
exit 1
fi
aws s3 sync "$SRC" "$DEST" --delete
6. Backup-Workflow: Sync mit Exclude-Mustern und ohne --delete
Für Backup-Zwecke ist das --delete-Flag in der Regel fehl am Platz, weil ein Backup gerade dazu dient, auch längst gelöschte oder überschriebene Dateien noch verfügbar zu haben. Ein reiner Sync ohne --delete fügt neue und geänderte Dateien hinzu, entfernt aber niemals etwas aus dem Backup-Bucket, was für die meisten Backup-Anforderungen das gewünschte Verhalten ist.
Exclude-Muster mit --exclude und --include filtern dabei unnötige Dateien wie temporäre Build-Artefakte, node_modules oder Log-Dateien aus, um Speicherplatz und Übertragungszeit zu sparen. Die Muster werden in der Reihenfolge ausgewertet, in der sie auf der Kommandozeile stehen, wobei ein späteres --include ein vorheriges --exclude für die passenden Dateien wieder aufhebt.
#!/usr/bin/env bash
set -euo pipefail
aws s3 sync /var/www/mironsoft/ s3://mironsoft-backups/www/ \
--exclude "node_modules/*" \
--exclude "*.log" \
--exclude "var/cache/*" \
--exclude "var/tmp/*"
7. Fehlerbehandlung bei Netzwerkabbrüchen und unterbrochenen Syncs
Ein Sync über tausende Dateien kann bei instabiler Netzwerkverbindung mittendrin abbrechen, wobei die AWS CLI standardmäßig einzelne fehlgeschlagene Datei-Transfers intern erneut versucht, aber nicht garantiert, dass der Gesamtlauf am Ende erfolgreich durchläuft. Der Exit-Code von aws s3 sync zeigt zuverlässig an, ob der Lauf insgesamt fehlerfrei war, und sollte in jedem produktiven Skript geprüft werden, statt stillschweigend vom Erfolg auszugehen.
Weil sync beim erneuten Aufruf ohnehin nur die noch fehlenden oder geänderten Dateien überträgt, ist ein einfacher Retry mit begrenzter Wiederholungsanzahl in den meisten Fällen ausreichend, um vorübergehende Netzwerkprobleme abzufangen, ohne komplizierte Resume-Logik selbst zu bauen. Eine kurze Pause zwischen den Versuchen verhindert dabei, dass ein anhaltendes Netzwerkproblem sofort wieder denselben Fehler produziert.
#!/usr/bin/env bash
set -uo pipefail
readonly SRC="./dist"
readonly DEST="s3://mironsoft-static-assets/frontend/"
readonly MAX_RETRIES=3
attempt=1
while (( attempt <= MAX_RETRIES )); do
echo "Sync-Versuch ${attempt}/${MAX_RETRIES} ..."
if aws s3 sync "$SRC" "$DEST" --delete; then
echo "Sync erfolgreich."
exit 0
fi
echo "Sync fehlgeschlagen, warte 10s vor erneutem Versuch." >&2
sleep 10
(( attempt++ ))
done
echo "FEHLER: Sync nach ${MAX_RETRIES} Versuchen weiterhin fehlgeschlagen." >&2
exit 1
8. Logging und Benachrichtigung bei Sync-Fehlern
Ein Deployment- oder Backup-Skript, das nachts unbeaufsichtigt per Cron läuft, braucht eine Möglichkeit, Fehler sichtbar zu machen, ohne dass jemand jeden Morgen manuell Log-Dateien durchsucht. Die Ausgabe von aws s3 sync lässt sich zusammen mit dem Exit-Code in eine Log-Datei mit Zeitstempel schreiben, während ein Fehlerfall zusätzlich über einen Webhook an Slack oder eine E-Mail-Benachrichtigung gemeldet wird.
Für regelmäßig laufende Sync-Jobs lohnt sich zusätzlich ein einfacher Heartbeat-Mechanismus, etwa ein Aufruf an einen externen Monitoring-Dienst nach jedem erfolgreichen Lauf, damit ein komplett ausbleibender Cron-Job auffällt, nicht nur ein fehlgeschlagener. Ein Sync-Skript, das seit Tagen nicht mehr läuft, verursacht sonst denselben Datenverlust wie ein fehlgeschlagener Sync, bleibt aber ohne aktives Monitoring oft deutlich länger unbemerkt.
9. S3-Sync in CI/CD-Pipelines automatisieren
In einer CI-Pipeline läuft der Sync typischerweise als letzter Schritt nach erfolgreichem Build und Test, mit Zugangsdaten, die über kurzlebige, auf den Deployment-Bucket beschränkte IAM-Rollen bereitgestellt werden, statt über langlebige, breit berechtigte Access Keys. Das begrenzt den Schaden, falls die Pipeline-Umgebung selbst kompromittiert wird, weil die verwendeten Anmeldedaten ohnehin nach kurzer Zeit ablaufen und nur für den einen Bucket gelten.
Der Dry-Run-Schwellenwert aus dem vorherigen Abschnitt lässt sich in der CI-Pipeline noch strenger fassen als lokal, etwa indem ein Sync mit mehr als einer Handvoll geplanter Löschungen automatisch pausiert und eine manuelle Freigabe im Pipeline-Tool verlangt, statt vollautomatisch durchzulaufen. Für die meisten Projekte ist dieser eine zusätzliche Prüfschritt die günstigste Versicherung gegen ein versehentlich leergeräumtes Produktions-Bucket.
| Werkzeug/Flag | Verhalten | Risiko | Empfehlung |
|---|---|---|---|
aws s3 cp --recursive |
Kopiert immer alle Dateien neu | Gering, aber langsam | Nur für einmalige, vollständige Kopien |
aws s3 sync ohne --delete |
Fügt hinzu, überschreibt, löscht nie | Gering | Standard für Backups |
aws s3 sync --delete |
Gleicht Ziel exakt an Quelle an | Hoch bei falschem Pfad | Nur mit engem Zielpfad und Dry-Run davor |
--dryrun |
Simuliert ohne Änderung | Keins | Immer vor --delete-Syncs ausführen |
| Bucket-Versionierung | Behält alte Objektversionen | Keins, erhöht Speicherkosten | Für alle produktiven Deployment-Buckets aktivieren |
Mironsoft
Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur
Shell-Skripte, die in der Produktion zuverlässig laufen?
Wir analysieren bestehende Bash-Skripte, erkennen fragile Muster und ersetzen sie durch robuste Bash-Patterns: mit vollständiger Fehlerbehandlung, Logging und sicherer Parallelisierung für euren Deployment-Stack.
Code-Review
ShellCheck-Analyse und manuelle Prüfung auf kritische Bash-Pattern-Verstöße.
Refactoring
Fehlerbehandlung, Logging und sichere Dateioperationen nachrüsten.
CI-Integration
ShellCheck und BATS in Pipelines integrieren und Regressionstests aufbauen.
10. Zusammenfassung
S3-Sync-Muster mit awscli: Das Wichtigste auf einen Blick
Grundprinzip
aws s3 sync überträgt nur geänderte Dateien anhand von Größe und Änderungsdatum, deutlich effizienter als aws s3 cp --recursive.
--delete-Risiko
Löscht alles im Ziel, was nicht in der Quelle existiert. Nur mit engem Zielpfad und geprüfter Quelle verwenden.
Dry-Run zuerst
--dryrun simuliert den Sync und zeigt geplante Löschungen, bevor tatsächlich etwas verändert wird.
Fehlerbehandlung
Exit-Code prüfen, Retry mit begrenzter Anzahl bei Netzwerkfehlern, Bucket-Versionierung als zusätzliches Sicherheitsnetz.