Borg prune et compact : libérer l'espace disque
Pourquoi borg prune ne libère pas d'espace, comment les règles de rétention choisissent les archives, et quand lancer borg compact et borg check.
Ce que borg prune fait, et ce qu'il ne fait pas
borg prune choisit les archives à supprimer d'après vos règles de rétention, et borg compact rend l'espace disque au système. Depuis Borg 1.2, ce sont deux commandes distinctes : après un borg prune, la taille du dépôt mesurée par du -sh ne bouge presque pas, et c'est le comportement attendu. Le plan est simple : construire un petit dépôt local avec 200 archives datées, regarder ce que borg prune --list --dry-run décide, lancer prune puis compact en mesurant entre les deux, et finir avec borg check et un calendrier pour les trois commandes.
Le point de départ est un borg create nocturne qui fonctionne déjà. Le dépôt grossit chaque nuit et la question devient : garder un historique utile sans remplir le disque. Chacune des trois commandes a un comportement qui surprend la première fois. Les options --keep-* ne comptent pas des archives, elles comptent des périodes. prune ne libère rien. check ne répare rien tant que vous ne le lui demandez pas, et il vaut mieux ne pas le lui demander à la légère.
Tout ce qui suit utilise un chemin de dépôt local. Un dépôt distant joint par SSH obéit exactement aux mêmes règles, seule l'adresse du dépôt change. Si vous hésitez encore entre les deux outils du moment, la comparaison entre restic et BorgBackup montre que restic sépare lui aussi forget et prune, pour la même raison que celle expliquée plus bas.
Quelle version de Borg est installée ?
borg --versionSur Ubuntu 24.04, le paquet borgbackup installe Borg 1.2.7, et c'est la version contre laquelle chaque commande de ce guide a été vérifiée, en septembre 2026. Si la commande n'existe pas encore : sudo apt install -y borgbackup. Borg 1.4, que les distributions plus récentes empaquettent, se comporte de la même façon pour prune, compact et check. La séparation entre prune et compact date de la version 1.2 : sur un vieux Borg 1.1, prune libère l'espace lui-même et la commande borg compact n'existe pas. Vérifiez donc ce numéro avant d'appliquer la suite à un serveur installé il y a quelques années.
Comment Borg applique --keep-daily, --keep-weekly et --keep-monthly
--keep-daily 7 ne veut pas dire « garder les 7 dernières archives ». Il veut dire : garder la dernière archive de chacun des 7 derniers jours qui contiennent au moins une archive. Borg parcourt les archives de la plus récente à la plus ancienne, calcule la période de chacune (le jour, la semaine ISO ou le mois) et garde la première archive rencontrée dans chaque nouvelle période, jusqu'à atteindre le nombre demandé. Un jour sans sauvegarde ne compte pas. Si le serveur est resté éteint une semaine, --keep-daily 7 remonte plus loin dans le temps pour trouver 7 jours distincts, et vous ne perdez pas d'historique à cause de la panne.
Deux conséquences pratiques. D'abord, plusieurs archives créées le même jour comptent pour une seule, et seule la plus récente est retenue par la règle daily. Ensuite, les règles se cumulent sans se compter deux fois : une semaine déjà représentée par une archive retenue par daily est bien consommée par la règle weekly, mais elle n'entre pas dans son décompte de 4. Autrement dit, --keep-weekly 4 garde 4 semaines en plus de celles que daily couvre déjà. Même logique pour monthly : un mois déjà représenté ne compte pas dans les 6. Au total, ces trois règles retiennent 17 archives, pas 7.
Les périodes sont calculées dans le fuseau horaire local du serveur qui lance prune. Une sauvegarde à 23 h 30 heure de Paris et une autre à 00 h 30 tombent dans deux jours différents, même si une heure seulement les sépare. Ce n'est un problème que si vous sauvegardez près de minuit ; une exécution à 02 h 00 évite la question.
Construire un petit dépôt local pour tester la rétention
Il faut un dépôt jetable avec un long historique. borg create accepte --timestamp, ce qui permet de fabriquer 200 archives datées d'un jour chacune sans attendre 200 nuits. La phrase d'accès est exportée dans BORG_PASSPHRASE pour que rien ne pose de question, ce qui est aussi la façon dont un script nocturne fonctionne.
export BORG_PASSPHRASE='demo-a-jeter'
export BORG_REPO=~/borg-demo/repo
mkdir -p ~/borg-demo/data
borg init --encryption=repokeyBORG_REPO évite de répéter le chemin : la forme ::nom-archive désigne alors une archive de ce dépôt, et les commandes sans argument agissent sur lui. borg init rappelle d'exporter la clé avec borg key export, parce qu'en mode repokey la clé chiffrée est stockée dans le fichier config du dépôt, et un dépôt dont ce fichier est abîmé est illisible même avec la bonne phrase. Pour un vrai dépôt, gardez la phrase dans un fichier en mode 600 et lisez-la depuis le script ; le guide sur le chiffrement des données d'un VPS de stockage détaille où ranger cette clé. --encryption=none fonctionne aussi pour ce test, mais uniquement pour un test.
La date est fixée en dur au 16 septembre 2026, et non lue avec date +%F, pour que les noms d'archives et les comptes de ce guide restent identiques quel que soit le jour où vous le suivez. Chaque « nuit » réécrit un fichier de 1 Mo de données aléatoires, donc chaque archive contient un morceau que les autres n'ont pas. C'est ce qui rendra l'effet de compact visible. Le fichier config.txt, lui, ne change jamais et sera dédupliqué 200 fois.
echo 'fichier stable, identique dans toutes les archives' > ~/borg-demo/data/config.txt
for i in $(seq 199 -1 0); do
day=$(date -u -d "2026-09-16 -$i day" +%F)
head -c 1M /dev/urandom > ~/borg-demo/data/etat.bin
borg create --timestamp "${day}T02:00:00" "::srv-$day" ~/borg-demo/data
done
borg list | wc -l
du -sh "$BORG_REPO"borg list doit compter 200 lignes, une par archive, de srv-2026-03-01 à srv-2026-09-16. Notez le chiffre de du -sh : c'est la mesure « avant ». Sur ce jeu de données, il est proche de 200 fois 1 Mo, puisque des données aléatoires ne se compressent pas et ne se dédupliquent pas. La boucle prend quelques minutes selon le disque : chaque borg create ouvre le dépôt, écrit un nouveau segment et valide une transaction. --timestamp est interprété en UTC ; sur un serveur réglé sur l'heure de Paris, borg list affichera donc 04:00:00 et non 02:00:00, et cela ne change rien aux comptes qui suivent.
Lire la sortie de borg prune --list --dry-run
Avant de supprimer quoi que ce soit, demandez à Borg ce qu'il ferait :
borg prune --list --dry-run --keep-daily 7 --keep-weekly 4 --keep-monthly 6Borg écrit cette liste sur la sortie d'erreur, comme tous ses messages, une ligne par archive, de la plus récente à la plus ancienne. Chaque ligne retenue nomme la règle et son rang, chaque ligne condamnée commence par Would prune:. Le début ressemble à ceci sur un serveur en UTC, l'identifiant hexadécimal de chaque archive étant remplacé par [...] :
Keeping archive (rule: daily #1): srv-2026-09-16 Wed, 2026-09-16 02:00:00 [...]
Keeping archive (rule: daily #2): srv-2026-09-15 Tue, 2026-09-15 02:00:00 [...]
Keeping archive (rule: daily #3): srv-2026-09-14 Mon, 2026-09-14 02:00:00 [...]
Keeping archive (rule: daily #4): srv-2026-09-13 Sun, 2026-09-13 02:00:00 [...]
Keeping archive (rule: daily #5): srv-2026-09-12 Sat, 2026-09-12 02:00:00 [...]
Keeping archive (rule: daily #6): srv-2026-09-11 Fri, 2026-09-11 02:00:00 [...]
Keeping archive (rule: daily #7): srv-2026-09-10 Thu, 2026-09-10 02:00:00 [...]
Would prune: srv-2026-09-09 Wed, 2026-09-09 02:00:00 [...]
Would prune: srv-2026-09-08 Tue, 2026-09-08 02:00:00 [...]
Would prune: srv-2026-09-07 Mon, 2026-09-07 02:00:00 [...]
Keeping archive (rule: weekly #1): srv-2026-09-06 Sun, 2026-09-06 02:00:00 [...]Lisez ce que la règle weekly a fait. Les semaines ISO du 14 septembre et du 7 septembre contiennent déjà une archive retenue par daily, elles ne comptent donc pas. La première archive retenue par weekly est celle du dimanche 6 septembre, la dernière de sa semaine, puis viennent le 30 août, le 23 août et le 16 août. La règle monthly saute septembre pour la même raison et retient le 31 août, le 31 juillet, le 30 juin, le 31 mai, le 30 avril et le 31 mars. Tout mars sauf le 31 est à supprimer.
Comptez pour vérifier :
borg prune --list --dry-run --keep-daily 7 --keep-weekly 4 --keep-monthly 6 2> ~/borg-demo/prune-dry-run.txt
grep -c 'Keeping archive' ~/borg-demo/prune-dry-run.txt
grep -c 'Would prune' ~/borg-demo/prune-dry-run.txtLe premier compte donne 17 (7 + 4 + 6), le second 183. La redirection 2> est nécessaire parce que Borg écrit cette liste sur la sortie d'erreur et non sur la sortie standard ; sans elle, le fichier ne serait jamais écrit et la liste défilerait simplement à l'écran.
Deux garde-fous à connaître. Sans aucune option --keep-*, Borg refuse de partir avec un message qui commence par At least one of the "keep-within" : il ne supprime jamais tout par défaut. Et si plusieurs machines écrivent dans le même dépôt, prune sans filtre applique vos règles à leurs archives aussi. Limitez-le avec -a 'srv-*' (--glob-archives), qui ne considère que les archives dont le nom correspond au motif.
Pourquoi borg prune ne libère pas d'espace disque
Lancez-le pour de vrai, puis mesurez :
borg prune --list --stats --keep-daily 7 --keep-weekly 4 --keep-monthly 6
borg list | wc -l
du -sh "$BORG_REPO"borg list compte maintenant 17 archives. --stats affiche un tableau dont la ligne Deleted data: porte des tailles négatives : la quantité de données que les 183 archives supprimées référençaient. Et pourtant du -sh affiche presque le même chiffre qu'avant. Ce n'est pas un bug.
Un dépôt Borg est une suite de fichiers appelés segments, rangés sous data/0/ dans le dossier du dépôt, et Borg les écrit en mode ajout : il place de nouveaux morceaux (chunks) à la fin d'un segment et ne modifie jamais un segment existant en place. Chaque morceau est compté : l'index du dépôt sait combien d'archives l'utilisent. prune supprime les métadonnées de chaque archive condamnée et décrémente ces compteurs. Un morceau dont le compteur tombe à zéro est marqué inutilisé dans l'index, mais ses octets restent dans le segment, entre d'autres morceaux encore vivants. Le fichier ne rétrécit pas, donc du non plus.
Ce choix rend prune rapide et sûr : il n'écrit presque rien, et une coupure au milieu ne casse rien. Le vrai travail, réécrire les morceaux vivants dans de nouveaux segments puis effacer les anciens, est confié à borg compact, que vous lancez quand cela vous arrange, et pas forcément chaque nuit.
Lancer borg compact et mesurer avant et après
borg compact -v
du -sh "$BORG_REPO"Sans -v, borg compact ne dit rien du tout, même quand il libère des gigaoctets, et beaucoup concluent qu'il n'a rien fait. Avec -v (ou --info), il termine par une ligne qui commence par compaction freed about et se termine par repository space., avec entre les deux la taille rendue. Le chiffre est le vôtre : sur ce dépôt de démonstration, il correspond aux 183 fichiers aléatoires de 1 Mo dont plus aucune archive n'a besoin. Comparez ensuite les trois du -sh : le premier et le deuxième sont presque identiques, le troisième est proche de 17 fois 1 Mo. prune n'a rien rendu, compact a tout rendu.
compact obéit à un seuil. Par défaut, un segment dont moins de 10 % du contenu est inutilisé n'est pas réécrit, parce que le gain ne vaut pas le coût d'écriture ; --threshold 5 abaisse ce seuil. Sur un dépôt dont on vient d'élaguer deux ou trois archives, il est donc normal que compact ne rende presque rien : les segments récents sont encore pleins de morceaux vivants. Il a aussi besoin d'espace libre, puisqu'il écrit les nouveaux segments avant d'effacer les anciens. Un disque plein à 100 % est le pire moment pour découvrir cette commande : libérez d'abord autre chose. Un dépôt créé avec Borg 1.1 mérite en plus, une seule fois, borg compact --cleanup-commits, qui supprime les milliers de petits segments de validation que l'ancien format laissait derrière lui.
Si, après compact, du a diminué mais que df ne bouge pas, ce n'est plus un problème Borg : un processus retient un fichier supprimé, et le guide sur le désaccord entre df et du explique comment le trouver.
borg check : la vérification périodique
borg check -v
echo $?borg check fait deux passes. La première lit tous les segments et vérifie que chaque entrée est intacte et que l'index correspond au contenu réel ; elle se termine par Finished full repository check, no problems found. La seconde ouvre les métadonnées de chaque archive et vérifie que chaque morceau référencé existe bien ; elle se termine par Archive consistency check complete, no problems found. Sans -v, un check réussi n'affiche rien, et le code de retour vaut 0. Un code 1 signale un avertissement, un code 2 une erreur : c'est ce qu'un script doit tester.
La durée est proportionnelle à la taille du dépôt, parce que la première passe lit chaque segment en entier. Le check de base ne déchiffre pas les données ; --verify-data le fait et compare chaque morceau à son empreinte, ce qui est beaucoup plus lent et se réserve à quelques fois par an, ou à un doute sur le disque. --repository-only et --archives-only permettent de séparer les deux passes si la fenêtre de maintenance est courte.
--repair est un dernier recours, pas une routine. Il modifie le dépôt : un morceau manquant est remplacé par un morceau de remplacement rempli de zéros, et l'archive qui le référençait est marquée pour qu'une réparation ultérieure puisse la compléter si le morceau réapparaît. Avant de le lancer, copiez le dépôt ailleurs, et ne le lancez jamais sur un disque qui affiche déjà des erreurs d'entrée-sortie, parce qu'écrire sur un disque mourant aggrave la perte. La commande pose une question et attend YES en toutes lettres ; dans un script, la variable BORG_CHECK_I_KNOW_WHAT_I_AM_DOING=YES remplace la réponse, ce qui est précisément la raison de ne pas la mettre dans un script. Un snapshot du fournisseur n'est pas une sauvegarde, mais c'est une bonne précaution juste avant un --repair sur un VPS.
Un check et un compact refusent de démarrer pendant qu'un create tient le verrou du dépôt, avec un message Failed to create/acquire the lock suivi du chemin de lock.exclusive et de (timeout). Par défaut, Borg n'attend qu'une seconde. Dans un script de maintenance, --lock-wait 600 lui fait attendre jusqu'à dix minutes qu'une sauvegarde en cours finisse. borg break-lock casse un verrou abandonné par un processus tué, et seulement dans ce cas : cassé pendant un create réel, il expose le dépôt à deux écritures simultanées.
Un calendrier raisonnable pour les trois commandes
prune coûte quelques secondes et va juste après create, dans le même script nocturne : la rétention est appliquée chaque nuit et le nombre d'archives reste stable. compact réécrit des segments et coûte des entrées-sorties proportionnelles à ce qu'il libère ; une fois par semaine, à une heure creuse, suffit. check lit tout le dépôt ; une fois par mois, avec --verify-data une ou deux fois par an. Les trois commandes tiennent dans deux scripts.
#!/bin/bash
# /usr/local/bin/borg-nightly.sh
export BORG_REPO=/srv/borg/repo
BORG_PASSPHRASE="$(cat /root/.borg-passphrase)"
export BORG_PASSPHRASE
borg create --stats "::srv-{now:%Y-%m-%d}" /etc /home /var/backups
rc=$?
if [ "$rc" -eq 2 ]; then
echo "borg create a echoue, prune annule"
exit 2
fi
borg prune -a 'srv-*' --keep-daily 7 --keep-weekly 4 --keep-monthly 6Le code de retour 1 de borg create signale un avertissement, souvent un fichier modifié pendant la lecture, et ne doit pas empêcher prune ; seul le code 2 est une erreur qui arrête le script. C'est la raison pour laquelle ce script n'utilise pas set -e. Le motif {now:%Y-%m-%d} est développé par Borg lui-même, et le filtre -a 'srv-*' garantit que prune ne touche qu'aux archives de ce serveur.
#!/bin/bash
# /usr/local/bin/borg-maintenance.sh : argument "compact" ou "check"
export BORG_REPO=/srv/borg/repo
BORG_PASSPHRASE="$(cat /root/.borg-passphrase)"
export BORG_PASSPHRASE
case "$1" in
compact) exec borg compact -v --lock-wait 600 ;;
check) exec borg check -v --lock-wait 600 ;;
*) echo "usage: $0 compact|check"; exit 2 ;;
esacUn timer systemd par tâche appelle ce script avec le bon argument :
# /etc/systemd/system/borg-compact.service
[Unit]
Description=borg compact hebdomadaire
[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-maintenance.sh compact# /etc/systemd/system/borg-compact.timer
[Unit]
Description=borg compact chaque dimanche
[Timer]
OnCalendar=Sun *-*-* 04:00:00
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now borg-compact.timer
systemctl list-timers 'borg-*'Même paire pour check, avec OnCalendar=*-*-01 05:00:00 et l'argument check. Persistent=true rattrape une exécution manquée au prochain démarrage. Placez check à 05 h 00 et compact à 04 h 00 pour qu'ils ne se chevauchent pas la nuit du 1er, et --lock-wait couvre le reste. Le guide sur les services et timers systemd explique la syntaxe OnCalendar, la lecture de systemctl list-timers et celle des journaux quand un timer n'a pas tourné.
Le dépôt de démonstration se supprime avec rm -rf ~/borg-demo, et n'oubliez pas de retirer BORG_PASSPHRASE et BORG_REPO de votre shell avant de travailler sur le vrai dépôt : une commande borg lancée avec les variables du test pointerait sur un dépôt qui n'existe plus.
FAQ
Pourquoi borg prune ne libère-t-il pas d'espace disque ?
Depuis Borg 1.2, prune supprime seulement les métadonnées des archives et marque leurs morceaux comme inutilisés dans l'index du dépôt. Les octets restent dans les fichiers segments, entre des morceaux encore utilisés par d'autres archives, donc du -sh ne change presque pas. C'est borg compact qui réécrit les morceaux vivants dans de nouveaux segments et efface les anciens. Lancez-le avec -v pour voir la ligne compaction freed about ... repository space. ; sans cette option, il travaille en silence.
--keep-daily 7 garde-t-il les 7 dernières archives ?
Non. Il garde la dernière archive de chacun des 7 derniers jours qui ont au moins une archive. Un jour sans sauvegarde ne compte pas, donc après une panne d'une semaine la règle remonte plus loin pour trouver 7 jours distincts. Les règles weekly et monthly s'ajoutent sans compter deux fois une période déjà couverte : --keep-daily 7 --keep-weekly 4 --keep-monthly 6 retient 17 archives sur un historique quotidien assez long. Vérifiez toujours avec borg prune --list --dry-run avant la première exécution réelle.
À quelle fréquence lancer borg compact et borg check ?
prune chaque nuit, juste après create, parce qu'il coûte quelques secondes. compact une fois par semaine à une heure creuse, parce qu'il réécrit des segments et a besoin d'espace libre pour le faire. check une fois par mois, et check --verify-data une ou deux fois par an ou dès qu'un doute sur le disque apparaît. Donnez --lock-wait 600 aux deux tâches de maintenance pour qu'elles attendent la fin d'une sauvegarde en cours au lieu d'échouer sur le verrou.
Quand faut-il utiliser borg check --repair ?
Seulement quand un borg check sans option a signalé un problème, que vous avez une copie du dépôt et que le disque ne montre pas d'erreurs d'entrée-sortie. --repair remplace les morceaux manquants par des morceaux de zéros et modifie le dépôt de façon irréversible, ce qui peut transformer une archive partiellement lisible en archive abîmée. Il demande la réponse YES en interactif, et la variable BORG_CHECK_I_KNOW_WHAT_I_AM_DOING=YES ne doit jamais figurer dans un script planifié.