Skip to content

Configuration Automatisée de Sauvegarde et Maintenance Plakar

Architecture de Sauvegarde Plakar

Cette documentation décrit une solution complète et sécurisée pour effectuer des sauvegardes centralisées vers un serveur distant avec Plakar, le tout au travers d'un montage SSHFS. Tout le processus de déploiement, de sauvegarde et de rotation (pruning / nettoyage) est entièrement automatisé à l'aide d'Ansible et de Systemd.

Points clés de l'architecture : - Sauvegardes autonomes (Pull / Agentless) : Chaque serveur s'occupe de sauvegarder ses propres données au travers d'un timer Systemd, vers un dépôt Plakar distant monté dynamiquement. - Réseau Sécurisé : Les montages s'effectuent via un hôte de rebond (Bastion SSH) pour protéger le serveur des sauvegardes limitant son exposition réseau. - Registre des hôtes : Chaque script de sauvegarde inscrit le nom d'hôte sur un fichier trace (backup-hosts.txt). - Maintenance Centralisée : Un seul serveur maître est en charge de lire le registre des hôtes et d'exécuter la rotation des sauvegardes afin de libérer de l'espace sur l'hôte distant. - Politique de Rétention Stricte : Application d'une double condition, garantissant de conserver au moins 3 sauvegardes utiles pour chaque hôte, tout en effaçant les sauvegardes de plus de 120 jours pour ceux en ayant davantage.


1. Prérequis et Sécurité

Avant d'exécuter les scripts, vos variables sensibles et configurations doivent être proprement préparées (ex: au sein de vos fichiers variable-ansible_playbook.yaml et variable-ansible_secret.yml).

Dans cette documentation (publique), les données d'identification sont identifiées par des valeurs fictives. Lors de votre usage, veillez à remplacer : - YOUR_SECRET_PASSPHRASE_HERE : La phrase secrète de chiffrement du dépôt Plakar. - backup_user : L'utilisateur de la machine ciblant le stockage distant. - backup.example.com : Le domaine / l'adresse IP du serveur de stockage Plakar. - bastion.example.com : Le domaine / l'adresse IP de votre Bastion SSH.


2. Déploiement du Service de Sauvegarde

Ce premier playbook Ansible se déroule sur l'ensemble de vos serveurs (cibles) qui doivent être sauvegardés. Il installe les dépendances nécessaires, récupère le binaire Plakar via ses dépôts officiels, crée un script de sauvegarde bash et le planifie dans Systemd.

Playbook Ansible

