SSH Login mit Public-Key-Authentifizierung einrichten

Grundlegender rsync mit ssh Befehl

rsync -avz -e ssh /lokaler/pfad/ benutzer@server:/ziel/pfad/
rsync -avzP --delete --progress -e "ssh -p 2222" /quelle/ user@server:/ziel/
rsync -avz --rsh="ssh -p 2222" /lokaler/pfad/ benutzer@server:/ziel/pfad/
Optionen+Bedeutung
-aArchiv-Modus (Behält Berechtigungen, Zeitstempel, Symlinks, etc.)
-vVerbose (zeigt Details)
-zKomprimiert während der Übertragung
--progressZeigt Fortschritt pro Datei
-P--progress + --partial (ermöglicht Wiederaufnahme)
-e sshExplizit SSH als Transport nutzen
-p 2222Anderer SSH-Port (Standard: 22)
--deleteLöscht Dateien auf dem Ziel, die lokal nicht mehr existieren
--dry-runTestlauf ohne Änderungen
--exclude="*.tmp"Bestimmte Dateien ausschließen
--include="*.txt"Bestimmte Dateien einschließen
--bwlimit=1000Bandbreite auf 1000 KB/s begrenzen

Verzeichnisse und Dateien beim rsync ausschließen

Die Option --exclude funktioniert für Dateien und Verzeichnisse auf die gleiche Weise. Du kannst sowohl einzelne Dateien als auch ganze Ordner mit derselben Syntax ausschließen.

Die allgemeine Syntax lautet:

rsync [OPTIONEN] QUELLE/ ZIEL/ --exclude=MUSTER

Dabei kann MUSTER sowohl ein Dateiname als auch ein Verzeichnisname sein.

Beispiel mit einem konkreten Verzeichnis und einer konkreten Datei

Lokaler Client                                Server
─────────────────────────────────────────────────────────────────────────────

~/Projekte/website/                           /meinserver/verzeichnis/
│
├── index.php                                 ├── index.php
├── about.php                                 ├── about.php
├── contact.php                               ├── contact.php
├── config.local.php  ──── ausgeschlossen ────✗ (nicht übertragen)
├── _archive/         ──── ausgeschlossen ────✗ (nicht übertragen)
│   ├── altes-design.css
│   └── backup-vom-januar/
├── cache/
│   └── temp/
├── css/
│   └── style.css
└── .rsync-exclude.txt

Angenommen, du möchtest von deinem lokalen Verzeichnis ~/Projekte/website auf den Server /meinserver/verzeichnis/ synchronisieren und dabei das Verzeichnis _archive sowie die Datei config.local.php ausschließen.

Mit und ohne /Schrägstrich/ am Ende

… ohne Schrägstrich kopiert rsync das gesamte Verzeichnis website inklusive seines Namens auf den Server.

----------
VARIANTE 1
----------
                                           👇
rsync -a --info=progress2 ~/Projekte/website meinserver:/verzeichnis/ --exclude=/website/_archive/ --exclude=/website/config.local.php

… mit Schrägstrich kopiert rsync nur den Inhalt von website direkt nach /verzeichnis/ auf dem Server.

----------
VARIANTE 2
----------
                                            👇
rsync -a --info=progress2 ~/Projekte/website/ meinserver:/verzeichnis/ --exclude=/_archive/ --exclude=/config.local.php

Vorsicht: Kein Schrägstrich beim Verzeichnis (Datei)

Bei der Schreibweise --exclude='_archive' oder --exclude='config.local.php' schließt rsync alle Dateien und Verzeichnisse die so heissen komplett aus.

Was ist für rsync root /

Der Pfad nach --exclude= muss immer relativ zur Wurzel der Übertragung sein.

  • Bei der ersten Variante ist ~/Projekte die Wurzel
  • bei der zweiten Variante ist ~/Projekte/website/ die Wurzel.

Mehrere Dateien und Verzeichnisse ausschließen

Du kannst den --exclude-Parameter beliebig oft wiederholen, um eine Mischung aus Dateien und Ordnern auszuschließen:

