Rclone auf Cyon installieren

1. SSH Verbindung mit Account aufbauen

Bash
# public key
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJMjHBOhNS+HEpQigcFeX7avlcxhYSc4oI0P8fSIAnw michu@macbook-pro-von-michu.home

# dann unter Account > Sicherheit > SSH-Keys hinzufügen.
# dann unter Webhosting > Übersicht den Anmeldenamen (z.B ertzssk) und die IPv4 Adresse kopieren (z.B 111.111.1.111)
# Verbindung via cli aufbauen
ssh ertzssk@111.111.1.111
# dann bist du drin

2. rclone installieren

Bash
# ssh verbindung aufbauen, dann:
mkdir -p ~/bin
curl -L https://downloads.rclone.org/rclone-current-linux-amd64.zip -o /tmp/rclone.zip
unzip /tmp/rclone.zip -d /tmp/
mv /tmp/rclone-*-linux-amd64/rclone ~/bin/
chmod +x ~/bin/rclone
rclone --version

3. rclone config

Bash
# speicherorte hinzufügen
rclone config
New remote: n
name: dropbox
storage: dropbox
client_id leer
client_secret leer
advanced config: n
web browser automatically: y
# hier kommt ein fehler, da kein browser auf server
# in der fehlermeldung siehst du den lokalen link mit port z.B http://127.0.0.1:53682/auth?state=ew-6BPRILkM_ZI8LFTvxAAD
# melde dich in anderem cli tab via ssh an: ssh -L 53682:localhost:53682 xyqicuro@s132.cyon.net
# wenn verbunden, gib im browser http://127.0.0.1:53682/auth?state=ew-6BPRILkM_ZI8LFTvxAAD ein und bestätige, fertig
# dann zurück zu terminal tab 1 - access token wurde generiert
keep this dropbox remote: y
quit config: q

SSH Tunnel erklärt:

-L 53682:localhost:53682 bedeutet:

  • Port 53682 auf deinem Mac → weiterleiten an → Port 53682 auf dem cyon-Server
  • rclone wartet auf dem Server auf eine Antwort von Dropbox – aber der Server hat keinen Browser
  • Der Tunnel macht deinen Mac-Browser zum „lokalen Browser“ des Servers

4. Backup einrichten

4.1 Backup File erstellen

Bash
nano ~/backup-sitename.sh

4.2. Cyon Backup Skript erstellen

  • DB Namen aus Cyon > Datenbank > Datenbanken
  • dropbox:/PFAD BACKUPS
  • –progress zeigt in live Tab progress an
  • –stats 30s sendet stats auch an nohup.out
  • –transfers 16 = 16 gleichzeitige Dateiübertragungen (Standard: 4)
  • –checkers 32 = 32 parallele Prüfungen ob Dateien übertragen werden müssen (Standard: 8)
Bash
#!/bin/bash

set -euo pipefail

SITE="deinedomain.ch"
SITE_DIR="$HOME/public_html/$SITE"
DB_NAME="db_name"
DB_USER="db_user"
DB_PASS='db_password'
DATE=$(date +%Y-%m-%d)
TMP_DB="/tmp/${SITE}-${DATE}.sql"
REMOTE="dropbox:/$SITE"
MARKER_DIR="$HOME/.backup-markers/$SITE"
mkdir -p "$MARKER_DIR"
> ~/nohup.out #logs löschen vor start

echo "==> Config zip..."
if [[ ! -f "$MARKER_DIR/config" ]] || [[ $(find "$SITE_DIR/wp-config.php" "$SITE_DIR/.htaccess" -newer "$MARKER_DIR/config" 2>/dev/null | wc -l) -gt 0 ]]; then
  zip -j "/tmp/config.zip" "$SITE_DIR/wp-config.php" "$SITE_DIR/.htaccess"
  rclone copy "/tmp/config.zip" "$REMOTE/latest/config/"
  rm "/tmp/config.zip"
  touch "$MARKER_DIR/config"
else
  echo "    ✓ keine Änderungen, übersprungen"
fi