---
- name: Installer et configurer Plakar pour les sauvegardes
  hosts: servers_to_backup
  gather_facts: false
  become: true
  become_method: sudo
  vars_files:
   - variable-ansible_playbook.yaml
   - variable-ansible_secret.yml

  tasks:
    - name: Bloc d'installation et configuration
      block:
        - name: Installer les dépendances pour Plakar
          ansible.builtin.package:
            name:
              - curl
              - gnupg2
              - sshfs
            state: present

        - name: Télécharger et ajouter la clé GPG de Plakar
          ansible.builtin.shell: |
            set -o pipefail
            curl -fsSL https://packages.plakar.io/keys/plakar.gpg | gpg --dearmor -o /usr/share/keyrings/plakar.gpg
          args:
            creates: /usr/share/keyrings/plakar.gpg
            executable: /bin/bash

        - name: Ajouter le dépôt Plakar
          ansible.builtin.apt_repository:
            repo: "deb [signed-by=/usr/share/keyrings/plakar.gpg] https://packages.plakar.io/deb stable main"
            filename: plakar
            state: present
            update_cache: yes

        - name: Installer Plakar
          ansible.builtin.package:
            name: plakar
            state: present

        - name: Vérifier la version de Plakar
          ansible.builtin.command: plakar version
          register: plakar_version
          changed_when: false

        - name: Afficher la version de Plakar
          ansible.builtin.debug:
            msg: "Plakar version: {{ plakar_version.stdout }}"

        - name: S'assurer que le répertoire /opt/ansible existe
          ansible.builtin.file:
            path: /opt/ansible
            state: directory
            mode: '0755'
            owner: root
            group: root

        - name: Supprimer l'ancien script de backup Plakar
          ansible.builtin.file:
            path: /opt/ansible/plakar-backup.sh
            state: absent

        - name: Copier le script de backup Plakar
          ansible.builtin.copy:
            src: ansible-script-backup.sh
            dest: /opt/ansible/ansible-script-backup.sh
            mode: '0700'
            owner: root
            group: root

        - name: Créer le service Systemd pour le backup
          ansible.builtin.copy:
            dest: /etc/systemd/system/plakar-backup.service
            mode: '0644'
            content: |
              [Unit]
              Description=Plakar Backup Service
              After=network.target

              [Service]
              Type=oneshot
              ExecStart=/opt/ansible/ansible-script-backup.sh
              User=root

        - name: Créer le timer Systemd pour le backup (Mercredi 03:00)
          ansible.builtin.copy:
            dest: /etc/systemd/system/plakar-backup.timer
            mode: '0644'
            content: |
              [Unit]
              Description=Run Plakar Backup every Wednesday at 3AM

              [Timer]
              OnCalendar=Wed *-*-* 03:00:00
              Persistent=true

              [Install]
              WantedBy=timers.target

        - name: Activer et démarrer le timer Systemd
          ansible.builtin.systemd:
            name: plakar-backup.timer
            state: started
            enabled: yes
            daemon_reload: yes

      rescue:
        - name: Envoyer une alerte par mail en cas d'erreur
          local_action:
            module: mail
            host: "{{ smtp_host }}"
            port: "{{ smtp_port }}"
            to: "{{ email_recipient }}"
            subject: "Échec du playbook Plakar sur {{ inventory_hostname }}"
            body: |
              Le job d'installation/configuration de Plakar sur {{ inventory_hostname }} a rencontré une erreur.
              Tâche en échec : {{ ansible_failed_task.name }}
              Détails de l'erreur :
              {{ ansible_failed_result.msg | default(ansible_failed_result) }}
          delegate_to: localhost

3. Le Script de Sauvegarde Automatique

Ce script (ansible-script-backup.sh) déploie plusieurs comportements cruciaux : 1. Traversée via Bastion SSH : Il s'authentifie au serveur distant au travers d'un mandataire. 2. Initialisation automatique : Si le dépôt Plakar distant n'a jamais été initialisé (ex: nouvelle configuration vierge), il exécute un flush de sécurité et lance son setup. 3. Fréquence respectueuse : Grâce à check_backup_needed, la tâche évalue si une précédente sauvegarde a été réalisée dans les 7 derniers jours sur sa racine. Elle passe cette sauvegarde le cas échéant (économie réseau et CPU). 4. Inventaire / Liste des Hôtes : Ajoute son Hostname ($HOSTNAME_TAG) à un fichier backup-hosts.txt dans le dépôt. Ceci devient alors une source de vérité (utilisée par la suite par la maintenance) montrant tous les serveurs qui ont une sauvegarde active. 5. Aide à la Restauration : Sauvegarde des listes des paquets logiciels APT et Flatpak, en plus des URLs d'ajout de vos repos apt.

#!/bin/bash

# --- SÉCURITÉ ---
if [ "$(id -u)" -ne 0 ]; then
  echo "Ce script est conçu pour être lancé avec sudo." >&2
  exit 1
fi

plakar -disable-security-check >/dev/null 2>&1

# --- VARIABLES DE CONFIGURATION ---
REMOTE_USER="backup_user"
REMOTE_HOST="backup.example.com"
BASTION_HOST="bastion.example.com"
REMOTE_PATH="/mnt/backup/plakar"
SSH_KEY_PATH="/home/your_user/.ssh/id_rsa"
LOCAL_MOUNT_POINT="/mnt/remote-backup"
export PLAKAR_PASSPHRASE="YOUR_SECRET_PASSPHRASE_HERE"

SSHFS_URL="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}"
PLAKAR_REPOSITORY="$LOCAL_MOUNT_POINT"

# --- FONCTION DE NETTOYAGE ---
cleanup() {
    echo "--- Nettoyage ---"
    echo "Tentative de démontage de $LOCAL_MOUNT_POINT..."
    fusermount -uz "$LOCAL_MOUNT_POINT"
}
trap cleanup EXIT

