Post

⛁ MariaDB Sauvegarde Restauration

⛁ MariaDB Sauvegarde Restauration

MariaDB Sauvegarde Restauration

Voici le pack complet simplifié pour un MariaDB où le root système peut se connecter sans mot de passe via socket local. MariaDB documente bien que l’authentification unix_socket permet au root système de se connecter à root@localhost sans mot de passe. mariadb
Configuration locale du serveur MariaDB pôur une utilisation sans mot de passe. ibug

Script de sauvegarde

Créez /usr/local/sbin/mariadb-backup.sh :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/var/backups/mariadb"
LOG_FILE="/var/log/mariadb-backup.log"
LOCK_FILE="/run/mariadb-backup.lock"
RETENTION_DAYS=14

log() { echo "[$(date '+%F %T')] $1" | tee -a "$LOG_FILE"; }
fail() { log "ERREUR: $1"; exit 1; }

mkdir -p "$BACKUP_DIR" "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"

command -v mariadb >/dev/null 2>&1 || fail "mariadb introuvable"
command -v mariadb-dump >/dev/null 2>&1 || fail "mariadb-dump introuvable"
command -v gzip >/dev/null 2>&1 || fail "gzip introuvable"

sudo mariadb -e "SELECT 1;" >/dev/null 2>&1 || fail "Connexion MariaDB impossible sans mot de passe"

exec 9>"$LOCK_FILE"
flock -n 9 || fail "Une autre sauvegarde est déjà en cours"

DATE="$(date +%F_%H-%M-%S)"
HOSTNAME="$(hostname -s)"

databases=$(sudo mariadb -Nse "SHOW DATABASES;" | grep -Ev '^(information_schema|performance_schema|mysql|sys)$')
[[ -n "$databases" ]] || fail "Aucune base à sauvegarder"

for db in $databases; do
  log "Sauvegarde de $db"
  tmp_file="$BACKUP_DIR/${HOSTNAME}_${db}_${DATE}.sql.gz.tmp"
  out_file="$BACKUP_DIR/${HOSTNAME}_${db}_${DATE}.sql.gz"

  sudo mariadb-dump --single-transaction --routines --events --triggers --hex-blob --databases "$db" \
    | gzip -9 > "$tmp_file"

  mv "$tmp_file" "$out_file"
  log "OK: $out_file"
done

find "$BACKUP_DIR" -type f -name '*.sql.gz' -mtime +"$RETENTION_DAYS" -delete
log "Rotation terminée"
log "Sauvegarde terminée"
# Message ntfy
curl \
-H "X-Email: ntfy@cinay.eu" \
-H "Title: ⛁ CWWK - Sauvegarde Bases MariaDB" \
-H "Authorization: Bearer tk_xxxxxxxxxxxxxxxxxxxxxxxx5x" \
-H prio:low \
-d "CWWK Debian 13
`hostname -I`
 ✔️ Fin Sauvegarde Bases MariaDB `date +%d/%m/%Y-%Hh%M`" \
https://ntfy.rnmkcy.eu/yan_infos

mariadb-dump est l’outil adapté pour créer des dumps logiques, et la restauration se fait ensuite avec le client mariadb. mariadb

Script de restauration

Créez /usr/local/sbin/mariadb-restore-fzf.sh :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env bash
set -euo pipefail

LOG_FILE="/var/log/mariadb-restore.log"
LOCK_FILE="/run/mariadb-restore.lock"
BACKUP_BEFORE_RESTORE="/var/backups/mariadb/pre-restore"

FILE=""
DRY_RUN=false
RESTORE_ALL=false

RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'
RESET=$'\033[0m'

log() { printf '%s[%s] %s%s\n' "$BLUE" "$(date '+%F %T')" "$1" "$RESET" | tee -a "$LOG_FILE"; }
ok() { printf '%s[%s] %s%s\n' "$GREEN" "$(date '+%F %T')" "$1" "$RESET" | tee -a "$LOG_FILE"; }
warn() { printf '%s[%s] %s%s\n' "$YELLOW" "$(date '+%F %T')" "$1" "$RESET" | tee -a "$LOG_FILE"; }
fail() { printf '%s[%s] %s%s\n' "$RED" "$(date '+%F %T')" "$1" "$RESET" | tee -a "$LOG_FILE" >&2; exit 1; }

usage() {
  cat <<EOF
Usage:
  $0 /chemin/vers/dump.sql.gz [--dry-run] [--all]
EOF
}

extract_sql() {
  if [[ "$FILE" == *.gz ]]; then
    gunzip -c "$FILE"
  else
    cat "$FILE"
  fi
}

