Source docs/backup.fr.md · 1de96aa

Sauvegarde et restauration

Cette page s’adresse aux administrateurs. Elle explique ce qu’il faut sauvegarder, comment faire une sauvegarde à la main ou automatiquement chaque nuit, comment prouver qu’une sauvegarde peut être restaurée, et comment restaurer une installation ou revenir à la version précédente après une mise à jour ratée.

Une sauvegarde ne vaut quelque chose qu’une fois que vous l’avez restaurée. Prévoyez un test de restauration dès le départ.

Ce qu’il faut sauvegarder

QuoiOùPourquoi
.envà côté de docker-compose.ymlContient SECRET_KEY, qui déchiffre chaque clé de provider stockée, ainsi que le mot de passe de la base de données. Sans lui, une installation restaurée redemande chaque clé de provider.
La base de donnéesvolume libris_database (epub-translator_… sur une installation pas encore renommée)Comptes, séries, livres, traductions, historique, glossaires, mémoire, travaux et réglages. Sauvegardez-la avec pg_dump, pas en copiant le volume.
Le volume des livresvolume libris_books, monté sur /dataEPUB d’origine (books/), sources texte et JSON (sources/), exports, résultats d’API livrés et imports en attente. Sans lui, une installation restaurée peut encore exporter du texte, mais pas d’EPUB, d’aperçus ni d’archives de projet.
État Codex (facultatif)volume libris_codex-stateConnexion des providers Codex · compte ChatGPT. Sans lui, reconnectez-vous.
OpenViking (facultatif)votre serveur OpenVikingNe fait pas partie de Libris ; sauvegardez-le selon sa propre procédure. Libris peut republier sa mémoire à partir de la base de données : après une restauration, Reconstruire réécrit les documents d’un volume que le nettoyage OpenViking a supprimés.

La base de données et le volume des livres vont ensemble : restaurez toujours les deux à partir de la même sauvegarde.

Pour sauvegarder un seul livre avec tout son travail, exportez-le plutôt comme archive de projet (Exporter › Projet complet (.zip)) et restaurez-le depuis la bibliothèque (Ajouter du contenu › Restaurer une archive Libris). Une série entière — ses volumes, son glossaire, ses identités et leurs liens, sa Series Bible et ses réglages — s’exporte en un seul fichier avec Exporter la série sur la page de la série et se restaure avec Ajouter du contenu › Restaurer une archive Libris › Une série complète (format dans architecture.md (en anglais)). Ni l’une ni l’autre ne remplace une sauvegarde complète : les comptes, les providers, les clés et les glossaires partagés n’y figurent pas.

Sauvegarder à la main

Exécutez ces commandes dans le dossier de Libris. Elles arrêtent l’application web et le worker le temps de la copie, afin que la base de données et les fichiers concordent.

