0 Votes

Sauvegarde

Version 7.2 par john le 2026/10/03 20:00

Sauvegarde quotidienne du serveur principal vers le Raspberry Pi avec Plakar : dépôt chiffré et dédupliqué, transport SFTP.

Fonctionnement

ÉlémentValeur
Machine sauvegardéeServeur principal
DépôtSSD USB du Raspberry Pi
TransportSFTP, via l'intégration Plakar sftp
DéclenchementCron, chaque nuit à 3 h 30
Rétention30 jours glissants
Script/root/backup-plakar.sh
Journal/var/log/backup-plakar.log, avec rotation par logrotate

Chaque exécution :

  • exporte toutes les bases en SQL dans <APPDATA>/dumps ;
  • attend la fin de la sauvegarde Borg de Nextcloud AIO (planifiée à 3 h 00), au plus une heure ;
  • sauvegarde les données des conteneurs, la bibliothèque Immich, le contenu de Joplin et le dépôt Borg de Nextcloud AIO, en excluant les fichiers bruts des bases et les données brutes de Nextcloud ;
  • supprime les instantanés de plus de 30 jours.
Information

Les fichiers bruts d'une base en fonctionnement ne sont pas fiables pour une restauration. Ce sont les dumps SQL qui font foi.

Nextcloud AIO fait exception : il n'y a pas de dump, sa base, ses fichiers et sa configuration sont dans son propre dépôt Borg, cohérent car produit conteneurs arrêtés. Plakar copie ce dépôt tel quel, d'où l'attente de la fin de la sauvegarde Borg : copier un dépôt en cours d'écriture donnerait une copie incohérente.

Information

Les dumps sont faits avec docker exec, donc avec le client pg_dump ou mysqldump du conteneur lui-même, toujours à la bonne version. Les intégrations Plakar postgresql et mysql ont été écartées : elles exigent un accès TCP à chaque base et des clients de même version installés sur l'hôte, et produisent un instantané séparé par base.

Mise en place

Installer Plakar (les deux machines)

Installer le paquet plakar (paquet .deb disponible sur la page de téléchargement) sur le serveur principal et sur le Raspberry Pi, dans la même version.

sudo apt install ./plakar_<VERSION>_<ARCH>.deb
plakar version

Installer l'intégration SFTP (serveur principal)

Depuis Plakar 1.1, le transport SFTP n'est plus inclus dans le binaire : c'est une intégration à installer à part. Sans elle, toute commande sur un dépôt sftp:// échoue.

Les intégrations sont installées par utilisateur (dans ~/.local/share/plakar/plugins). La sauvegarde tournant en root, l'installation se fait avec sudo.

Le téléchargement des intégrations précompilées demande un compte Plakar connecté (gratuit). Le serveur n'ayant pas de navigateur, utiliser -no-spawn : Plakar affiche une URL à ouvrir sur un autre poste, puis un code à saisir dans le terminal.

sudo plakar login -github -no-spawn      # ou : sudo plakar login -email <ADRESSE>
sudo plakar login -status
sudo plakar pkg add sftp
sudo plakar pkg list
Information

La connexion au compte ne sert qu'au gestionnaire d'intégrations. Les sauvegardes et restaurations n'en dépendent pas.

Le Raspberry Pi n'a pas besoin de l'intégration : il ouvre le dépôt en local.

Sur le Raspberry Pi (dépôt)

Utilisateur dédié, sans shell :

sudo useradd -m -d /srv/plakar -s /usr/sbin/nologin plakar
sudo mkdir -p <CHEMIN_DEPOT>
sudo chown plakar:plakar <CHEMIN_DEPOT>
sudo install -d -m 700 -o plakar -g plakar /srv/plakar/.ssh

Limitation au SFTP, dans /etc/ssh/sshd_config.d/plakar.conf :

Match User plakar
    ForceCommand internal-sftp -l VERBOSE
    AllowTcpForwarding no
    X11Forwarding no
sudo sshd -t && sudo systemctl reload ssh

Même en cas de vol de la clé, elle ne donne ni shell ni tunnel : seulement un accès SFTP.

L'option -l VERBOSE journalise chaque opération SFTP (ouverture, renommage, suppression, refus) dans journalctl -u ssh. Sans elle, un refus côté dépôt n'apparaît nulle part sur le Pi.

Sur le serveur principal (source)

Clé SSH dédiée, sans phrase de passe puisqu'elle sert à la tâche cron :

sudo ssh-keygen -t ed25519 -f /root/.ssh/plakar_matrix -N ""

Contenu de /root/.ssh/plakar_matrix.pub à copier dans /srv/plakar/.ssh/authorized_keys sur le Pi (propriétaire plakar, droits 600).

/root/.ssh/config :

Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com

Host <HOTE_DEPOT>
    
User plakar
    
IdentityFile /root/.ssh/plakar_matrix

La ligne Ciphers limite, pour toutes les connexions SSH de root, les algorithmes de chiffrement à des variantes modernes.

