0 Votes

Sauvegarde

Version 4.1 par john le 2026/09/19 19:19

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
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 ;
  • sauvegarde les données des conteneurs, la bibliothèque Immich et le contenu de Joplin, en excluant les fichiers bruts des bases ;
  • 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.

Mise en place du dépôt

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
    AllowTcpForwarding no
    X11Forwarding no
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.

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.

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 :

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   # Ajout : dumps lisibles par root seul

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 \
   || echo "ATTENTION: dump $c échoué" >&2
done

# Nextcloud : dump via l'utilisateur applicatif
docker exec "$(docker ps -q -f name=nextcloud_db)" sh -c 'exec mariadb-dump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' \
 > "$DUMPS/nextcloud.sql" 2>/dev/null \
 || echo "ATTENTION: dump nextcloud échoué" >&2

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

# Sauvegarde, sans core dumps, binlogs ni fichiers bruts des bases
plakar --concurrency 2 -keyfile "$KEYFILE" at "$REPO" backup \
 -ignore "**/joplin-app/core.*" \
 -ignore "**/Nextcloud/DB/**" \
 -ignore "**/log/mysql/**" \
 -ignore "**/xwiki/mariadb/**" \
 -ignore "**/store.bak.*/**" \
 <APPDATA> \
 <BIBLIOTHEQUE_IMMICH>\
 <CONTENU_JOPLIN>

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

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

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

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 plakar -keyfile <FICHIER_PHRASE_SECRETE> at <REPO> ls

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
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 coursSupprimer le verrou orphelin avant de relancer
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