# --- AJOUT DE LA CLÉ DE L'HÔTE ---
mkdir -p "$HOME/.ssh"
ssh-keyscan -H "$REMOTE_HOST" >> "$HOME/.ssh/known_hosts" 2>/dev/null
ssh-keyscan -H "$BASTION_HOST" >> "$HOME/.ssh/known_hosts" 2>/dev/null

# --- MONTAGE ET PRÉPARATION ---
echo "--- Préparation ---"
mkdir -p "$LOCAL_MOUNT_POINT"

# --- MONTAGE SSHFS ---
echo "--- Montage SSHFS ---"
if ! mountpoint -q "$LOCAL_MOUNT_POINT"; then
    sshfs "$SSHFS_URL" "$LOCAL_MOUNT_POINT" -o "IdentityFile=${SSH_KEY_PATH}",reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,allow_other,StrictHostKeyChecking=no,ProxyCommand="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ${SSH_KEY_PATH} -W %h:%p ${REMOTE_USER}@${BASTION_HOST}"
    if [ $? -ne 0 ]; then
        echo "Erreur critique lors du montage SSHFS. Arrêt." >&2
        exit 1
    fi
else
    echo "Le point de montage est déjà actif."
fi

# --- CRÉATION DU DÉPÔT PLAKAR (PREMIER LANCEMENT) ---
if [ ! -f "$PLAKAR_REPOSITORY/CONFIG" ]; then
    echo "Le dépôt Plakar n'existe pas ou est corrompu. Création..."
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -J "${REMOTE_USER}@${BASTION_HOST}" -i "$SSH_KEY_PATH" "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf ${REMOTE_PATH}/*"
    plakar at "$PLAKAR_REPOSITORY" create
    if [ $? -ne 0 ]; then
        echo "Erreur lors de la création du dépôt plakar. Arrêt." >&2
        exit 1
    fi
fi

# --- CONFIGURATION EXCLUSIONS ---
EXCLUSIONS=(
    "*.mp4"
    "*.mkv"
    "*.avi"
    "*.mov"
    "*.wmv"
    "/mnt/data/downloads"
    "/mnt/remote-backup"
    "/mnt/media"
    "/bin"
    "/boot"
    "/dev"
    "/etc"
    "/initrd.img"
    "/initrd.img.old"
    "/lib"
    "/lib64"
    "/media"
    "/proc"
    "/root"
    "/run"
    "/sbin"
    "/srv"
    "/sys"
    "/tmp"
    "/usr"
    "/var"
    "/vmlinuz"
    "/vmlinuz.old"
    "/snap"
    "/mnt/backup"
)

IGNORE_FLAGS=""
for excl in "${EXCLUSIONS[@]}"; do
    IGNORE_FLAGS="$IGNORE_FLAGS -ignore \"$excl\""
done

register_hostname_tag() {
    local hostname_tag="$1"
    local tag_file="$LOCAL_MOUNT_POINT/backup-hosts.txt"

    echo "Enregistrement du hostname-tag"
    if [ ! -f "$tag_file" ]; then
        touch "$tag_file"
    fi

    if grep -qFx "$hostname_tag" "$tag_file" 2>/dev/null; then
        echo "Le hostname-tag '$hostname_tag' existe déjà."
    else
        echo "Ajout en de '$hostname_tag'."
        echo "$hostname_tag" >> "$tag_file"
    fi
}

check_backup_needed() {
    local target="$1"
    local last_date_str
    local hostname_tag
    hostname_tag=$(hostname)

    # Récupère la dernière date du snapshot pour ce sous-serveur
    last_date_str=$(plakar at "$PLAKAR_REPOSITORY" ls -tag "$hostname_tag" | awk -v tgt="$target" '$NF == tgt {print $1; exit}')

    if [ -n "$last_date_str" ]; then
        local last_ts=$(date -d "$last_date_str" +%s)
        local current_ts=$(date +%s)
        local elapsed_seconds=$((current_ts - last_ts))

        # 604800 secondes = 1 semaine (7 jours)
        if [ $elapsed_seconds -lt 604800 ]; then
            return 1 # Récent, pas besoin de backup
        fi
    fi
    return 0 # Pas de backup ou trop vieux
}

HOSTNAME_TAG=$(hostname)
register_hostname_tag "$HOSTNAME_TAG"

DO_BACKUP=false
if check_backup_needed "/"; then
    echo "Sauvegarde de / nécessaire."
    DO_BACKUP=true