Première connexion, pour valider l'empreinte du Pi (sinon la tâche cron échouera) :

sudo sftp <HOTE_DEPOT>

Phrase secrète du dépôt et création :

sudo sh -c 'umask 077; openssl rand -base64 12 > /root/.plakar-pass'
sudo plakar -keyfile /root/.plakar-pass at "sftp://plakar@<HOTE_DEPOT>:<CHEMIN_DEPOT>" create
Warning

Le dépôt est chiffré avec cette phrase secrète. Sans elle, les sauvegardes sont illisibles, même pour leur propriétaire : en conserver une copie dans le gestionnaire de mots de passe, jamais uniquement sur le serveur.

Mettre à jour Plakar

Le paquet plakar et l'intégration sftp se mettent à jour séparément : apt ne touche pas aux intégrations. Après chaque mise à jour du paquet :

# Sur les deux machines : même version partout
plakar version

# Sur le serveur principal : mise à jour de toutes les intégrations de root
sudo plakar login -status                # se reconnecter si besoin
sudo plakar pkg add -u
sudo plakar pkg list

# Contrôle de l'accès au dépôt
sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls | head -2
Warning

Une intégration restée sur une ancienne version alors que Plakar a été mis à jour peut provoquer des échecs difficiles à diagnostiquer, sur le verrou du dépôt notamment (voir Incidents connus). Ne jamais mettre à jour le paquet sans mettre à jour l'intégration dans la foulée.

Restaurer si le serveur principal est perdu

Le dépôt peut s'ouvrir directement sur le Raspberry Pi, où Plakar est aussi installé, avec la phrase secrète récupérée dans le gestionnaire de mots de passe. L'accès étant local, aucune intégration ni compte Plakar n'est nécessaire :

sudo plakar at <CHEMIN_DEPOT> ls
sudo plakar at <CHEMIN_DEPOT> restore -to /tmp/restore <ID_INSTANTANE>

Plakar demande alors la phrase secrète de façon interactive.

Script

#!/bin/bash
set -euo pipefail
umask 077

# Alertes Telegram
. /root/.backup-telegram
notifier() {
  curl -fsS -m 10 "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
   -d chat_id="$TG_CHAT" --data-urlencode text="$1" >/dev/null || true
}
attention() { echo "ATTENTION: $1" >&2; notifier "⚠️ Sauvegarde : $1"; }
trap 'notifier "❌ Sauvegarde interrompue à la ligne $LINENO"' ERR

REPO="sftp://<UTILISATEUR>@<HOTE_DEPOT>:<CHEMIN_DEPOT>"
KEYFILE="<FICHIER_PHRASE_SECRETE>"
DUMPS="<APPDATA>/dumps"
mkdir -p "$DUMPS"

# Dumps MariaDB/MySQL
for c in xwiki-mariadb-db passbolt-db castopod-test-db; do
  docker exec "$c" sh -c 'exec mysqldump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \
   > "$DUMPS/$c.sql" 2>/dev/null \
   || docker exec "$c" sh -c 'exec mariadb-dump -uroot -p"${MYSQL_ROOT_PASSWORD:-$MARIADB_ROOT_PASSWORD}" --all-databases' \
   > "$DUMPS/$c.sql" 2>/dev/null \
   || attention "dump $c échoué"
done

# Dumps PostgreSQL
for c in joplin-db flus-db wallabag-db immich_postgres vikunja-db jellystat-db n8n-postgres dawarich_db; do
  docker exec "$c" sh -c 'exec pg_dumpall -U "$POSTGRES_USER"' \
   > "$DUMPS/$c.sql" 2>/dev/null \
   || attention "dump $c échoué"
done

# Attente de la fin de la sauvegarde Borg d'AIO (planifiée à 3h00)
attente=0
while docker ps --format '{{.Names}}' | grep -qx nextcloud-aio-borgbackup; do
 if [ "$attente" -ge 3600 ]; then
    attention "Borg AIO encore en cours après 1 h : dépôt Borg possiblement incohérent"
   break
 fi
  sleep 60
 attente=$((attente + 60))
done

# Backup avec exclusions : core dumps, binlogs, fichiers bruts des bases
# et données brutes de Nextcloud AIO (déjà dans le dépôt Borg, de façon cohérente)
plakar --concurrency 2 -keyfile "$KEYFILE" at "$REPO" backup \
 -ignore "**/joplin-app/core.*" \
 -ignore "**/Nextcloud-AIO/data/**" \
 -ignore "**/log/mysql/**" \
 -ignore "**/xwiki/mariadb/**" \
 -ignore "**/store.bak.*/**" \
 <APPDATA> \
 <BIBLIOTHEQUE_IMMICH> \
 <CONTENU_JOPLIN> \
 <DEPOT_BORG_AIO>

# Rétention : 30 jours glissants
plakar -keyfile "$KEYFILE" at "$REPO" rm -before 30d -apply
notifier "✅ Sauvegarde terminée"

Tâche cron, /etc/cron.d/backup-plakar :