rsync -a ~/Projekte/website/ meinserver:/verzeichnis/ --exclude=/_archive/ --exclude=/config.local.php --exclude='*.tmp' --exclude=/cache/

Exclude-Liste aus einer Datei verwenden

Falls du viele Ausnahmen definieren möchtest, ist es übersichtlicher, diese in eine Datei zu schreiben und mit --exclude-from= zu verwenden:

rsync -a ~/Projekte/website/ meinserver:/verzeichnis/ --exclude-from=/pfad/zur/ausschlussliste.txt

Die Ausschlussliste könnte beispielsweise so aussehen:

/_archive/
/config.local.php
/*.tmp
/cache/
/logs/

Wenn die Datei z.B. (versteckt) .rsync-exclude.txt mit den Excludes im selben Pfad liegt, muss sie selbst natürlich auch excluded werden.

rsync -a ~/Projekte/website/ meinserver:/verzeichnis/ --exclude-from=/.rsync-exclude.txt --exclude=/.rsync-exclude.txt

Die Datei kann natürlich auch außerhalb des Verzeichnisses platziert werden.

Testlauf durchführen

Bevor du den Befehl ausführst, kannst du mit --dry-run oder -n prüfen, ob die richtigen Dateien und Verzeichnisse ausgeschlossen werden:

rsync -a --dry-run ~/Projekte/website/ meinserver:/verzeichnis/ --exclude=/_archive/ --exclude=/config.local.php

Exclude-Übersicht: Schreibweisen für Dateien & Verzeichnisse

OptionVerhaltenBeispiel für VerzeichnisBeispiel für Datei
--exclude=/pfad/zum/element/Schließt nur den exakten Pfad in der Übertragungswurzel aus.--exclude=/_archive/--exclude=/config.local.php
--exclude=nameSchließt jedes Element dieses Namens aus, wo auch immer es sich befindet.--exclude=_archive--exclude=config.local.php
--exclude=*/name/Schließt jeden Ordner name aus, der ein direktes Unterverzeichnis eines beliebigen Ordners ist.--exclude=*/_archive/nicht anwendbar
--exclude=*.endungSchließt alle Dateien mit einer bestimmten Endung aus.nicht anwendbar--exclude=*.log

Performance-Tuning

OptimierungBefehl
Schnellere Verbindung-e "ssh -c arcfour" (weniger sicher, aber schneller)
Größere Blockgröße-B 8192 (für große Dateien)
Keine Komprimierung--no-compress (bei schnellen Netzwerken)
Weniger I/O--inplace (Dateien direkt überschreiben)

Parallelisieren für viele kleine Dateien

Hierzu muss das Tool parallel installiert sein.

## Mit GNU parallel
find /daten -type f | parallel -j 4 rsync -avz {} server:/backup/{}

Restriktive Berechtigungen auf dem Server

## ~/.ssh/authorized_keys mit Einschränkungen
command="rsync --server -vlogDtprze.iLsf . /backup/",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAA... backup-user

Komplettes Backup mit Löschung (Spiegelung)

# -avzP    : Komplettes Backup 
# P        : Fortschrittsanzeige
# --delete : mit Löschung (Spiegelung)
# -e "ssh -p 2222" : anderer SSH Port
# --exclude="*.mp4" --exclude=".cache/" : Ausschluss von Dateien oder Ordnern
# --bwlimit=5000 : Limitierte Bandbreite hier 5Mbit
# --dry-run : Testlauf ... nichts passiert
# --no-compress : keine Komprimierung

rsync -avzP --delete -e "ssh -p 2222" /home/benutzer/ benutzer@server:/backup/home/

Highperformance Sync

Hier ist ein hochperformantes Beispiel für das parallele Synchronisieren vieler Dateien mit rsync:

find /daten -type f | parallel -j 8 rsync -avzP --delete -e "ssh -p 2222 -c aes128-gcm@openssh.com -o Compression=no" {} benutzer@server:/backup/{} --no-compress --bwlimit=10000