else
    echo "Sauvegarde de / récente (< 7 jours). Ignorée."
fi

if [ "$DO_BACKUP" = false ]; then
    echo "Toutes les sauvegardes sont à jour."
    exit 0
fi

# --- SAUVEGARDE DES PAQUETS MÉTA ---
echo "Sauvegarde des listes de paquets et d'applications..."
if command -v apt-mark &> /dev/null; then
    apt-mark showmanual > "/opt/apt-list.txt"
fi
if command -v flatpak &> /dev/null; then
    flatpak list --columns=application --app > "/opt/flatpak-list.txt"
fi
if [ -d "/etc/apt/sources.list.d" ]; then
    cat /etc/apt/sources.list.d/* > "/opt/repo-fallback.txt"
fi

# --- SAUVEGARDE PLAKAR ---
if [ "$DO_BACKUP" = true ]; then
    CMD="plakar at \"$PLAKAR_REPOSITORY\" backup -tag \"$HOSTNAME_TAG\" -concurrency 1 $IGNORE_FLAGS \"/\""
    echo "Commande: $CMD"
    eval "$CMD"

    if [ $? -ne 0 ]; then
        echo "Erreur critique de la sauvegarde plakar." >&2
        exit 1
    fi
fi

exit 0

4. Maintenance Centralisée et Pruning (Nettoyage des archives)

Afin d'éviter de remplir l'espace de stockage distant à terme, un autre serveur d'architecture (ou votre serveur d'infrastructure principal) va exécuter une routine Ansible ponctuelle pour nettoyer l'évolution des sauvegardes. L'opération est hautement sécurisée : elle utilise un script sur-mesure Python (intégré et exécutable par Ansible via stdin) pour évaluer quels snapshots doivent être prunes ou conservés.

  • Mécanisme Anti-Conflit : Ce script identifie les éventuels processus Docker de l'UI Web Plakar qui tourneraient et pourraient verrouiller le dossier, afin de libérer les stale locks avant l'exécution.
  • Paramètres : Tous les snapshots vieux de plus de 120 jours sont supprimés.
  • Fail-safe Intelligent : Si un serveur n'a pas pu se sauvegarder récemment après une panne, et que toutes ces sauvegardes sont proches du stade obsolète (> 120 jours), la tâche préserve au minium ses 3 dernières sauvegardes afin de ne pas perdre de données complètes.

Scénario de Maintenance (Ansible)

---
- name: Maintenance et nettoyage Plakar
  hosts: central_maintenance_server
  gather_facts: false
  become: true
  become_method: sudo
  vars_files:
   - variable-ansible_playbook.yaml
   - variable-ansible_secret.yml

  vars:
    min_snapshots_per_host: 3
    prune_age_days: 120
    plakar_passphrase: "YOUR_SECRET_PASSPHRASE_HERE"
    remote_user: "backup_user"
    remote_host: "backup.example.com"
    bastion_host: "bastion.example.com"
    remote_path: "/mnt/backup/plakar"
    ssh_key_path: "/home/your_user/.ssh/id_rsa"
    local_mount_point: "/mnt/remote-backup"

    plakar_service_container: "plakar-web"

  tasks:
    - name: Gestion du cycle de maintenance
      block:
        - name: Désactiver la politique de redémarrage automatique
          ansible.builtin.command: docker update --restart=no {{ plakar_service_container }}
          changed_when: true

        - name: Arrêter le service Plakar Web (pour libérer le verrou)
          community.docker.docker_container:
            name: "{{ plakar_service_container }}"
            state: stopped

        - name: Attendre la libération complète du verrou (5s)
          ansible.builtin.pause:
            seconds: 5

        - name: Créer le point de montage
          ansible.builtin.file:
            path: "{{ local_mount_point }}"
            state: directory
            mode: '0755'

        - name: Ajouter la clé de l'hôte distant (known_hosts)
          ansible.builtin.shell: |
            mkdir -p /root/.ssh
            ssh-keyscan -H {{ remote_host }} >> /root/.ssh/known_hosts 2>/dev/null || true
            ssh-keyscan -H {{ bastion_host }} >> /root/.ssh/known_hosts 2>/dev/null || true
          changed_when: false

        - name: Vérifier si déjà monté
          ansible.builtin.shell: grep -qs "{{ local_mount_point }}" /proc/mounts
          register: mount_check
          ignore_errors: true
          changed_when: false

        - name: Monter SSHFS si nécessaire
          ansible.builtin.command: >
            sshfs "{{ sshfs_url }}" "{{ local_mount_point }}"
            -o "IdentityFile={{ ssh_key_path }}",reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,allow_other,StrictHostKeyChecking=no,ProxyCommand="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i {{ ssh_key_path }} -W %h:%p {{ remote_user }}@{{ bastion_host }}"
          when: mount_check.rc != 0
          register: sshfs_mount

        - name: Nettoyer les fichiers de verrouillage (stale locks)
          ansible.builtin.shell: rm -f "{{ local_mount_point }}/locks/"*
          changed_when: true
          ignore_errors: true

        - name: Lire la liste des hosts depuis backup-hosts.txt
          ansible.builtin.command: cat "{{ local_mount_point }}/backup-hosts.txt"
          register: backup_hosts_file
          changed_when: false
          failed_when: false

        - name: Parser la liste des hosts
          ansible.builtin.set_fact:
            all_tags_list: "{{ backup_hosts_file.stdout_lines | select('match', '^[a-zA-Z0-9._-]+$') | list }}"

        - name: Analyser les snapshots pour chaque tag (Prune Evaluation)
          ansible.builtin.shell: |
            plakar at "{{ local_mount_point }}" ls -tag "{{ item }}" | python3 -c '
            import sys, json, datetime

            snapshots = []
            current_time = datetime.datetime.now(datetime.timezone.utc)
            prune_threshold = 120  # jours

            for line in sys.stdin:
                parts = line.strip().split()
                if len(parts) < 6: continue

                date_str = parts[0]
                snapshot_id = parts[1]
                path = parts[-1]

                try:
                    if date_str.endswith("Z"): date_str = date_str[:-1] + "+00:00"
                    snap_date = datetime.datetime.fromisoformat(date_str)
                    age = (current_time - snap_date).days
                except:
                    continue

                snapshots.append({
                    "id": snapshot_id,
                    "date": snap_date.isoformat(),
                    "age_days": age,
                    "path": path
                })

            oldest_age = max([s["age_days"] for s in snapshots]) if snapshots else 0
            old_count = len([s for s in snapshots if s["age_days"] > prune_threshold])
            remaining_after_prune = len(snapshots) - old_count

            print(json.dumps({
                "tag": "{{ item }}",
                "total_snapshots": len(snapshots),
                "oldest_age_days": oldest_age,
                "snapshots_older_than_120d": old_count,
                "remaining_after_prune": remaining_after_prune
            }))
            '
          environment:
            PLAKAR_PASSPHRASE: "{{ plakar_passphrase }}"
          loop: "{{ all_tags_list }}"
          register: all_tags_analysis
          changed_when: false

        - name: Exécuter le prune PAR TAG (protège les hosts inactifs)
          ansible.builtin.command: plakar at "{{ local_mount_point }}" prune -days {{ prune_age_days }} -tag "{{ item.item }}" -apply
          environment:
            PLAKAR_PASSPHRASE: "{{ plakar_passphrase }}"
          when: >-
            (item.stdout | from_json).snapshots_older_than_120d > 0 and
            (item.stdout | from_json).remaining_after_prune >= min_snapshots_per_host
          loop: "{{ all_tags_analysis.results }}"
          loop_control:
            label: "Pruning {{ item.item }}"
          register: prune_results

        - name: Attendre la fin des prunes (10s)
          ansible.builtin.pause:
            seconds: 10
          when: prune_results.changed

        - name: Exécuter la maintenance globale Plakar
          ansible.builtin.command: plakar at "{{ local_mount_point }}" maintenance
          environment:
            PLAKAR_PASSPHRASE: "{{ plakar_passphrase }}"
          register: maintenance_out
          when: prune_results.changed
          changed_when: maintenance_out.rc == 0

        - name: Démonter le volume SSHFS
          ansible.builtin.command: fusermount -uz "{{ local_mount_point }}"
          when: mount_check.rc != 0 or sshfs_mount.changed
          ignore_errors: true

        - name: Réactiver et Redémarrer le service Plakar Web
          ansible.builtin.command: docker update --restart=always {{ plakar_service_container }}
          changed_when: true
        - community.docker.docker_container:
            name: "{{ plakar_service_container }}"
            state: started

      rescue:
        - name: Rollback / Rescue en cas d'erreur de maintenance
          ansible.builtin.command: fusermount -uz "{{ local_mount_point }}"
          ignore_errors: true

        - name: Envoyer une alerte par mail
          local_action:
            module: mail
            host: "{{ smtp_host }}"
            port: "{{ smtp_port }}"
            to: "{{ email_recipient }}"
            subject: "Échec critique / Maintenance Plakar"
            body: |
              Tâche en échec : {{ ansible_failed_task.name }}
              Erreur : {{ ansible_failed_result.msg | default(ansible_failed_result) }}
          delegate_to: localhost

F.A.Q

Avec l’outil de backup plakar je peux utiliser SFTP quelle serais la commande ?

Oui ! Plakar dispose d’un connecteur SFTP natif.
Il suffit de pointer le “kloset” vers l’URL SFTP puis d’utiliser les actions habituelles (create, backup, restore, …).

Exemple complet

# 1️⃣ Créer un dépôt (kloset) sur le serveur SFTP
plakar at sftp://sftpuser@my‑server.example.com/backups create

# 2️⃣ Sauvegarder un répertoire local dans ce dépôt
plakar at sftp://sftpuser@my‑server.example.com/backups backup /home/monuser/Documents

Avec un remote nommé (option pratique)

# Définir le remote « mysftp » une fois pour toutes
plakar config remote set mysftp location sftp://sftpuser@my‑server.example.com/backups

# Sauvegarder en utilisant le nom du remote
plakar at mysftp backup /home/monuser/Documents

Ces commandes créent le dépôt sur le serveur SFTP (si besoin) et y envoient le snapshot du répertoire indiqué, le tout via une connexion SSH/SFTP sécurisée et sans interaction manuelle (clé SSH configurée au préalable).

Il y’a des commande en cli via plakar pour voir depuis quand certain “repo” (source de sauvegarde) on reçu une sauvegarde ?

Oui, Plakar dispose de plusieurs commandes qui permettent d’afficher les dates (et donc l’historique) des sauvegardes réalisées pour un dépôt / « repo ».
Voici les deux moyens les plus courants :

1. Lister toutes les snapshots d’un dépôt (ls)

# Exemple : dépôt stocké dans /var/backups
plakar at /var/backups ls
Cette commande renvoie la liste de toutes les snapshots présentes dans le kloset :
ID de la snapshotdate/heure du snapshottag éventuel.

Le format de sortie par défaut montre déjà la date (ISO‑8601) à laquelle chaque snapshot a été créée, ce qui vous indique « depuis quand ce repo a reçu une sauvegarde ».

2. Obtenir le détail d’une snapshot précise (info)

# 1️⃣ Lister pour récupérer l’ID de la snapshot qui vous intéresse
plakar at /var/backups ls

# 2️⃣ Afficher les métadonnées de cette snapshot
plakar info <snapshot-id>
plakar info affiche :

  • timestamp : date et heure de création du snapshot
  • source : le repo (source) qui a été sauvegardé
  • tags, size, nombre de fichiers, etc.

Ainsi vous pouvez vérifier exactement à quel moment chaque source a reçu sa première (ou sa dernière) sauvegarde.

Récapitulatif pratique
Action Commande Résultat affiché
Lister toutes les sauvegardes plakar at /chemin/du/kloset ls ID + date + tags
Détails d’une sauvegarde plakar info <snapshot‑id> Timestamp, source, etc.
Filtrer par source (optionnel) plakar query --source @nom_source --last 1 Dernière snapshot de la source

Astuce : si vous avez plusieurs dépôts, vous pouvez créer un remote nommé (ex. mysftp) avec plakar config remote set mysftp location sftp://user@host/backups puis utiliser plakar at mysftp ls pour la même chose sans retaper le chemin complet.

Ces deux commandes couvrent normalement le besoin de savoir « depuis quand un repo a reçu une sauvegarde ». Si vous avez besoin de filtres plus avancés (par période, par tag, …), la sous‑commande query permet de combiner des critères supplémentaires.