echo "==> Plugins zip..."
if [[ -d "$SITE_DIR/wp-content/plugins" ]]; then
  if [[ ! -f "$MARKER_DIR/plugins" ]] || [[ $(find "$SITE_DIR/wp-content/plugins" -newer "$MARKER_DIR/plugins" 2>/dev/null | wc -l) -gt 0 ]]; then
    zip -r "/tmp/plugins.zip" "$SITE_DIR/wp-content/plugins/"
    rclone copy "/tmp/plugins.zip" "$REMOTE/latest/plugins/"
    rm "/tmp/plugins.zip"
    touch "$MARKER_DIR/plugins"
  else
    echo "    ✓ keine Änderungen, übersprungen"
  fi
else
  echo "    ⚠️  plugins nicht gefunden, übersprungen"
fi

echo "==> MU-Plugins zip..."
if [[ -d "$SITE_DIR/wp-content/mu-plugins" ]]; then
  if [[ ! -f "$MARKER_DIR/mu-plugins" ]] || [[ $(find "$SITE_DIR/wp-content/mu-plugins" -newer "$MARKER_DIR/mu-plugins" 2>/dev/null | wc -l) -gt 0 ]]; then
    zip -r "/tmp/mu-plugins.zip" "$SITE_DIR/wp-content/mu-plugins/"
    rclone copy "/tmp/mu-plugins.zip" "$REMOTE/latest/mu-plugins/"
    rm "/tmp/mu-plugins.zip"
    touch "$MARKER_DIR/mu-plugins"
  else
    echo "    ✓ keine Änderungen, übersprungen"
  fi
else
  echo "    ⚠️  mu-plugins nicht gefunden, übersprungen"
fi

echo "==> Themes zip..."
if [[ -d "$SITE_DIR/wp-content/themes" ]]; then
  if [[ ! -f "$MARKER_DIR/themes" ]] || [[ $(find "$SITE_DIR/wp-content/themes" -newer "$MARKER_DIR/themes" 2>/dev/null | wc -l) -gt 0 ]]; then
    zip -r "/tmp/themes.zip" "$SITE_DIR/wp-content/themes/"
    rclone copy "/tmp/themes.zip" "$REMOTE/latest/themes/"
    rm "/tmp/themes.zip"
    touch "$MARKER_DIR/themes"
  else
    echo "    ✓ keine Änderungen, übersprungen"
  fi
else
  echo "    ⚠️  themes nicht gefunden, übersprungen"
fi

echo "==> DB Dump..."
mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$TMP_DB"
rclone sync "$TMP_DB" "$REMOTE/latest/db/" \
  --backup-dir "$REMOTE/$DATE/db/"
rm "$TMP_DB"

echo "==> Uploads zu Dropbox..."
rclone sync "$SITE_DIR/wp-content/uploads" "$REMOTE/latest/uploads/" \
  --progress \
  --stats 30s \
  --transfers 16 \
  --checkers 32

