Sauvegardes MariaDB

Sauvegarder MariaDB / MySQL vers Proxmox Backup Server : méthode testée, chiffres mesurés

Un dump par base, envoyé chiffré vers Proxmox Backup Server avec proxmox-backup-client, plus les binlogs toutes les 5 minutes : c'est la méthode que nous avons retenue après l'avoir comparée à six autres sur notre propre base. Voici les scripts, la restauration à la minute près, et les chiffres de notre banc.

Mesures en cours : Le premier backup complet (6 bases, 29,73 Gio) est mesuré ci-dessous. La déduplication d'un jour sur l'autre sur les 6 bases est mesurée ci-dessous (troisième backup). La durée du verrou de la copie physique d'une nuit ordinaire sera ajoutée ici.

Pourquoi PBS pour des bases MariaDB

  • Déduplication au fil de l'eau : seuls les morceaux nouveaux partent. Sur notre base interne, un dump quotidien complet ne coûte que quelques Mio de plus d'un passage à l'autre.
  • Chiffrement côté client : la clé reste chez vous. Sans elle, personne ne peut lire vos dumps, pas même nous.
  • Hors site : les sauvegardes quittent le serveur de production, et le compte client n'a aucun droit de suppression par défaut sur NimbusBackup. Une copie air-gapped est possible en complément.
  • Un dump applicatif plutôt qu'un gel de VM : un fil connu du forum Proxmox décrit un MariaDB figé par des fsfreeze qui se chevauchent. Le dump ne dépend pas de ce mécanisme.

Prérequis : le client et la clé de chiffrement

Sur Debian, le paquet officiel de Proxmox suffit ; sur Ubuntu, utilisez le paquet statique de Proxmox, via son dépôt ou le nôtre. Sur Fedora, RHEL, Rocky, Alma, Arch ou Alpine, Proxmox ne publie pas de paquet : nous maintenons un dépôt non officiel proxmox-backup-client (RPM, Arch, Alpine, arm64). Grâce à ce dépôt, nous pouvons fournir cette méthode sur toutes ces distributions. L'installation pas à pas est dans notre tutoriel proxmox-backup-client sous Linux.

Nous préconisons le chiffrement côté client dans tous les cas. Gardez la clé et sa copie papier hors du serveur : sans elle, rien ne se restaure.

Clé de chiffrement et identifiants du dépôt

# 1. Client-side encryption key (keep it OUTSIDE the server too)
proxmox-backup-client key create /root/.config/proxmox-backup/encryption-key.json

# 2. Paper copy of the key (print it, store it offline)
proxmox-backup-client key paperkey /root/.config/proxmox-backup/encryption-key.json --output-format text

# 3. Optional: a master key (RSA) to recover backups if the key is lost
proxmox-backup-client key create-master-key
proxmox-backup-client key import-master-pubkey master-public.pem

# Repository credentials, readable by root only
install -d -m 700 /etc/mariadb-pbs
cat > /etc/mariadb-pbs/env <<'EOF'
export PBS_REPOSITORY='user@pbs!token@your-host.nimbus.rdem-systems.com:8007:your-datastore'
export PBS_PASSWORD='token-secret'
EOF
chmod 600 /etc/mariadb-pbs/env

La méthode recommandée : un dump par base, en pxar

Chaque base est exportée dans son propre fichier, sans compression, avec --skip-dump-date et --order-by-primary pour que le contenu reste identique d'un jour à l'autre là où rien n'a changé. Le répertoire part ensuite en archive pxar, découpée selon le contenu.

Chaque base est cohérente en elle-même (une transaction par base), pas avec les autres. Le script s'arrête dès qu'un dump échoue, avant tout envoi.

Script de dump quotidien

#!/bin/bash
# /usr/local/sbin/mariadb-pbs-dump : daily logical dump -> Proxmox Backup Server
set -euo pipefail
. /etc/mariadb-pbs/env
ID="$(hostname --fqdn)"
STAGE=/var/backups/mariadb-pbs
KEY=/root/.config/proxmox-backup/encryption-key.json
CHUNK=1024  # KiB: same dedup as 256 within 0.1 point on our test, 4x fewer chunks