Erklärung der Parameter

  • find /daten -type f listet alle Dateien im Quellverzeichnis auf und übergibt sie an parallel
  • -j 8 bestimmt die Anzahl der parallelen Prozesse (Faustregel: 4 bis 8)
  • -c aes128-gcm@openssh.com verwendet einen schnellen Verschlüsselungsalgorithmus
  • -o Compression=no deaktiviert die SSH-Komprimierung (rsync hat bereits --no-compress)
  • --bwlimit=10000 begrenzt die Bandbreite auf 10 MB/s (Wert in KB/s)

Performance-Tipps für viele kleine Dateien

Bei sehr vielen kleinen Dateien (z.B. tausende 1-KB-Dateien) ist die Latenz der SSH-Verbindung der limitierende Faktor. In diesem Fall sind weniger parallele Prozesse sinnvoll, um den SSH-Handshake-Overhead zu reduzieren. Ich empfehle dann -j 4 oder sogar nur -j 2.

Ein weiterer Performance-Gewinn lässt sich erzielen, indem große Dateien aus der Parallelisierung ausgeschlossen werden und sie separat synchronisiert werden.

## Kleine Dateien parallel synchronisieren
find /daten -type f -size -10M | parallel -j 8 rsync -av --no-compress {} benutzer@server:/backup/{}
## Große Dateien einzeln mit Komprimierung übertragen
find /daten -type f -size +10M | parallel -j 1 rsync -avzP {} benutzer@server:/backup/{}

Komfortables rsync-ssh Script

  • Die Exclude-Datei wird automatisch von der Übertragung ausgeschlossen, damit sie nicht auf den Server kopiert wird.
  • Das Script speicherst du als rsync-parallel-backup.sh und machst es ausführbar:
chmod +x rsync-parallel-backup.sh

Vor der Ausführung passt du die Werte im Bereich “Benutzerkonfiguration” an:

VariableBedeutung
REMOTE_USERBenutzername auf dem Server
REMOTE_HOSTServer-Adresse oder Domain
SSH_PORTSSH-Port (Standard: 22)
LOCAL_SOURCELokales Quellverzeichnis
REMOTE_TARGETZielverzeichnis auf dem Server
EXCLUDE_FILEPfad zur Exclude-Datei (wird selbst ausgeschlossen)
COMPRESSIONyes oder no - bei schnellen Netzwerken no
BANDWIDTH_LIMITBandbreitenlimit in KB/s, 0 für unbegrenzt
RSYNC_EXTRA_OPTIONSZusätzliche rsync-Optionen (z.B. --chmod=755)
DRY_RUN"true" Testen - es werden keine Dateien übertragen oder gelöscht / "false" Echter Sync - Dateien werden übertragen und gelöscht

Bei DRY_RUN="true" wird die rsync-Option --dry-run hinzugefügt. Das Script zeigt dann in der Ausgabe einen deutlichen Hinweis an, dass der Testmodus aktiv ist, und gibt nach Abschluss eine entsprechende Meldung aus.

#!/bin/bash

# ============================================================
# rsync-parallel-backup.sh - Hochperformantes paralleles Backup
# ============================================================
# Author: hoergen
# Date: 20260901
# URL: https://hyperblog.de/hoergen/

# -------------------- Benutzerkonfiguration --------------------

# SSH-Verbindungsdaten
REMOTE_USER="benutzer"
REMOTE_HOST="server.domain.de"
SSH_PORT="2222"

# Verzeichnisse
LOCAL_SOURCE="/daten"
REMOTE_TARGET="/backup/"

# Exclude-Datei (Pfad zur Datei mit Ausschlusslisten)
EXCLUDE_FILE="/pfad/zur/ausschlussliste.txt"

# Performance-Einstellungen
COMPRESSION="no"            # "yes" oder "no" - bei schnellen Netzwerken "no"
BANDWIDTH_LIMIT="10000"     # KB/s, 0 = unbegrenzt

# Testmodus
DRY_RUN="false"             # "true" = Testlauf (--dry-run), "false" = echter Sync