30 3 * * * root /root/backup-plakar.sh >> /var/log/backup-plakar.log 2>&1

Alertes

Le script envoie un message Telegram à chaque exécution :

MessageSignification
✅ Sauvegarde terminéeTout s'est bien passé
⚠️ Sauvegarde : dump … échouéUn dump de base a échoué, la sauvegarde a continué sans lui
⚠️ Sauvegarde : Borg AIO encore en cours après 1 h …La sauvegarde Borg de Nextcloud n'était pas finie : la copie du dépôt Borg de cette nuit peut être incohérente, celle des nuits précédentes reste utilisable
❌ Sauvegarde interrompue à la ligne …Erreur bloquante, la sauvegarde n'a pas eu lieu (la ligne du plakar backup indique un échec de Plakar lui-même : consulter la fin du journal)

Si aucun message n'arrive un matin, le script n'a pas démarré (serveur éteint, tâche cron cassée).

Jeton du bot et identifiant du groupe dans /root/.backup-telegram, lisible par root seul :

TG_TOKEN="<JETON_BOT>"
TG_CHAT="<ID_GROUPE>"
Warning

Dans le script, chaque ligne continuée doit se terminer par \ sans rien après, pas même un espace. Une barre oblique inverse suivie d'un espace ne continue plus la ligne : c'est ce qui a interrompu plusieurs sauvegardes en septembre 2026. Contrôle après toute modification : sudo bash -n /root/backup-plakar.sh, et cat -A pour voir les caractères invisibles en fin de ligne.

Ajouter une base

Ajouter le nom du conteneur dans la boucle MariaDB ou PostgreSQL du script, puis exclure ses fichiers bruts avec un -ignore s'ils se trouvent dans <APPDATA>.

Vérifier

tail -n 50 /var/log/backup-plakar.log
grep ATTENTION /var/log/backup-plakar.log
sudo grep -nE '^(warn|error|plakar):' /var/log/backup-plakar.log | tail -n 20
sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls
sudo plakar pkg list

La colonne durée de ls donne le temps de chaque sauvegarde : une hausse brutale mérite un coup d'œil au journal.

Contrôle d'intégrité du dernier instantané, à lancer sur le Raspberry Pi (lecture locale du SSD, sans passer par le réseau) :

sudo -u plakar plakar at <CHEMIN_DEPOT> check <ID_INSTANTANE>

Sans identifiant, check vérifie tous les instantanés : c'est très long sur un Pi et doit être terminé avant 3 h 30.

Restaurer

Restaurer un instantané complet dans un dossier temporaire :

sudo plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> restore -to /tmp/restore <ID_INSTANTANE>

Réinjecter une base MariaDB :

docker exec -i <CONTENEUR> sh -c 'exec mariadb -uroot -p"$MARIADB_ROOT_PASSWORD"' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql

Réinjecter une base PostgreSQL (dump complet de l'instance) :

docker exec -i <CONTENEUR> sh -c 'exec psql -U "$POSTGRES_USER" -d postgres' < /tmp/restore/<CHEMIN>/dumps/<CONTENEUR>.sql

Restaurer Nextcloud AIO : restaurer le dossier <DEPOT_BORG_AIO> sur l'hôte, déployer le mastercontainer sur une installation neuve, puis choisir la restauration dans l'interface AIO (chemin du dépôt, mot de passe Borg, archive). Voir la page Nextcloud.

Warning

Une sauvegarde qui n'a jamais été restaurée n'est pas une sauvegarde vérifiée. Faire un essai de restauration de temps en temps, dans un dossier temporaire.

Incidents connus

SymptômeCauseSolution
Verrou restant après une exécution interrompueLe processus a été coupé en coursVérifier qu'aucun Plakar ne tourne (pgrep -a plakar), puis supprimer le fichier signalé dans <CHEMIN_DEPOT>/locks/ sur le Pi
Avertissements VFSCache local incohérentVider ~/.cache/plakar de root
Transfert coupé (EOF SFTP) sur de gros fichiersLien Wi-Fi du dépôt instableÉconomie d'énergie Wi-Fi désactivée sur le Pi ; passer à --concurrency 1 pour une première sauvegarde volumineuse
Déconnexion du SSD pendant la sauvegardePont USB en mode UASVoir la configuration SSD sur la page Raspberry Pi
Arrêt après une dizaine de minutes, failed to refresh the repository lock: sftp: "Failure" (SSH_FX_FAILURE) répété dans le journal, puis lock refresher: failed to refresh the repository lockCause probable : intégration sftp restée sur une ancienne version après une mise à jour de Plakar (octobre 2026). Le verrou est rafraîchi toutes les 5 minutes, les sauvegardes courtes peuvent donc masquer le problèmesudo plakar pkg add -u, supprimer le verrou orphelin, consulter journalctl -u ssh sur le Pi (log VERBOSE)
authentication required. Run `plakar login` lors d'un pkg addLe gestionnaire d'intégrations exige un compte connectésudo plakar login -github -no-spawn