list_databases() {
  extract_sql | awk '
    /^(CREATE DATABASE|USE) / {
      gsub(/`/, "", $0)
      if ($1 == "USE") print $2
      else if ($1 == "CREATE" && $2 == "DATABASE") print $4
    }
  ' | sed 's/;$//' | sort -u
}

list_tables_for_db() {
  local db="$1"
  extract_sql | awk -v target="$db" '
    BEGIN {p=0}
    /^CREATE DATABASE / {p=($0 ~ target)}
    /^USE / {p=($0 ~ target)}
    p==1 && /^CREATE TABLE / {
      gsub(/`/, "", $0)
      print $3
    }
  ' | sed 's/;$//' | sort -u
}

verify_env() {
  [[ -n "$FILE" ]] || fail "Fichier manquant"
  [[ -f "$FILE" ]] || fail "Fichier introuvable: $FILE"
  command -v mariadb >/dev/null 2>&1 || fail "mariadb introuvable"
  command -v mariadb-dump >/dev/null 2>&1 || fail "mariadb-dump introuvable"
  command -v gunzip >/dev/null 2>&1 || fail "gunzip introuvable"
  command -v fzf >/dev/null 2>&1 || warn "fzf absent: sélection interactive désactivée"

  if [[ "$FILE" == *.gz ]]; then
    gunzip -t "$FILE" || fail "Archive gzip invalide"
  fi

  sudo mariadb -e "SELECT 1;" >/dev/null 2>&1 || fail "Connexion MariaDB impossible sans mot de passe"
  extract_sql | head -n 30 | grep -qiE 'CREATE|INSERT|USE|DROP|LOCK TABLES' || fail "Aucun SQL détecté"
  ok "Vérifications OK"
}

backup_current_db() {
  local db="$1"
  mkdir -p "$BACKUP_BEFORE_RESTORE"
  local out="$BACKUP_BEFORE_RESTORE/${db}_$(date +%F_%H-%M-%S).sql.gz"
  warn "Sauvegarde de sécurité: $db -> $out"
  sudo mariadb-dump --single-transaction --routines --events --triggers --hex-blob --databases "$db" | gzip -9 > "$out"
  ok "Sauvegarde créée: $out"
}

restore_db() {
  local db="$1"
  log "Restauration de $db"

  if [[ "$DRY_RUN" == true ]]; then
    warn "[DRY-RUN] $db serait restaurée"
    warn "[DRY-RUN] Tables détectées:"
    list_tables_for_db "$db" | sed 's/^/  - /'
    return 0
  fi

  backup_current_db "$db"

  if [[ "$FILE" == *.gz ]]; then
    gunzip -c "$FILE" | awk -v target="$db" '
      BEGIN {p=0}
      /^-- Current Database: / {p=0}
      /^CREATE DATABASE / {if ($0 ~ target) p=1}
      /^USE / {if ($0 ~ target) p=1}
      {if (p==1) print}
    ' | sudo mariadb "$db"
  else
    awk -v target="$db" '
      BEGIN {p=0}
      /^-- Current Database: / {p=0}
      /^CREATE DATABASE / {if ($0 ~ target) p=1}
      /^USE / {if ($0 ~ target) p=1}
      {if (p==1) print}
    ' "$FILE" | sudo mariadb "$db"
  fi

  ok "Restauration terminée: $db"
}

mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"

exec 9>"$LOCK_FILE"
flock -n 9 || fail "Une autre restauration est déjà en cours"

[[ $# -ge 1 ]] || { usage; exit 1; }

while [[ $# -gt 0 ]]; do
  case "$1" in
    --dry-run) DRY_RUN=true; shift ;;
    --all) RESTORE_ALL=true; shift ;;
    -h|--help) usage; exit 0 ;;
    *)
      if [[ -z "$FILE" ]]; then
        FILE="$1"
        shift
      else
        fail "Argument inattendu: $1"
      fi
      ;;
  esac
done

verify_env

mapfile -t DBS < <(list_databases)
[[ "${#DBS[@]}" -gt 0 ]] || fail "Aucune base détectée"

printf '\n%sBases détectées:%s\n' "$BLUE" "$RESET"
printf '  - %s\n' "${DBS[@]}"

SELECTED=()

if [[ "$RESTORE_ALL" == true ]]; then
  SELECTED=("${DBS[@]}")
else
  if command -v fzf >/dev/null 2>&1; then
    warn "Sélectionnez les bases avec Tab puis Entrée"
    CHOICES="$(printf '%s\n' "${DBS[@]}" | fzf -m --prompt='Bases > ')"
    [[ -n "$CHOICES" ]] || fail "Aucune base sélectionnée"
    mapfile -t SELECTED <<< "$CHOICES"
  else
    warn "fzf indisponible, saisie manuelle requise"
    read -r -p "Entrez les bases séparées par des espaces: " -a SELECTED
  fi
fi

printf '\n%sBases retenues:%s\n' "$BLUE" "$RESET"
printf '  - %s\n' "${SELECTED[@]}"

if [[ "$DRY_RUN" == true ]]; then
  warn "Mode dry-run activé"
  for db in "${SELECTED[@]}"; do
    warn "[DRY-RUN] $db"
    list_tables_for_db "$db" | sed 's/^/    table: /'
  done
  exit 0