echo "==> Alte Backups löschen (>7 Tage)..."
for dir in $(rclone lsd "$REMOTE/" | awk '{print $5}' | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'); do
  if [[ "$dir" < "$(date -d '7 days ago' +%Y-%m-%d)" ]]; then
    rclone purge "$REMOTE/$dir"
  fi
done

echo "==> Fertig: $DATE"

4.3. Backup File ausführbar machen

Bash
chmod +x ~/backup-sitename.sh

# backup ausführen
~/backup-sitename.sh

5. Backup erstellen

Bash
# bei kleinen sites <2GB - cli tab offen lassen
~/backup-sitename.sh

# bei grossen sites 2GB+ - 
nohup ~/backup-sitename.sh &
  • nohup = läuft weiter nach Terminal-Schliessung
  • & = läuft im Hintergrund
  • Output landet in ~/nohup.out – kannst du mit tail -f ~/nohup.out verfolgen
Bash
tail -f ~/nohup.out
# Zeigt dir live was rclone gerade macht. Mit Ctrl+C beenden (Script läuft weiter).

6. Automatisches Backup in der Nacht um 3.00 Uhr

Bash
# in cli tab
crontab -e
Bash
# zuunterst einfügen
0 3 * * * /home/USERNAME/backup-SITENAME.sh > /home/USERNAME/backup-SITENAME.log 2>&1

7. Restore Skript erstellen

7.1 File erstellen

Bash
nano ~/restore-sitename.ch

7.2 Cyon Restore Skript erstellen

Bash
#!/bin/bash

set -euo pipefail

SITE="deinedomain.ch"
SITE_DIR="$HOME/public_html/$SITE"
DB_NAME="db_name"
DB_USER="db_user"
DB_PASS='db_password'
REMOTE="dropbox:/$SITE"

echo "==> Config wiederherstellen..."
rclone copy "$REMOTE/latest/config/config.zip" /tmp/
unzip -o /tmp/config.zip -d "$SITE_DIR/"
rm /tmp/config.zip

echo "==> Plugins wiederherstellen..."
rclone copy "$REMOTE/latest/plugins/plugins.zip" /tmp/
unzip -o /tmp/plugins.zip -d "$SITE_DIR/wp-content/"
rm /tmp/plugins.zip

echo "==> MU-Plugins wiederherstellen..."
if rclone ls "$REMOTE/latest/mu-plugins/" 2>/dev/null | grep -q "mu-plugins.zip"; then
  rclone copy "$REMOTE/latest/mu-plugins/mu-plugins.zip" /tmp/
  unzip -o /tmp/mu-plugins.zip -d "$SITE_DIR/wp-content/"
  rm /tmp/mu-plugins.zip
else
  echo "    ✓ keine mu-plugins, übersprungen"
fi

echo "==> Themes wiederherstellen..."
rclone copy "$REMOTE/latest/themes/themes.zip" /tmp/
unzip -o /tmp/themes.zip -d "$SITE_DIR/wp-content/"
rm /tmp/themes.zip

echo "==> Uploads wiederherstellen..."
rclone sync "$REMOTE/latest/uploads/" "$SITE_DIR/wp-content/uploads/" \
  --transfers 16 \
  --checkers 32 \
  --progress \
  --stats 30s

echo "==> DB wiederherstellen..."
rclone copy "$REMOTE/latest/db/" /tmp/
mysql -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < /tmp/*.sql
rm /tmp/*.sql

echo "==> Fertig!"

7.3 File ausführbar machen

Bash
chmod +x ~/restore-sitename.sh

# backup ausführen
~/restore-sitename.sh

Was passiert beim Skript?

  1. Files – zieht latest/files/ von Dropbox auf den Server, überschreibt nur geänderte/fehlende
  2. DB herunterladen – kopiert den SQL-Dump aus Dropbox nach /tmp/
  3. DB wiederherstellen – importiert den SQL-Dump in die DB, überschreibt alles
  4. Cleanup – löscht den temporären SQL-Dump
  5. Fertig

Site ist danach identisch zum letzten Backup-Zeitpunkt.

RESTORE DATUM

Bash
#!/bin/bash
export PATH="$HOME/bin:/usr/local/bin:/usr/bin:/bin:$PATH"

set -euo pipefail

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 YYYY-MM-DD"
  echo "Beispiel: $0 2026-05-15"
  exit 1
fi

TARGET="$1"

if ! [[ "$TARGET" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
  echo "Fehler: Datum muss im Format YYYY-MM-DD sein"
  exit 1
fi

SITE="younique-onlinekurs.com"
SITE_DIR="$HOME/public_html/$SITE"
DB_NAME="xyqicuro_younique"
DB_USER="xyqicuro_younique"
DB_PASS='S[e.RhW5bP(H2)@7p.'
REMOTE="dropbox:/$SITE"

echo "==> Restore auf Stand vom $TARGET"
echo ""

# Liste aller Tagesordner > TARGET, aufsteigend sortiert (ältester zuerst)
NEWER_DIRS=$(rclone lsd "$REMOTE/" | awk '{print $5}' | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' | sort | awk -v t="$TARGET" '$0 > t')

# Ältester Tagesordner > TARGET (für Zip-Restore)
OLDEST_NEWER=$(echo "$NEWER_DIRS" | head -1)

restore_zip() {
  local TYPE="$1"
  local ZIPNAME="$2"
  local UNZIP_DEST="$3"

  echo "==> $TYPE wiederherstellen..."

  local SOURCE_DIR=""

  # Suche ältesten Tagesordner > TARGET, der ein Zip dieses Typs enthält
  for dir in $NEWER_DIRS; do
    if rclone ls "$REMOTE/$dir/$TYPE/" 2>/dev/null | grep -q "$ZIPNAME"; then
      SOURCE_DIR="$REMOTE/$dir/$TYPE/"
      echo "    Quelle: $dir/$TYPE/ (Version die nach $TARGET ersetzt wurde)"
      break
    fi
  done

  # Falls keiner gefunden: nimm latest (zip wurde seit TARGET nicht ersetzt)
  if [[ -z "$SOURCE_DIR" ]]; then
    if rclone ls "$REMOTE/latest/$TYPE/" 2>/dev/null | grep -q "$ZIPNAME"; then
      SOURCE_DIR="$REMOTE/latest/$TYPE/"
      echo "    Quelle: latest/$TYPE/ (seit $TARGET unverändert)"
    else
      echo "    ⚠️  keine Quelle gefunden, übersprungen"
      return
    fi
  fi

  rclone copy "$SOURCE_DIR$ZIPNAME" /tmp/
  unzip -o "/tmp/$ZIPNAME" -d "$UNZIP_DEST"
  rm "/tmp/$ZIPNAME"
}

restore_zip "config" "config.zip" "$SITE_DIR/"
restore_zip "plugins" "plugins.zip" "$SITE_DIR/wp-content/"
restore_zip "mu-plugins" "mu-plugins.zip" "$SITE_DIR/wp-content/"
restore_zip "themes" "themes.zip" "$SITE_DIR/wp-content/"

echo "==> Uploads wiederherstellen..."
echo "    Basis: latest/"
rclone sync "$REMOTE/latest/uploads/" "$SITE_DIR/wp-content/uploads/" \
  --transfers 16 --checkers 32 --progress --stats 30s

# Overlay: neueste Tagesordner zuerst, älteste zuletzt (älteste gewinnt → näher am TARGET)
for dir in $(echo "$NEWER_DIRS" | sort -r); do
  if rclone ls "$REMOTE/$dir/uploads/" 2>/dev/null | head -1 | grep -q .; then
    echo "    Overlay: $dir/uploads/"
    rclone copy "$REMOTE/$dir/uploads/" "$SITE_DIR/wp-content/uploads/" \
      --transfers 16 --checkers 32
  fi
done

echo "==> DB wiederherstellen..."
if rclone ls "$REMOTE/$TARGET/db/" 2>/dev/null | grep -q "\.sql\.gz$"; then
  echo "    Quelle: $TARGET/db/"
  rclone copy "$REMOTE/$TARGET/db/" /tmp/
  gunzip -c /tmp/*.sql.gz | mysql -u "$DB_USER" -p"$DB_PASS" "$DB_NAME"
  rm /tmp/*.sql.gz
else
  # Fallback: ältester Tagesordner > TARGET (entspricht Stand kurz nach TARGET)
  FALLBACK_DB=""
  for dir in $NEWER_DIRS; do
    if rclone ls "$REMOTE/$dir/db/" 2>/dev/null | grep -q "\.sql\.gz$"; then
      FALLBACK_DB="$dir"
      break
    fi
  done
  if [[ -n "$FALLBACK_DB" ]]; then
    echo "    ⚠️  Keine DB für $TARGET, verwende $FALLBACK_DB (nächster Tag)"
    rclone copy "$REMOTE/$FALLBACK_DB/db/" /tmp/
    gunzip -c /tmp/*.sql.gz | mysql -u "$DB_USER" -p"$DB_PASS" "$DB_NAME"
    rm /tmp/*.sql.gz
  else
    echo "    ⚠️  Keine DB-Sicherung gefunden für $TARGET oder später"
  fi
fi

echo ""
echo "==> Fertig: Stand wiederhergestellt auf $TARGET"

8. Laufendes Skript abbrechen – anpassen und neu starten

Bash
ps aux | grep rclone
# es sollte zwei anzeigen. grep selbst und den rclone sync
# nimm die id des rclone syncs und dann
kill 10765333 z.B

# dann nano ~/backup-sitename.sh anpassungen vornehmen und skript danach neu starten

9. Bash Profil alias hinzufügen

Bash
# User specific aliases and functions
alias backup-sitename='nohup ~/backup-sitename.sh &'
alias log='tail -f ~/nohup.out'

10. Kontext

Täglich automatisch (03:00):

  • config,zip (aus htaccess und wp-config.php)
  • plugins.zip (wenn 1 plugin aktualisiert, macht es alles neu)
  • themes.zip
  • mu-plugins.zip
  • uploads → inkrementell
  • db → inkrementell
  • 7-Tage-Rotation

Bei Restore:

  • Plugin korrupt → restore Script → wenige Minuten
  • Totalausfall → WP-CLI + restore Script → läuft wieder