rm -rf "$STAGE"; install -d -m 700 "$STAGE"
mapfile -t DBS < <(mariadb -N -e "SELECT schema_name FROM information_schema.schemata
  WHERE schema_name NOT IN ('information_schema','performance_schema','sys','mysql')")
[ "${#DBS[@]}" -gt 0 ] || { echo "no database listed, aborting" >&2; exit 1; }
# binlog position in the dump header (needed for point-in-time recovery), only if binlogs are on
BINPOS=(); [ "$(mariadb -N -e 'SELECT @@log_bin')" = 1 ] && BINPOS=(--master-data=2)

for db in "${DBS[@]}"; do
  # one file per database, NOT compressed (PBS compresses and deduplicates itself)
  mariadb-dump --single-transaction --skip-dump-date --order-by-primary \
    --routines --events --triggers --hex-blob "${BINPOS[@]}" \
    --databases "$db" > "$STAGE/$db.sql"       # set -e stops here if the dump fails
done
# system tables are NOT dumped raw (reloading them on another version is risky):
# accounts and grants as portable SQL instead (tested with mariadb-dump 11.8)
mariadb-dump --system=users > "$STAGE/_users.sql"
(cd "$STAGE" && sha256sum *.sql > SHA256SUMS)   # lets you self-check a restore later

proxmox-backup-client backup "dumps.pxar:$STAGE" \
  --backup-id "$ID-mariadb-dumps" --chunk-size "$CHUNK" \
  --keyfile "$KEY" --crypt-mode encrypt
rm -rf "$STAGE"

Planification

# /etc/cron.d/mariadb-pbs
15 2 * * *   root  /usr/local/sbin/mariadb-pbs-dump     >> /var/log/mariadb-pbs.log 2>&1
*/5 * * * *  root  /usr/local/sbin/mariadb-pbs-binlogs  >> /var/log/mariadb-pbs.log 2>&1

Les binlogs toutes les 5 minutes, et la restauration à la minute près

Le dump quotidien donne un point de reprise par jour. Pour revenir juste avant une erreur humaine, il faut les binlogs. À chaque cycle, on ferme le binlog courant (FLUSH BINARY LOGS) et on n'envoie que les fichiers fermés, en mode --change-detection-mode=metadata : seuls les nouveaux fichiers sont lus. Pour chiffrer la perte de données acceptable et la durée d'une restauration, voir le calculateur RPO / RTO.

Dans notre test (en conteneur, en accéléré), chaque cycle envoyait 4,7 à 5,5 Mio de binlog, soit 0,6 à 0,7 Mio après compression. La restauration à la minute près a été validée : dump de référence plus binlogs rapatriés depuis Nimbus, rejoués jusqu'au DROP TABLE fautif, 180 000 lignes retrouvées sur 180 000.

Script binlogs

#!/bin/bash
# /usr/local/sbin/mariadb-pbs-binlogs : every 5 minutes, closed binlogs only
set -euo pipefail
. /etc/mariadb-pbs/env
ID="$(hostname --fqdn)"
BL=/var/lib/mysql-binlog     # dedicated directory (log_bin=/var/lib/mysql-binlog/mariadb-bin)

mariadb -e "FLUSH BINARY LOGS"                             # close the current binlog
ACTIVE=$(mariadb -N -e "SHOW MASTER STATUS" | cut -f1)     # the new, still open one

proxmox-backup-client backup "binlogs.pxar:$BL" \
  --backup-id "$ID-mariadb-binlogs" --change-detection-mode=metadata \
  --exclude "$ACTIVE" --exclude "$ACTIVE.idx" \
  --keyfile /root/.config/proxmox-backup/encryption-key.json --crypt-mode encrypt

Restaurer juste avant une erreur (PITR)

# 1. restore the dump of the database and the latest binlog snapshot
proxmox-backup-client restore "host/$ID-mariadb-dumps/<date>"   dumps.pxar   /r/dumps   --keyfile "$KEY"
proxmox-backup-client restore "host/$ID-mariadb-binlogs/<date>" binlogs.pxar /r/binlogs --keyfile "$KEY"

# 2. replay start: the position written by --master-data=2 at the top of the dump
grep -m1 "CHANGE MASTER TO" /r/dumps/app.sql
#   -- CHANGE MASTER TO MASTER_LOG_FILE='mariadb-bin.000001', MASTER_LOG_POS=4394120;

# 3. find the faulty event: note the "# at" of the GTID event just before it
mariadb-binlog /r/binlogs/mariadb-bin.000008 | grep -B30 "DROP TABLE" | grep -E "^# at|GTID"

# 4. binlog list from the index file (never a glob: 11.8 writes .idx files next to them)
FILES=$(sed 's|.*/||' /r/binlogs/mariadb-bin.index | awk '$0>="mariadb-bin.000001" && $0<="mariadb-bin.000008"' | sed 's|^|/r/binlogs/|')

# 5. reload the dump, then replay up to the event before the mistake
mariadb < /r/dumps/app.sql
mariadb-binlog --start-position=4394120 --stop-position=5737637 $FILES | mariadb
  • MariaDB 11.8 écrit des fichiers .idx à côté des binlogs : lisez la liste dans mariadb-bin.index, jamais avec un motif shell.
  • --stop-datetime ne s'arrête qu'à la seconde près : notre test y a perdu un cycle entier d'écritures. Face à une erreur humaine, repérez l'événement fautif et utilisez --stop-position.
  • Un snapshot toutes les 5 minutes se multiplie vite. Le compte client n'ayant pas le droit de supprimer, la rétention (prune) se règle côté Nimbus.
  • Notre test couvrait une seule base. Sur un serveur à plusieurs bases, validez le rejeu sur votre charge avant d'en dépendre.

Copie physique sans espace temporaire : FLUSH TABLES … FOR EXPORT

Un dump se restaure en rejouant le SQL : chaque INSERT, chaque index reconstruit. Sur 30 Go, cela peut se compter en heures. Une copie physique se remet en place au débit du disque. Sur nos 5 petites bases, notre test sur une base interne donne 43 s pour rapatrier et remettre en service une copie physique de 4,05 Gio, contre environ 195 s pour rapatrier et recharger le dump de 1,7 Gio. Mais mariadb-backup demande un espace temporaire de la taille de la base, qui manquait sur notre serveur.

FLUSH TABLES … FOR EXPORT fige les tables, écrit un fichier .cfg à côté de chaque .ibd et bloque les écritures jusqu'à UNLOCK TABLES. Pendant ce temps, pxar lit le répertoire de la base sur place, lancé depuis la même session (commande system du client mariadb) pour que le verrou tienne. En mode metadata, les fichiers inchangés, comme les partitions fermées d'une série temporelle, ne sont même pas relus. Pour que le verrou reste court dès le premier soir, un pré-passage sans verrou envoie d'abord presque tout dans le même groupe : son snapshot n'est pas cohérent, il est annoté comme tel.

Script de copie physique (pré-passage, puis passage verrouillé)

#!/bin/bash
# /usr/local/sbin/mariadb-pbs-datadir : physical copy per database, NO temporary space
# 1) pre-pass without lock (not consistent, annotated)  2) locked pass: FLUSH TABLES ... FOR EXPORT,
# pxar of the database directory sent FROM THE SAME SESSION, then UNLOCK TABLES.
set -euo pipefail
. /etc/mariadb-pbs/env
ID="$(hostname --fqdn)"
KEY=/root/.config/proxmox-backup/encryption-key.json
O="--chunk-size 1024 --change-detection-mode=metadata --keyfile $KEY --crypt-mode encrypt"
note() { t=$(proxmox-backup-client snapshot list "host/$1" --output-format json |
    python3 -c 'import json,sys; print(max(x["backup-time"] for x in json.load(sys.stdin)))')
  proxmox-backup-client snapshot notes update "host/$1/$(date -u -d @$t +%Y-%m-%dT%H:%M:%SZ)" "$2"; }

# schema + system tables as portable SQL (needed to rebuild a fresh instance)
S=/var/backups/mariadb-pbs-schema; rm -rf "$S"; install -d -m 700 "$S"
mariadb-dump --no-data --routines --triggers --all-databases > "$S/schema.sql"
mariadb-dump --system=all > "$S/system.sql"
proxmox-backup-client backup "schema.pxar:$S" --backup-id "$ID-mariadb-schema" $O
rm -rf "$S"

for db in $(mariadb -N -e "SELECT schema_name FROM information_schema.schemata
    WHERE schema_name NOT IN ('mysql','information_schema','performance_schema','sys')"); do
  T=$(mariadb -N -e "SELECT GROUP_CONCAT(table_name) FROM information_schema.tables
      WHERE table_schema='$db' AND engine='InnoDB' AND table_type='BASE TABLE'")
  [ "$T" != "NULL" ] || continue
  G="$ID-mariadb-datadir-$db"
  # 1. pre-pass, no lock, low priority: uploads almost everything while the database runs
  PRE_START=$(date +%s)
  nice -n 19 ionice -c3 proxmox-backup-client backup "$db.pxar:/var/lib/mysql/$db" --backup-id "$G" $O
  note "$G" "PRE-PASS without lock: NOT CONSISTENT, do not restore"
  # 2. locked pass: only files changed since the pre-pass are re-read (metadata mode).
  #    Under the lock, every file modified since the pre-pass began is touched: it is then fully
  #    re-read, even if a write during the pre-pass left the same mtime (ext4 timestamp granularity).
  mariadb "$db" <<SQL
FLUSH TABLES $T FOR EXPORT;
system find /var/lib/mysql/$db -type f -newermt @$((PRE_START-2)) -exec touch {} +
system proxmox-backup-client backup $db.pxar:/var/lib/mysql/$db --backup-id $G $O
UNLOCK TABLES;
SQL
  note "$G" "CONSISTENT (FLUSH TABLES FOR EXPORT): restore with IMPORT TABLESPACE"
done

Restauration : instance neuve, schéma, puis IMPORT TABLESPACE (complète ou partielle)

# On a FRESH instance of the same major version (mysql_install_db, then start MariaDB)
. /etc/mariadb-pbs/env; ID="$(hostname --fqdn)"; K="--keyfile /root/.config/proxmox-backup/encryption-key.json"
# pick the snapshots annotated CONSISTENT, never the pre-pass ones
proxmox-backup-client snapshot list "host/$ID-mariadb-datadir-mydb"     # read the notes column

# 1. schema and system tables
proxmox-backup-client restore "host/$ID-mariadb-schema/<time>" schema.pxar /srv/restore/schema $K
mariadb < /srv/restore/schema/schema.sql
mariadb < /srv/restore/schema/system.sql

# 2. tablespace files of the database (.ibd + .cfg)
proxmox-backup-client restore "host/$ID-mariadb-datadir-mydb/<time>" mydb.pxar /srv/restore/mydb $K
D=/var/lib/mysql/mydb

# 3a. non-partitioned table (repeat per table: that's also the partial restore)
mariadb mydb -e "ALTER TABLE customers DISCARD TABLESPACE"
cp /srv/restore/mydb/customers.ibd /srv/restore/mydb/customers.cfg $D/ && chown mysql:mysql $D/customers.*
mariadb mydb -e "ALTER TABLE customers IMPORT TABLESPACE"

# 3b. partitioned table: MariaDB refuses DISCARD on it -> one partition at a time via EXCHANGE
mariadb mydb -e "CREATE TABLE _imp LIKE measurements; ALTER TABLE _imp REMOVE PARTITIONING;
                 ALTER TABLE _imp DISCARD TABLESPACE"
cp "/srv/restore/mydb/measurements#P#p202609.ibd" $D/_imp.ibd
cp "/srv/restore/mydb/measurements#P#p202609.cfg" $D/_imp.cfg; chown mysql:mysql $D/_imp.*
mariadb mydb -e "ALTER TABLE _imp IMPORT TABLESPACE;
                 ALTER TABLE measurements EXCHANGE PARTITION p202609 WITH TABLE _imp; DROP TABLE _imp"

# 4. check the pages of every restored tablespace (catches a torn page), BEFORE importing in production
for f in /srv/restore/mydb/*.ibd; do innochecksum "$f" || echo "PAGE ERROR: $f"; done
# 5. check: row count + logical checksum, to compare with the source
mariadb -N mydb -e "SELECT COUNT(*), BIT_XOR(CRC32(CONCAT_WS('#', id, measured_at, v))) FROM measurements"

Ce que nous avons mesuré

  • Prototype en conteneur (table partitionnée par mois, 2,45 millions de lignes) : 1er envoi avec un verrou de 2,5 s ; 2e envoi après 50 000 insertions : 18,6 Mio relus sur 160,8, verrou de 0,3 s.
  • Restauration du prototype par IMPORT TABLESPACE et EXCHANGE PARTITION : nombre de lignes et checksum logique identiques à la source.
  • Pré-passage sans verrou sur notre serveur (6 bases) : 45 Gio → 9,6 Gio stockés (−79 %), lus en 14 min en priorité basse. C'est environ deux fois le dump : la copie physique contient les index et l'espace libre interne des fichiers .ibd.
  • Premier passage verrouillé sur notre serveur (30/09/2026), deux jours après le pré-passage : verrou tenu 138,5 s sur la base de séries temporelles (2,51 Gio relus et envoyés sur 41,7 Gio, les partitions fermées étant reprises sans relecture), puis 4,4 s, 2,2 s, 2,0 s, 1,7 s et 0,4 s sur les cinq autres bases. Ce premier passage cumulait deux jours de modifications : le verrou d'une nuit ordinaire sera plus court.
  • Les écritures sur les tables verrouillées sont bloquées pendant le passage verrouillé : c'est pour cela que le pré-passage existe, et que l'envoi verrouillé ne tourne pas en priorité basse.
  • InnoDB uniquement. La base système mysql part en SQL portable (mariadb-dump --system=all), avec le schéma.
  • Ce n'est pas un datadir sur lequel démarrer tel quel : il n'y a ni espace système ni journaux. La restauration passe par une instance neuve, le schéma, puis l'import. Pour pouvoir démarrer le moteur directement sur la sauvegarde, il faut une copie complète par mariadb-backup.
  • MariaDB refuse DISCARD TABLESPACE sur une table partitionnée : on importe partition par partition, via une table temporaire et EXCHANGE PARTITION.
  • Les snapshots du pré-passage ne sont pas restaurables : filtrez sur la note « CONSISTENT ». L'import exige le même schéma et une version compatible.
  • Dans une ligne system du client mariadb, un ; fait basculer toute la ligne en SQL, même entre apostrophes : notre premier essai en production a échoué ainsi (erreur 1064). Enchaînez avec && ou appelez un script.
  • Le mode metadata ne relit un fichier que si sa date de modification a changé. Sur ext4, cette date a une granularité de quelques millisecondes : une écriture pendant le pré-passage pourrait laisser la même date, et le passage verrouillé reprendrait une page à moitié écrite. D'où le touch, sous verrou, de tous les fichiers modifiés depuis le début du pré-passage, qui force leur relecture, et le contrôle innochecksum dans la procédure de restauration.

Restaurer et vérifier soi-même

Le fichier SHA256SUMS produit par le script permet de vérifier une restauration au bit près. Rechargez ensuite le dump dans une instance jetable : c'est le seul vrai test. Les tests de restauration réalisés par nos équipes font partie de notre infogérance, pas de la prestation de sauvegarde.

Restauration et contrôle

. /etc/mariadb-pbs/env; ID="$(hostname --fqdn)"
KEY=/root/.config/proxmox-backup/encryption-key.json

# list snapshots (paths are host/<backup-id>/<RFC 3339 date>, not an epoch)
proxmox-backup-client snapshot list "host/$ID-mariadb-dumps"

# restore every dump of a snapshot, then check it bit for bit
SNAP="host/$ID-mariadb-dumps/2026-09-28T12:06:51Z"
proxmox-backup-client restore "$SNAP" dumps.pxar /srv/restore --keyfile "$KEY"
cd /srv/restore && sha256sum -c SHA256SUMS

# or pick a single database file interactively
proxmox-backup-client catalog shell "$SNAP" dumps.pxar --keyfile "$KEY"
#   pxar:/ > find --select *.sql      # --select is required, otherwise nothing is restored
#   pxar:/ > restore-selected /srv/restore

Notre banc : les chiffres mesurés

Notre test, sur une base interne : serveur web-01 de RDEM, MariaDB 11.8.6, 5 bases, dumps d'environ 1,695 Gio, proxmox-backup-client 4.2.6 (paquet Debian officiel), datastore NimbusBackup, chiffrement côté client activé, le 28/09/2026. Chaque ligne donne ce que le 2ᵉ passage, une heure après le 1ᵉʳ, a dû envoyer. La déduplication est calculée de la même façon pour toutes les lignes : 1 − (nouveau brut / total brut).

MéthodeNouveau (brut)Total (brut)Nouveau (compressé)Déduplication
Un dump par base → répertoire → pxar, --chunk-size 25624.2 MiB1.695 GiB1.6 MiB98.6 %
mariadb-dump --dir → pxar, 256 Ko (instance de restauration)29.4 MiB1.493 GiB2.2 MiB98.1 %
Un dump par base → pxar, 4 Mo (défaut)106.4 MiB1.695 GiB8.5 MiB93.9 %
Client Go, -backupstream (morceaux ≈ 5,8 Mo)107.8 MiB1.695 GiBnon mesuré93.8 %
mariadb-dump --dir → pxar, 4 Mo (instance de restauration)96.0 MiB1.493 GiB9.5 MiB93.7 %
mariadb-backup (physique, 5 bases) → pxar882.1 MiB4.012 GiB56.1 MiB78.5 %
mydumper 1.0.5 (4 fils) → pxar, 256 Ko1.173 GiB1.967 GiB66.8 MiB40.4 %
mydumper 1.0.5 → pxar, 4 Mo1.617 GiB1.967 GiB91.7 MiB17.8 %
Pipe → .img:/dev/stdin (client officiel)1.691 GiB1.695 GiB108.0 MiB0.2 %
Pipe | zstd -3 → .img:/dev/stdin111.9 MiB111.9 MiB111.9 MiB0 %

Premier backup complet : 6 bases, 29,73 Gio

Notre test, sur une base interne : même serveur, les 5 bases plus une base de séries temporelles de 29 Go sur disque (tables partitionnées par mois, surtout des ajouts), sur un datastore vidé au préalable, avec une nouvelle clé de chiffrement. Le 28/09/2026, en fin de journée, en priorité basse (nice, ionice) sur un serveur de production. Le banc dumpe avec le script de cette page, mais sans --events : son compte MariaDB est en lecture seule et n'a pas le droit EVENT.

  • Dump MariaDB des 6 bases : 2 047 s (34 min), dont 1 926 s pour la base de séries temporelles. C'est le temps de MariaDB, pas celui de l'envoi.
  • Espace temporaire : le dump SQL de cette base pèse 28 Gio (30,1 Go), autant que la base sur disque. La méthode par répertoire demande cette place en local.
  • Envoi chiffré vers NimbusBackup : 29,73 Gio en 7 min 00 s avec des morceaux de 1 Mo (72 Mio/s), en 6 min 29 s avec 4 Mo (78 Mio/s), en 7 min 49 s avec 256 Ko (65 Mio/s).
  • Stocké : 29,73 Gio → 4,3 Gio (−86 %), soit une compression d'environ 7 fois sur du texte SQL. Pour estimer l'espace nécessaire sur votre propre datastore, voir notre calculateur de capacité ZFS et d'espace PBS.
  • Nombre de morceaux sur le datastore pour ces 29,73 Gio : 121 483 en 256 Ko, 30 362 en 1 Mo, 7 132 en 4 Mo. Des morceaux plus petits dédupliquent mieux, mais multiplient les fichiers à stocker, vérifier et nettoyer.
  • Restauration des fichiers de dump depuis NimbusBackup (archive 1 Mo) : 29,73 Gio en 12 min 27 s (40,8 Mio/s), SHA256 identiques. Le rechargement dans MariaDB n'est pas compté. Elle aussi tournait en priorité basse et écrivait sur le disque de production : sur une machine dédiée, elle irait plus vite.

Deuxième backup, 7 h plus tard

Notre test, sur une base interne : même jeu de 6 bases, dumpé à nouveau le 29/09/2026 à 2 h, 7 heures après le premier backup. Chaque ligne donne ce que PBS a dû recevoir de neuf. C'est un repère de modification de notre base, surtout alimentée par des ajouts, sur 7 heures : ne l'extrapolez pas à 24 h ni à une autre base.

  • Morceaux de 1 Mo : 132,2 Mio de données nouvelles sur 29,77 Gio (99,57 % déjà présents), soit 17,2 Mio réellement envoyés après compression, en 2 min 35 s.
  • Morceaux de 256 Ko : 96,1 Mio nouveaux (99,68 %), 12,6 Mio envoyés. Morceaux de 4 Mo : 260,2 Mio nouveaux (99,15 %), 34,1 Mio envoyés.
  • L'envoi prend encore 2 à 3 minutes pour quelques dizaines de Mio : le client doit relire et découper les 29,77 Gio en local pour trouver ce qui a changé.
  • Restauration des fichiers de dump depuis NimbusBackup : 12 min 57 s, SHA256 identiques.

Troisième backup, 24 h plus tard

Notre test, sur une base interne : les mêmes 6 bases, dumpées le 30/09/2026 à 2 h, 24 heures après le deuxième backup. C'est la mesure d'un jour sur l'autre pour notre base, une série temporelle surtout alimentée par des ajouts : une base plus modifiée donnera moins.

  • Morceaux de 1 Mo : 668,9 Mio de données nouvelles sur 29,93 Gio (97,82 % déjà présents), soit 60,6 Mio réellement envoyés après compression, en 2 min 35 s.
  • Morceaux de 256 Ko : 634,7 Mio nouveaux (97,93 %), 55,3 Mio envoyés. Morceaux de 4 Mo : 791,4 Mio nouveaux (97,42 %), 77,6 Mio envoyés.
  • Le 1 Mo ne déduplique que 0,1 point de moins que le 256 Ko, pour quatre fois moins de morceaux (30 513 contre 122 246) : c'est la taille que nous retenons.
  • Restauration des fichiers de dump depuis NimbusBackup : 12 min 09 s, SHA256 identiques.

Premier banc (5 bases) : premier passage et restauration

  • Premier passage (groupe vide) : 1,695 Gio envoyés en 18 s, 108 Mio stockés après compression.
  • Restauration de 1,694 Gio depuis Nimbus en 10,2 s (170,5 Mio/s selon le client), avec la seule clé.
  • SHA256 identiques pour les 5 dumps : c'est la preuve que la restauration est fidèle au bit près.
  • Rechargement dans MariaDB 11.8.6 en 3 minutes environ, 552 tables sur 552, même nombre de tables qu'à la source (un CHECK TABLE sur un import frais prouve peu de chose, le SHA256 fait foi).

La déduplication de PBS se fait au fil de l'eau

Il n'y a pas de déduplication a posteriori. Chaque morceau est identifié par son empreinte : le client n'envoie pas ceux que le groupe possède déjà, et le serveur ne stocke qu'une fois chaque morceau, tous groupes confondus. Le ramasse-miettes ne fait que supprimer les morceaux orphelins. Sur notre datastore de test, 39 snapshots occupaient 2,05 Gio pour 22,3 Gio de données logiques.

La déduplication dépend du profil d'écriture

Sur des données synthétiques (1 million de lignes), des mises à jour éparpillées (une ligne sur cent, réparties uniformément) ont donné 0 % de déduplication, en pxar 256 Ko comme avec le client Go : chaque morceau contenait une modification. Avec des mises à jour groupées (1 % de lignes contiguës, plus des ajouts), le pxar 256 Ko est remonté à 96,4 %. Une base qui ajoute surtout des lignes déduplique très bien ; une base réécrite de partout, beaucoup moins.

Les autres méthodes et leurs limites

L'approche historique de NimbusBackup, indépendante du moteur

Cette page décrit la méthode propre à MariaDB / MySQL, en direct vers Proxmox Backup Server : NimbusBackup, notre offre de PBS managé, ou le vôtre. Pour toutes les bases, quel que soit le moteur, NimbusBackup propose aussi son approche historique : la sauvegarde de bases de données externalisée.

Compresser avant l'envoi (zstd)

À éviter : PBS compresse déjà (112 Mio avec zstd contre 108 Mio compressés par PBS), et un flux compressé ne se déduplique plus. zstd --rsyncable en préserverait une partie, mais reste une mauvaise idée.

mariadb-backup (sauvegarde physique)

78,5 % de déduplication dans notre banc. Utile pour restaurer vite une grosse base, mais il demande root, l'accès au datadir et quelques Go de disque local.

mariadb-dump --dir

Aussi bon que le dump par base à taille de morceau égale (98,1 % en 256 Ko), mais c'est le serveur qui écrit les fichiers : il faut le privilège FILE et un secure_file_priv défini. Mesuré sur notre instance de restauration, pas en production.

mydumper

Parallèle et cohérent, mais sa sortie change d'un passage à l'autre : 40,4 % en 256 Ko, 17,8 % en 4 Mo. Le parallélisme coûte ici la déduplication.

Client Go (tizbac/proxmoxbackupclient_go v1.1.3)

Son option -backupstream range un flux dans un index dynamique, sans disque local : 93,8 % dans notre banc, avec des morceaux d'environ 5,8 Mo non réglables. Deux limites aujourd'hui : pas de chiffrement côté client, et le client officiel ne sait pas relire ces sauvegardes (« failed to parse archive type »). Nous contribuons à ce client.

Et le pipe direct, sans disque local ?

Le client officiel accepte un flux depuis la version 4.1.5 (commit 2db23ce, mars 2026), sous la forme nom.img:/dev/stdin. Ça fonctionne, et la restauration est fidèle au bit près. Mais une archive .img est découpée en blocs fixes de 4 Mio : une ligne ajoutée décale tout ce qui suit, et notre banc n'a mesuré que 0,2 % de déduplication. Chaque sauvegarde est donc stockée presque en entier.

Deux autres limites : seule la taille de morceau par défaut fonctionne (toute archive .img en dessous de 4096 Ko échoue avec « detected multiple end chunks (chunk size too small) », même depuis un fichier), et le COPR Fedora/RHEL est bloqué en 4.1.0, sans pipe. Notre dépôt non officiel proxmox-backup-client (RPM, Arch, Alpine, arm64) livre une version récente.

Pensez à set -o pipefail : sans lui, un dump qui échoue au milieu est sauvegardé tronqué sans erreur.

Pipe vers .img (client ≥ 4.1.5)

# Works from client 4.1.5 onwards; default chunk size only
set -o pipefail
mariadb-dump --single-transaction --skip-dump-date --databases app \
  | proxmox-backup-client backup app.img:/dev/stdin --keyfile "$KEY" --crypt-mode encrypt

Certains guides proposent db.pxar.didx:- (exemple, consulté le 28/09/2026). Cette syntaxe est refusée par toutes les versions du client que nous avons testées. Elle semble mélanger le client Go, qui range un flux dans un .didx, et la commande du client officiel :

Sortie réelle du client

$ echo x | proxmox-backup-client backup db.pxar.didx:-
Error: parameter verification failed - 'backupspec': value does not match the regex pattern

Versions testées

VersionPipe .img:/dev/stdin.pxar.didx:-.img < 4096 Ko
4.0.20 → 4.1.4❌ « got unexpected file type (expected file or block device) »❌❌
4.1.5 → 4.2.6✅ taille de morceau par défaut❌❌

Une sauvegarde chiffrée se restaure dans les deux sens entre les versions 4.2.6 et 4.1.1, avec des SHA256 identiques.

Sur Rocky Linux et RHEL : le piège des certificats

Sur Rocky Linux 10.2, le binaire statique brut de Proxmox cherche les certificats dans /usr/lib/ssl, qui n'existe pas, et échoue :

Erreur du binaire brut sur Rocky 10.2

certificate validation failed - Certificate fingerprint was not confirmed.
Error: client error (Connect)
Caused by:
    0: error:0A000086:SSL routines:tls_post_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:2124:

Le paquet de notre dépôt non officiel proxmox-backup-client (RPM, Arch, Alpine, arm64) ajoute les deux liens nécessaires : installé par dnf avec vérification GPG, il a restauré nos dumps avec la seule clé, SHA256 identiques. À défaut, SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt contourne le problème (testé sur Rocky 10.2 avec le binaire brut).

Et pour un cluster Galera ?

Nous suivons le modèle publié dans notre étude de cas sauvegarder un cluster Galera vers Proxmox Backup Server : un 4ᵉ nœud réplica, arrêté proprement, puis sauvegardé, sans toucher aux nœuds de production. Les dumps et binlogs décrits ici peuvent s'y ajouter depuis ce nœud.

Nommage des sauvegardes et rétention

Un backup-id ne peut pas contenir de /, et le compte client ne peut pas créer de namespace. Nous utilisons le nom complet du serveur, suivi du type de sauvegarde, dans des groupes séparés : les rythmes diffèrent, et PBS déduplique d'un snapshot au suivant dans le même groupe. Si un namespace a été créé pour le serveur à la mise en service, utilisez-le.

Convention de nommage

ID="$(hostname --fqdn)"
--backup-id "$ID-mariadb-dumps"      # daily dump
--backup-id "$ID-mariadb-binlogs"    # every 5 minutes
--backup-id "$ID-mariadb-datadir"    # mariadb-backup, if you use it
# or, when a namespace was created for the server: --ns "$ID" --backup-id dumps

La rétention se règle côté Nimbus, puisque le client n'a pas le droit de supprimer : c'est aussi ce qui protège vos sauvegardes si le serveur est compromis.

Questions fréquentes

Combien de temps prend un premier backup vers NimbusBackup ?

Dans notre test sur une base interne (6 bases, 29,73 Gio de dump, chiffrement côté client, serveur de production en priorité basse) : 7 minutes d'envoi avec des morceaux de 1 Mo, 4,3 Gio stockés, et 12 min 27 s pour restaurer les fichiers de dump depuis NimbusBackup, SHA256 identiques. Le dump MariaDB lui-même a pris 34 minutes, et le rechargement des dumps dans une base (non mesuré sur ce jeu complet) dépend lui aussi de MariaDB et du serveur, pas de PBS.

Faut-il compresser les dumps avant de les envoyer à PBS ?

Non. PBS compresse lui-même chaque morceau, et un dump déjà compressé ne se déduplique plus : dans notre banc, un flux zstd était renvoyé en entier à chaque passage.

Peut-on envoyer mariadb-dump directement à proxmox-backup-client ?

Oui, depuis la version 4.1.5, avec nom.img:/dev/stdin et set -o pipefail. Mais l'archive .img est découpée en blocs fixes : notre banc n'a mesuré que 0,2 % de déduplication. Un dump dans un répertoire envoyé en pxar dédupliquait à 98,6 %.

La syntaxe db.pxar.didx:- fonctionne-t-elle ?

Non. Toutes les versions testées, de 4.0.20 à 4.2.6, la refusent avec « 'backupspec': value does not match the regex pattern ».

PBS déduplique-t-il après coup ?

Non, la déduplication se fait à l'envoi : le client n'envoie pas les morceaux déjà connus, et le serveur stocke chaque morceau une seule fois. Le ramasse-miettes supprime seulement les morceaux orphelins.

RDEM peut-il restaurer mes sauvegardes chiffrées ?

Non. Avec le chiffrement côté client, la clé reste chez vous et nous ne pouvons pas lire les données. Les tests de restauration par nos équipes font partie de l'infogérance, avec des accès prévus au contrat.

Vos bases MariaDB hors site, chiffrées, dédupliquées

Le stockage PBS de NimbusBackup, sans droit de suppression côté client par défaut. Vous pouvez aussi comparer toutes les offres. Pour des tests de restauration réalisés par nos équipes, voyez notre infogérance.

Démarrez votre projet MariaDB infogéré

Discutons de vos besoins en bases de données. Notre équipe DBA vous conseille sur l'architecture optimale pour votre cas d'usage.

RDEM Systems SAS — SIREN 820 338 671 — 5 B rue des Noyers, 95300 Pontoise