fi

read -r -p "Confirmez la restauration ? (o/n): " confirm
[[ "$confirm" == "o" || "$confirm" == "O" ]] || fail "Restauration annulée"

for db in "${SELECTED[@]}"; do
  restore_db "$db"
done

ok "Toutes les restaurations sont terminées"

fzf reste pratique pour la sélection multiple et le mode --dry-run permet de valider avant import. thevaluable

Systemd service mariadb-backup

/etc/systemd/system/mariadb-backup.service

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=MariaDB backup
After=network.target mariadb.service
Requires=mariadb.service

[Service]
Type=oneshot
User=root
Group=root
ExecStart=/usr/local/sbin/mariadb-backup.sh
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7

Systemd timer mariadb-backup

/etc/systemd/system/mariadb-backup.timer

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=Daily MariaDB backup timer

[Timer]
OnCalendar=*-*-* 01:00:00
Persistent=true
Unit=mariadb-backup.service

[Install]
WantedBy=timers.target

oneshot avec OnCalendar est la combinaison appropriée pour une sauvegarde planifiée par systemd. dailystuff

README d’installation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Pack sauvegarde / restauration MariaDB

Ce pack permet :
- des sauvegardes automatiques via systemd timer,
- une restauration interactive avec `fzf`,
- un mode `--dry-run`,
- une sauvegarde de sécurité avant restauration.

## Prérequis
- MariaDB installé.
- `mariadb`, `mariadb-dump`, `gzip`.
- `fzf` pour la sélection interactive.

## Hypothèse
Le compte système `root` peut utiliser `mariadb` sans mot de passe via socket local.

Test :
```bash
sudo mariadb -e "SELECT 1;"

Installation Pack

1. Copier les scripts

1
2
sudo install -m 750 mariadb-backup.sh /usr/local/sbin/mariadb-backup.sh
sudo install -m 750 mariadb-restore-fzf.sh /usr/local/sbin/mariadb-restore-fzf.sh

2. Créer les dossiers

1
2
3
sudo mkdir -p /var/backups/mariadb/pre-restore
sudo mkdir -p /var/backups/mariadb
sudo touch /var/log/mariadb-backup.log /var/log/mariadb-restore.log

3. Installer systemd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo tee /etc/systemd/system/mariadb-backup.service >/dev/null <<'EOF'
[Unit]
Description=MariaDB backup
After=network.target mariadb.service
Requires=mariadb.service

[Service]
Type=oneshot
User=root
Group=root
ExecStart=/usr/local/sbin/mariadb-backup.sh
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOF
1
2
3
4
5
6
7
8
9
10
11
12
sudo tee /etc/systemd/system/mariadb-backup.timer >/dev/null <<'EOF'
[Unit]
Description=Daily MariaDB backup timer

[Timer]
OnCalendar=*-*-* 01:00:00
Persistent=true
Unit=mariadb-backup.service

[Install]
WantedBy=timers.target
EOF

4. Activer le timer

1
2
sudo systemctl daemon-reload
sudo systemctl enable --now mariadb-backup.timer

5. Vérification

1
2
systemctl list-timers --all | grep mariadb-backup
journalctl -u mariadb-backup.service -n 100 --no-pager

6. Sauvegarde manuelle

1
sudo /usr/local/sbin/mariadb-backup.sh

Sortie

1
2
3
4
5
6
7
8
[2026-06-10 22:24:55] Sauvegarde de authelia
[2026-06-10 22:24:55] OK: /var/backups/mariadb/alder_authelia_2026-06-10_22-24-55.sql.gz
[2026-06-10 22:24:55] Sauvegarde de git
[2026-06-10 22:24:55] OK: /var/backups/mariadb/alder_git_2026-06-10_22-24-55.sql.gz
[2026-06-10 22:24:55] Sauvegarde de nextcloud
[2026-06-10 22:24:56] OK: /var/backups/mariadb/alder_nextcloud_2026-06-10_22-24-55.sql.gz
[2026-06-10 22:24:56] Rotation terminée
[2026-06-10 22:24:56] Sauvegarde terminée

Restauration

Dry-run :

1
sudo /usr/local/sbin/mariadb-restore-fzf.sh /var/backups/mariadb/full.sql.gz --dry-run

Sélection interactive :

1
sudo /usr/local/sbin/mariadb-restore-fzf.sh /var/backups/mariadb/full.sql.gz

Tout restaurer :

1
sudo /usr/local/sbin/mariadb-restore-fzf.sh /var/backups/mariadb/full.sql.gz --all

Rotation

Les sauvegardes de plus de 14 jours sont supprimées automatiquement.

Sécurité

Chaque restauration crée une sauvegarde de sécurité dans :

1
/var/backups/mariadb/pre-restore/
Cet article est sous licence CC BY 4.0 par l'auteur.