# Zusätzliche rsync-Optionen (leer lassen wenn nicht benötigt)
RSYNC_EXTRA_OPTIONS=""

# -------------------- Ende Benutzerkonfiguration --------------------

error_exit() {
    echo "FEHLER: $1" >&2
    exit 1
}

# Prüfen, ob die lokale Quelle existiert
if [ ! -d "$LOCAL_SOURCE" ]; then
    error_exit "Lokales Quellverzeichnis '$LOCAL_SOURCE' existiert nicht."
fi

# Exclude-Optionen zusammenbauen
EXCLUDE_OPTION=""
if [ -n "$EXCLUDE_FILE" ]; then
    if [ -f "$EXCLUDE_FILE" ]; then
        EXCLUDE_OPTION="--exclude-from=$EXCLUDE_FILE --exclude=$EXCLUDE_FILE"
        echo "Verwende Exclude-Datei: $EXCLUDE_FILE"
    else
        echo "WARNUNG: Exclude-Datei '$EXCLUDE_FILE' existiert nicht. Fahre ohne Ausschlüsse fort."
    fi
fi

# SSH-Optionen
SSH_OPTIONS="-p $SSH_PORT -c aes128-gcm@openssh.com -o Compression=no -o ServerAliveInterval=60"

# rsync-Optionen zusammenbauen
RSYNC_OPTIONS="-avP --delete $RSYNC_EXTRA_OPTIONS"

if [ "$COMPRESSION" = "no" ]; then
    RSYNC_OPTIONS="$RSYNC_OPTIONS --no-compress"
fi

if [ "$BANDWIDTH_LIMIT" -gt 0 ]; then
    RSYNC_OPTIONS="$RSYNC_OPTIONS --bwlimit=$BANDWIDTH_LIMIT"
fi

if [ "$DRY_RUN" = "true" ]; then
    RSYNC_OPTIONS="$RSYNC_OPTIONS --dry-run"
fi

# Ausgabe der Konfiguration
echo "============================================================"
echo "rsync-backup.sh - Starte Backup"
echo "============================================================"
echo "Quelle:                 $LOCAL_SOURCE"
echo "Ziel:                   $REMOTE_USER@$REMOTE_HOST:$REMOTE_TARGET"
echo "SSH-Port:               $SSH_PORT"
echo "Komprimierung:          $COMPRESSION"
echo "Bandbreitenlimit:       $BANDWIDTH_LIMIT KB/s"
echo "Exclude-Datei:          ${EXCLUDE_FILE:-keine}"
echo "Testmodus (dry-run):    $DRY_RUN"
echo "============================================================"

if [ "$DRY_RUN" = "true" ]; then
    echo "ACHTUNG: Testmodus aktiv! Es werden keine Dateien übertragen oder gelöscht."
    echo "============================================================"
fi

# Bestätigung abfragen
read -p "Mit diesen Einstellungen fortfahren? (j/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Jj]$ ]]; then
    echo "Abgebrochen."
    exit 0
fi

# Startzeit messen
START_TIME=$(date +%s)
echo "Starte Synchronisation um $(date)"

# Rsync-Befehl ausführen
rsync $RSYNC_OPTIONS -e "ssh $SSH_OPTIONS" $EXCLUDE_OPTION "$LOCAL_SOURCE/" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_TARGET"

# Prüfen, ob der Befehl erfolgreich war
if [ $? -eq 0 ]; then
    END_TIME=$(date +%s)
    DURATION=$((END_TIME - START_TIME))
    echo "============================================================"
    if [ "$DRY_RUN" = "true" ]; then
        echo "Testlauf erfolgreich abgeschlossen!"
        echo "Es wurden keine Dateien übertragen oder gelöscht."
        echo "Zum Ausführen des echten Backups setze DRY_RUN='false'."
    else
        echo "Backup erfolgreich abgeschlossen!"
    fi
    echo "Dauer: $((DURATION / 60)) Minuten und $((DURATION % 60)) Sekunden"
    echo "============================================================"
else
    echo "FEHLER: Backup fehlgeschlagen!" >&2
    exit 1
fi

exit 0