mkdir -p backups && chmod 700 backups
docker compose stop api worker
docker compose exec -T database sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' > backups/database.dump
docker compose run --rm --no-deps -T api tar -C /data -czf - . > backups/books.tar.gz
cp .env backups/config.env
chmod 600 backups/*
docker compose up -d --no-build --wait

Copiez le dossier backups sur une autre machine. Une sauvegarde conservée sur le même disque ne survit pas à la perte de ce disque.

Sur un hôte piloté par le script de déploiement de production (en anglais), librisctl backup effectue seul le dump de la base de données (sans rien arrêter) et affiche son chemin ; le volume des livres et la configuration sont couverts par la sauvegarde planifiée ci-dessous.

Sauvegarder chaque nuit

backup/libris-backup fait une sauvegarde vérifiée en gelant l’API et le worker le temps du dump et de l’archive (de quelques secondes à quelques minutes), l’écrit sur un partage monté depuis une autre machine et supprime les anciennes sauvegardes. Un minuteur systemd l’exécute chaque nuit. Rien n’est installé automatiquement.

L’installer

En root sur l’hôte Docker, depuis le dossier de l’installation du registre (par défaut /opt/libris) :

install -m 0755 backup/libris-backup backup/libris-restore /usr/local/sbin/
install -m 0644 backup/libris-backup.service backup/libris-backup.timer /etc/systemd/system/
install -m 0600 backup/libris-backup.conf.example /etc/libris-backup.conf
editor /etc/libris-backup.conf
systemctl daemon-reload
  1. Montez de façon permanente un partage depuis une autre machine (NFS, SMB, sshfs…), par exemple sur /mnt/libris-backup, et créez-y un dossier de destination. Root doit pouvoir y écrire, les permissions des fichiers doivent être conservées (les sauvegardes sont créées en 0600), et il faut de la place pour autant de sauvegardes que vous en conservez.

  2. Modifiez /etc/libris-backup.conf (voir le tableau ci-dessous). Définissez LIBRIS_BACKUP_MOUNTPOINT pour que la sauvegarde échoue, au lieu de remplir silencieusement le disque local, quand le partage n’est pas monté.

  3. Lancez une première sauvegarde et lisez son résultat :

    systemctl start libris-backup.service
    journalctl -u libris-backup.service -n 20
    
  4. Activez l’exécution nocturne (à 03 h 30, avec jusqu’à 20 minutes de délai aléatoire ; une exécution manquée pendant que la machine était éteinte a lieu au démarrage suivant) :

    systemctl enable --now libris-backup.timer
    systemctl list-timers libris-backup.timer
    
  5. Surveillez les échecs : vérifiez systemctl is-failed libris-backup.service dans votre supervision, ou ajoutez un fichier complémentaire (drop-in) OnFailure= qui appelle votre unité de notification habituelle.

Réglages

VariableDéfautSignification
LIBRIS_BACKUP_DESTINATIONaucun (obligatoire)Dossier qui reçoit un dossier libris-<UTC timestamp>/ par sauvegarde, par exemple /mnt/libris-backup/libris.
LIBRIS_BACKUP_MOUNTPOINTvideSi elle est définie, la sauvegarde échoue quand ce chemin n’est pas un système de fichiers monté.
LIBRIS_BACKUP_RETENTION_DAYS14Les sauvegardes plus anciennes que ce nombre de jours sont supprimées après chaque sauvegarde réussie.
LIBRIS_BACKUP_PROJECTvaleur renseignée par l’installateur ; sinon $LIBRIS_PRODUCTION_BASE/project, puis volume libris ou epub-translator détectéProjet Compose de l’installation (le renommer (en anglais) écrit ce fichier).
LIBRIS_BACKUP_BOOKS_VOLUME<project>_booksVolume des livres à archiver.
LIBRIS_BACKUP_FREEZEtruefalse n’interrompt plus l’API ni le worker (déconseillé : tout changement de fichier pendant l’exécution fait échouer la sauvegarde).
LIBRIS_BACKUP_INCLUDE_ENVfalsetrue copie .env dans chaque sauvegarde sous le nom config.env. Uniquement si la destination peut contenir tous les secrets de l’installation.
LIBRIS_BACKUP_SECRET_ENVchemin renseigné par l’installateur du registre ; sinon $LIBRIS_PRODUCTION_BASE/.env, puis l’ancien /opt/epub-translator/.envChemin du .env de l’installation, copié quand LIBRIS_BACKUP_INCLUDE_ENV=true. Vérifiez ce chemin avant d’activer la copie des secrets.
LIBRIS_PRODUCTION_BASE/opt/libris-productionEmplacement où le script de déploiement de production (en anglais) enregistre la version déployée, inscrite dans backup.info. Sans conséquence s’il est absent.

Quand .env n’est pas inclus, sauvegardez-le vous-même en lieu sûr : il change rarement.

Contenu d’une sauvegarde

FichierContenu
database.dumppg_dump -Fc de toute la base de données, cohérent, pris pendant que l’API et le worker sont gelés
books.tar.gzle volume des livres complet
books.sha256SHA-256 de chaque fichier de books.tar.gz ; libris-restore y compare le volume restauré
config.envune copie de .env, seulement avec LIBRIS_BACKUP_INCLUDE_ENV=true
backup.infodate, projet Compose, volume, image de la base de données et version de Libris déployée
SHA256SUMSsommes de contrôle des fichiers ci-dessus

Le dump et l’archive sont pris à des instants différents, et l’API et le worker ajoutent, remplacent et suppriment des fichiers (EPUB, sources, couvertures, exports) après avoir validé leurs lignes. Le script exécute donc docker pause sur les conteneurs api et worker avant le dump et les reprend dès l’archive écrite (aussi en cas d’échec) : les requêtes attendent, rien n’est refusé ni perdu, et aucun fichier ne change entre les deux phases. Il liste le volume (chemin, taille, date de modification) avant le dump et après l’archive ; si un fichier a été ajouté, supprimé ou remplacé entre-temps, la sauvegarde échoue avec files of … changed while the backup ran, ne garde rien et n’affiche jamais « written and verified ». Les deux fichiers sont ensuite entièrement relus, et le hash de chaque fichier archivé est écrit dans books.sha256 ; l’archive doit contenir exactement les fichiers du volume.

Limites : le gel couvre les deux conteneurs du projet Compose, pas une commande librisctl ni un autre conteneur qui écrirait dans le volume (le contrôle du volume les détecte, et il suffit de relancer) ; le contrôle compare la taille et la date de modification, pas le contenu, donc un fichier réécrit avec la même taille et la même date passe inaperçu ; avec LIBRIS_BACKUP_FREEZE=false, une sauvegarde sur une installation active échoue dès qu’un fichier change pendant l’exécution. Les conteneurs gelés gardent leurs connexions à la base et leurs contrôles de santé s’arrêtent : une pause longue peut les faire apparaître comme défaillants jusqu’à leur reprise. Une sauvegarde avant mise à jour doit toujours arrêter les services d’abord. La sauvegarde est écrite dans un dossier caché .libris-….partial et renommée seulement à la fin : une exécution interrompue ne ressemble donc jamais à une sauvegarde. Les anciennes sauvegardes ne sont supprimées qu’après une sauvegarde réussie : une exécution en échec ne supprime jamais les précédentes. Tout échec fait échouer l’unité systemd.

Tester une restauration chaque mois

libris-restore restaure une sauvegarde dans un projet Compose distinct et jetable, et vérifie qu’elle fonctionne. Il ne touche jamais à votre installation et ne démarre jamais le worker, qui reprendrait les travaux sauvegardés et appellerait vos providers de modèles.

cd /opt/libris
project="$(sed -n 's/^LIBRIS_PROJECT=//p' .env | head -n 1)"
export LIBRIS_PRODUCTION_PROJECT="${project:-libris}"
export LIBRIS_RESTORE_COMPOSE_FILE="$PWD/docker-compose.yml"
export LIBRIS_RESTORE_ENV_FILE="$PWD/.env"
export LIBRIS_RESTORE_IMAGE="$(sed -n 's/^LIBRIS_IMAGE=//p' .env | head -n 1)"
backup/libris-restore --dry-run /mnt/libris-backup/libris/libris-<timestamp>
backup/libris-restore /mnt/libris-backup/libris/libris-<timestamp>
  • --dry-run vérifie les sommes de contrôle et l’archive, et affiche chaque commande Docker sans l’exécuter.
  • L’exécution réelle crée le projet libris-restore-test avec ses propres volumes, publie l’API sur un port aléatoire de la boucle locale, restaure la base de données et les livres, exécute les migrations et l’API, puis vérifie alembic check et /health et affiche le nombre d’utilisateurs, de livres, de passages, de fichiers de livres et de fichiers sources.
  • Le projet et ses volumes sont supprimés à la fin. Avec --keep, ils restent en place pour inspection : le script affiche la commande Compose du projet de test (avec ses --file et --env-file) ; ajoutez-y port api 8088 pour trouver le port, connectez-vous, puis exécutez-la avec down --volumes pour supprimer le projet.

Comparez les nombres avec ceux de votre installation et notez la date du test.

Pour une installation du registre, le script se trouve dans backup/. Les variables ci-dessus indiquent le vrai projet Compose (libris par défaut), le fichier de configuration et l’image épinglée. Pour une installation pilotée par le script de production, les valeurs par défaut restent celles du script de déploiement (en anglais).

VariableDéfautSignification
LIBRIS_RESTORE_PROJECTlibris-restore-testNom du projet jetable. Le nom du projet de production est refusé.
LIBRIS_RESTORE_COMPOSE_FILE$LIBRIS_PRODUCTION_BASE/docker-compose.ymlFichier Compose à utiliser.
LIBRIS_RESTORE_ENV_FILEle config.env de la sauvegarde, sinon LIBRIS_PRODUCTION_SECRET_ENV, sinon $LIBRIS_PRODUCTION_BASE/.env ou l’ancien /opt/epub-translator/.envConfiguration à utiliser.
LIBRIS_RESTORE_IMAGEl’image enregistrée par le dernier déploiement de productionImage de l’application avec laquelle restaurer. Utilisez la version qui a fait la sauvegarde, ou une plus récente.
LIBRIS_PRODUCTION_PROJECTepub-translatorLe projet auquel le script refuse de toucher.

Restaurer une installation

Faites-le après une perte de données, ou pour revenir à l’état antérieur à une mise à jour ratée. Utilisez le même .env (au moins la même SECRET_KEY) et une version de l’application au moins aussi récente que celle qui a fait la sauvegarde.

  1. Mettez en pause les livres en cours dans l’interface si Libris tourne encore.

  2. Vérifiez la sauvegarde :

    backup=/mnt/libris-backup/libris/libris-<timestamp>
    (cd "$backup" && sha256sum --check SHA256SUMS)
    
  3. Confirmez qu’il est acceptable de perdre les modifications de la base de données faites depuis cette sauvegarde. Sur une installation gérée par le récepteur de production, utilisez la CLI d’exploitation actuelle :

    librisctl restore "$backup/database.dump" --confirm --no-start
    

    Elle valide l’archive avant d’arrêter les services, fait un dump de secours privé, remplace la base de données de l’application et laisse les services arrêtés. Le dump de secours n’est pas supprimé automatiquement. Avant un retour arrière, gardez --no-start : sinon, l’image actuelle réapplique ses migrations.

    Sans cette CLI, utilisez une base de données neuve, et non pg_restore --clean par-dessus un schéma plus récent. --clean ne supprime que les objets listés dans l’ancienne archive ; les tables et clés étrangères ajoutées depuis (par exemple review_links) peuvent la bloquer ou subsister. Depuis le dossier Libris exact, vérifiez le projet Compose et la base de données visés, puis n’exécutez chaque étape que si la précédente a réussi :

    docker compose exec -T database pg_restore --list < "$backup/database.dump" > /dev/null
    docker compose stop api worker
    mkdir -p backups && chmod 700 backups
    rescue=$(mktemp backups/pre-restore-XXXXXXXX.dump)
    chmod 600 "$rescue"
    docker compose exec -T database sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' > "$rescue"
    test -s "$rescue"
    docker compose exec -T database sh -c 'case "$POSTGRES_DB" in ""|postgres|template0|template1) exit 64;; esac; dropdb -U "$POSTGRES_USER" -- "$POSTGRES_DB" && createdb -U "$POSTGRES_USER" --owner "$POSTGRES_USER" --template template0 -- "$POSTGRES_DB"'
    docker compose exec -T database sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --no-owner --exit-on-error' < "$backup/database.dump"
    

    Cela remplace la base de données dédiée à l’application, y compris les objets absents du dump. Les autres connexions doivent être fermées ; la commande ne les déconnecte pas de force. La base recréée utilise la locale et l’encodage par défaut du cluster, ainsi que le propriétaire PostgreSQL de l’installation. Si la base de données a une locale personnalisée, des extensions ou des objets appartenant à d’autres applications, faites d’abord adapter la procédure par son exploitant. Une restauration échouée laisse les services arrêtés ; conservez les deux dumps.

  4. Restaurez aussi les livres s’ils ont été perdus ou endommagés. Cela remplace tout le contenu du volume :

    docker compose run --rm --no-deps -T api sh -c 'find /data -mindepth 1 -delete && tar -C /data -xzf -' < "$backup/books.tar.gz"
    
  5. Redémarrez Libris et vérifiez-le :

    docker compose up -d --no-build --wait
    docker compose exec -T api alembic check
    

    Connectez-vous ensuite, ouvrez l’aperçu d’un livre, testez la connexion d’un provider et exportez un EPUB.

Si .env a lui aussi été perdu, restaurez-le d’abord à partir de config.env si vos sauvegardes l’incluent. Sinon, créez-en un nouveau avec python3 scripts/setup.py (le mot de passe de la base de données doit alors être celui du volume restauré) et saisissez à nouveau chaque clé de provider.

Pour reconstruire une installation du registre sauvegardée avec Libris 0.24.0 ou ultérieure sur une nouvelle machine, placez le .env sauvegardé avant tout démarrage. Cette séquence exige que l’image de la sauvegarde contienne l’installateur --no-start (introduit en 0.24.0). Pour une sauvegarde plus ancienne, suivez la procédure de sa version ; n’exécutez pas cette séquence avec son ancien installateur. LIBRIS_IMAGE doit contenir le digest sha256 complet de l’image du registre Libris. L’installateur lit cette image, préserve le .env et en extrait les scripts de sauvegarde. Exécutez :

Ouvrez une session root et exécutez-y toute la séquence ; l’accès à /opt/libris et aux archives de sauvegarde est réservé à root :

sudo -i
mkdir -p -m 750 /opt/libris
install -m 0600 /chemin/vers/config.env /opt/libris/.env
curl --fail --location --silent --show-error https://libris-translate.com/install.sh | bash -s -- --no-start
cd /opt/libris
docker compose up -d --wait database
backup=/mnt/libris-backup/libris/libris-YYYYMMDDTHHMMSSZ
(cd "$backup" && sha256sum --check SHA256SUMS)
docker compose exec -T database sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --no-owner --exit-on-error' < "$backup/database.dump"
docker compose run --rm --no-deps -T api tar -C /data -xzf - < "$backup/books.tar.gz"
docker compose --profile codex up -d --no-build --wait
exit

--no-start (ou LIBRIS_NO_START=1) tire les images sans lancer api, worker ni migrate ; seule la commande explicite ci-dessus démarre la base avant la restauration. Le .env sauvegardé doit contenir les images épinglées et les secrets d’origine. Si vous utilisez un autre dossier, définissez LIBRIS_HOME pour l’installateur et exécutez les commandes Compose depuis ce dossier. Remplacez YYYYMMDDTHHMMSSZ par l’horodatage UTC du dossier de sauvegarde.

Ne remplacez jamais une base de données dont vous voulez garder les données actuelles sans une sauvegarde de secours vérifiée.

Revenir en arrière après une mise à jour ratée

Les migrations de la base de données ne vont que vers l’avant. Pour revenir à la version précédente après une mise à jour qui a modifié le schéma de la base de données, il vous faut la base de données telle qu’elle était avant la mise à jour.

Installation cliente faite avec le bootstrap du registre :

  1. Arrêtez l’application : docker compose stop api worker.

  2. Restaurez la sauvegarde de la base de données faite avant la mise à jour (étape 3 ci-dessus). Le volume des livres n’a normalement pas besoin d’être restauré.

  3. Démarrez la version précédente depuis le registre (la commande conserve /opt/libris/.env et les volumes restaurés) :

    curl --fail --location --silent --show-error https://libris-translate.com/install.sh \
      | sudo env LIBRIS_TAG=<version-précédente> bash
    

    Le bootstrap extrait le fichier Compose correspondant depuis cette image précise. Ne le relancez pas sans LIBRIS_TAG avant de vouloir délibérément revenir à la version stable courante.

  4. Vérifiez /health, connectez-vous et reprenez les livres.

Installation gérée par le script de déploiement de production : chaque déploiement fait un dump de la base de données juste avant la migration, dans /opt/libris-production/backups/pre-<timestamp>-<commit>.<random>.dump (les cinq derniers sont conservés), et garde les images qu’il a remplacées.

  • Si la version fautive n’a pas modifié le schéma, exécutez libris-production-deploy --rollback (ou librisctl rollback --confirm) : il redémarre les images précédentes. Il refuse quand le schéma diffère, sans rien modifier.
  • Sinon, restaurez le dump fait juste avant le déploiement fautif (librisctl restore <dump> --confirm --no-start, ou l’étape 3 ci-dessus avec les options Compose indiquées dans operations (en anglais)), laissez les livres de côté, puis exécutez libris-production-deploy --rollback.

Les dumps d’avant déploiement restent sur le même disque que l’installation : ils protègent une mise à jour, pas la machine. Gardez aussi la sauvegarde nocturne.