Nantes, le 11 Septembre 2025

Nous avons vu dans un précédent article comment faciliter l’industrialisation du déploiement d’instances PostgreSQL.

Dans ce nouvel article, nous allons approfondir sur le déploiement des sauvegardes, qui sont un aspect important à prendre en compte pour que les instances déployées automatiquement soient prêtes pour la production. Nous verrons comment l’outil pglift peut aider dans cette démarche.

Types de sauvegarde

Pour les sauvegardes de PostgreSQL, on distingue deux stratégies distinctes mais complémentaires, les sauvegardes physiques et les sauvegardes logiques.

Sauvegardes physiques locale

Une sauvegarde physique est une copie du répertoire données de l’instance. Si cette opération est réalisée « à froid », c’est à dire lorsque l’instance est arrêtée, la restauration se fait par simple copie des fichiers sauvegardés vers le répertoire de données. Si la sauvegarde est réalisée « à chaud », cette dernière est alors composée de données incohérentes. Il faut alors, en plus de la sauvegarde des fichiers de données, sauvegarder les archives des journaux de transactions (WAL). Lors du redémarrage, PostgreSQL rejoue les WAL sur les fichiers de données afin de retrouver un état cohérent. Souvent, on utilise un outil de sauvegarde pour gérer les opérations de sauvegarde et restauration, ainsi que la gestion de l’archivage des WAL. Dans cet article, nous allons nous pencher sur l’utilisation de pgBackRest au travers de pglift pour automatiser le déploiement d’une sauvegarde dans un dépôt local.

Avec pglift, il est possible d’industrialiser la configuration de pgBackRest pour nos instances.

Voici un exemple simple d’une configuration de pglift qui le permet :

---
systemd: {}
postgresql:
    auth:
        host: scram-sha-256
        local: peer
    datadir: /pgsql_data/{version}/{name}/data
pgbackrest:
    repository:
        mode: path
        path: /pgsql_backups/pgbackrest

Lorsqu’une instance PostgreSQL est déployée avec cette configuration de site, et qu’un nom de stanza pgBackRest est spécifié lors de la création d’instance, pgBackRest est configuré lors du déploiement :

[postgres@srv-pg1 ~]$ pglift instance create main --pgbackrest-stanza=my_app
INFO     initializing PostgreSQL         
INFO     configuring PostgreSQL authentication
INFO     configuring PostgreSQL
INFO     enabling systemd unit pglift-postgresql@14-main.service
INFO     starting PostgreSQL 14/main
INFO     starting systemd unit pglift-postgresql@14-main.service
INFO     creating role 'backup'
INFO     configuring pgBackRest stanza 'my_app' for pg1-path=/pgsql_data/14/main/data
INFO     creating pgBackRest stanza 'my_app'
INFO     checking pgBackRest configuration for stanza 'my_app'
INFO     enabling systemd unit pglift-backup@14-main.timer
INFO     creating instance dumps directory: /home/postgres/.local/share/pglift/srv/dumps/14-main
INFO     starting systemd unit pglift-backup@14-main.timer

pglift génère un fichier de configuration pour pgBackRest :

[postgres@srv-pg1 ~]$ cat ~/.local/share/pglift/etc/pgbackrest/pgbackrest.conf 
[global]
lock-path = /run/user/1001/pglift/pgbackrest/lock
log-path = /home/postgres/.local/share/pglift/log/pgbackrest
spool-path = /home/postgres/.local/share/pglift/srv/pgbackrest/spool
repo1-path = /pgsql_backups/pgbackrest
repo1-retention-archive = 2
repo1-retention-diff = 3
repo1-retention-full = 2

Il ajoute une stanza pour l’instance dans conf.d :

[postgres@srv-pg1 ~]$ cat ~/.local/share/pglift/etc/pgbackrest/conf.d/my_app.conf 
[my_app]
pg1-path = /pgsql_data/14/main/data
pg1-port = 5432
pg1-user = backup

Enfin, il prend également en charge la configuration du paramètre archive_command pour archiver les WAL vers le dépôt de sauvegarde via la commande pgbackrest archive-push :

[postgres@srv-pg1 ~]$ pglift instance exec main -- psql -c "show archive_command";
                                                                     archive_command                               
                                      
-------------------------------------------------------------------------------------------------------------------
--------------------------------------
 /usr/bin/pgbackrest --config-path=/home/postgres/.local/share/pglift/etc/pgbackrest --stanza=my_app --pg1-path=/pg
sql_data/14/main/data archive-push %p
(1 row)

Par conséquent, dès le déploiement, les WAL sont archivés dans le dépôt de sauvegarde de pgBackRest et la sauvegarde de l’instance est alors possible. Pour cela, on utilise la commande pglift instance backup :

[postgres@srv-pg1 ~]$ pglift instance backup
INFO     backing up instance 17/main with pgBackRest    

Pour lister les sauvegardes présentes dans le dépôt, on utilise la commande pglift instance backups :

[postgres@srv-pg1 ~]$ pglift instance backups
                                       Available backups for instance 14/main                                        
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┓
┃ label            ┃ size    ┃ repo_size ┃ date_start                ┃ date_stop                 ┃ type ┃ databases ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━┩
│ 20250826-121904F │ 26.5 MB │ 3.3 MB    │ 2025-08-26 12:19:04+02:00 │ 2025-08-26 12:19:10+02:00 │ full │ postgres  │
└──────────────────┴─────────┴───────────┴───────────────────────────┴───────────────────────────┴──────┴───────────┘

Sinon, la commande pgbackrest info, exécutée au travers de pglift instance exec permet aussi d’obtenir les informations sur les sauvegardes, cette fois directement depuis pgBackRest :

[postgres@srv-pg1 ~]$ pglift instance exec main -- pgbackrest info
stanza: my_app
    status: ok
    cipher: none

    db (current)
        wal archive min/max (14): 000000010000000000000001/000000010000000000000003

        full backup: 20250826-121904F
            timestamp start/stop: 2025-08-26 12:19:04+02 / 2025-08-26 12:19:10+02
            wal start/stop: 000000010000000000000003 / 000000010000000000000003
            database size: 25.3MB, database backup size: 25.3MB
            repo1: backup set size: 3.2MB, backup size: 3.2MB

Pour vérifier le bon fonctionnement, on peut créer une table dans une nouvelle base de données, pour y insérer quelques lignes :

[postgres@srv-pg1 pglift]$ pglift database create db1
INFO     creating 'db1' database in 17/main      
[postgres@srv-pg1 pglift]$ pglift instance exec main -- psql -d db1 -c "CREATE TABLE t1 (id INT); INSERT INTO t1 VALUES (1),(2),(3);"
CREATE TABLE
INSERT 0 3

Noter ensuite la date et l’heure actuelle :

[postgres@srv-pg1 ~]$ pglift instance exec main -- psql -c "select now();"
              now              
-------------------------------
 2025-08-26 12:20:18.968157+02
(1 row)

Puis supprimer la table t1 :

[postgres@srv-pg1 ~]$ pglift instance exec main -- psql -d db1 -c "DROP TABLE t1"
DROP TABLE

La table t1, désormais manquante, n’était pas présente au moment de la sauvegarde, ni même la base de données db1 qui la contient. On pourrait donc croire que les données sont perdues. Cependant, les actions menant à la création de la base de données et de la table on été enregistrées dans les journaux de transaction (WAL), il est donc tout à fait possible de retrouver les données en restaurant la sauvegarde qui précède l’existence de la table, puis en laissant PostgreSQL rejouer les WAL sur ses fichiers de données pendant la phase de recover.

Les WAL sont archivés dans le dépôt de sauvegarde de pgBackRest, et PostgreSQL est capable d’accéder à ces archives via le paramètre restore_command, qui est également configuré automatiquement par pglift lors du déploiement :

postgres=# show restore_command ;
                                                     restore_command                                                     
-------------------------------------------------------------------------------------------------------------------------
 /usr/bin/pgbackrest --config-path=/home/postgres/.local/share/pglift/etc/pgbackrest --stanza=my_app archive-get %f "%p"
(1 row)

Le rejeu des WAL est exécuté lors du premier démarrage qui suit la restauration. Pour restaurer notre table t1, pglift va donc exécuter une restauration de l’instance via pgBackRest. Ce dernier va alors configurer PostgreSQL pour rejouer les WAL jusqu’à la date et heure demandée, c’est à dire celle que nous avons noté plus tôt.

L’instance est arrêtée, puis restaurée avec la commande pglift instance restore. On spécifie la date au format YYYY-MM-JJ HH:MM:SS avec l’option --date.

[postgres@srv-pg1 pglift]$ pglift instance stop
INFO     stopping PostgreSQL 17/main
INFO     stopping systemd unit pglift-postgresql@17-main.
INFO     stopping systemd unit pglift-backup@17-main.timer
[postgres@srv-pg1 pglift]$ pglift instance restore --date "2025-08-26 12:20:18"
INFO     restoring instance 17/main with pgBackRest 

Démarrer l’instance. PostgreSQL effectue alors le recover jusqu’à la date et heure cible :

[postgres@srv-pg1 ~]$ pglift instance start
INFO     starting PostgreSQL 14/main                                                                               
INFO     starting systemd unit pglift-postgresql@14-main.service                                                   
INFO     starting systemd unit pglift-backup@14-main.timer     

La trace lors du démarrage nous indique bien que le recovery s’est arrêté avant la transaction exécutée à 12:20:35, ce qui correspond à la suppression de la table t1.

Aug 26 10:21:06 srv-pg1 postgresql-14-main[33116]: [13-1] [33116]: [6-1] db=,user=,app=,client= LOG:  restored log file "000000010000000000000004" from archive
Aug 26 10:21:06 srv-pg1 postgresql-14-main[33116]: [14-1] [33116]: [7-1] db=,user=,app=,client= LOG:  recovery stopping before commit of transaction 736, time 2025-08-26 12:20:35.654705+02
Aug 26 10:21:06 srv-pg1 postgresql-14-main[33116]: [15-1] [33116]: [8-1] db=,user=,app=,client= LOG:  redo done at 0/40188D8 system usage: CPU: user: 0.00 s, system: 0.03 s, elapsed: 0.14 s
Aug 26 10:21:06 srv-pg1 postgresql-14-main[33116]: [16-1] [33116]: [9-1] db=,user=,app=,client= LOG:  last completed transaction was at log time 2025-08-26 12:20:14.593246+02
Aug 26 10:21:06 srv-pg1 postgresql-14-main[33116]: [17-1] [33116]: [10-1] db=,user=,app=,client= LOG:  selected new timeline ID: 2
Aug 26 10:21:06 srv-pg1 postgresql-14-main[33116]: [18-1] [33116]: [11-1] db=,user=,app=,client= LOG:  archive recovery complete

La table est donc bien de nouveau consultable dans la base de données db1 :

[postgres@srv-pg1 ~]$ pglift instance exec main -- psql -d db1 -c "SELECT * FROM t1";
 id 
----
  1
  2
  3
(3 rows)

Sauvegarde physique à distance

Nous avons décrit le fonctionnement de la sauvegarde physique locale, mais pgBackRest peut également fonctionner sur un modèle client-serveur, dans lequel un serveur pgBackRest regroupe les sauvegardes provenant de plusieurs instances PostgreSQL dans un dépôt unique et centralise leurs planifications. Ce mode de fonctionnement ne sera pas développé en détail dans cet article, mais il est également supporté par pglift.

Voir la documentation associée

Sauvegardes logiques

Les sauvegardes logiques fonctionnent selon un principe différent. Dans ce mode de sauvegarde, les données de l’instance sont exportées dans un ou plusieurs fichiers.

Un dump est une sauvegarde qui correspond au résultat d’une lecture cohérente de la base de données, telle qu’elle était au moment du début de la sauvegarde, et quelque soit la durée d’export des données. Cela est rendu possible par le mécanisme MVCC de PostgreSQL, qui permet d’accéder aux versions précédentes des données qui pourraient avoir été modifiées pendant que la sauvegarde est effectuée.

Le dump peut être importé sur son instance d’origine pour restaurer une base de données à un état précédent. Il peut aussi être utile pour des besoins de migration, ou pour porter des données sur une instance de test par exemple.

L’outil standard pg_dump permet de réaliser ces sauvegardes, et il peut être piloté par pglift pour réaliser les dumps sur les instances qu’il déploie.

pglift effectue l’export des données via la commande spécifiée dans la clé postgresql.dump_commands de sa configuration. La valeur par défaut est la suivante :

dump_commands:
- - '{bindir}/pg_dump'
- -Fc
- -f
- '{path}/{dbname}_{date}.dump'
- -d
- '{conninfo}'

On voit ici que pg_dump sera utilisé pour réaliser un dump au format custom (-Fc).

L’emplacement du dump ({path}) correspond au paramètre dumps_directory, qui est par défaut déduit du répertoire de données :

dumps_directory: /pgsql_data/backups/dumps/{version}-{name}

Sur notre instance main, nous pouvons donc faire une sauvegarde de la base de données db1 avec la commande suivante :

[postgres@srv-pg1 ~]$ pglift database dump db1
INFO     backing up database 'db1' on instance 14/main 

On retrouve bien le dump à l’emplacement prévu :

[postgres@srv-pg1 ~]$ ls -l /pgsql_data/backups/dumps/14-main/
total 4
-rw-r--r--. 1 postgres users 1176 Sep  1 14:35 db1_2025-09-01T14:35:30+00:00.dump

Et pglift peut également lister les dumps présents dans ce répertoire :

[postgres@srv-pg1 ~]$ pglift database dumps
                                                   Dumps for all databases                                                    
┏━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ id             ┃ dbname ┃ date                      ┃ path                                                                 ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ db1_665a78398d │ db1    │ 2025-09-01 14:35:30+00:00 │ /pgsql_data/backups/dumps/14-main/db1_2025-09-01T14:35:30+00:00.dump │
└────────────────┴────────┴───────────────────────────┴──────────────────────────────────────────────────────────────────────┘

Supprimer à nouveau la table t1 :

[postgres@srv-pg1 ~]$ pglift instance exec main -- psql -d db1 -c "DROP TABLE t1"
DROP TABLE

Pour retrouver nos données à partir de la sauvegarde, il convient de supprimer la base de données, puis de restaurer via le dump, en spécifiant son identifiant :

[postgres@srv-pg1 ~]$ pglift database drop db1
INFO     dropping 'db1' database    
[postgres@srv-pg1 ~]$ pglift database restore db1_665a78398d
INFO     restoring dump for 'db1' on instance 14/main    

Les données sont bien restaurées :

[postgres@srv-pg1 ~]$ pglift instance exec main -- psql -d db1 -c "SELECT * FROM t1";
 id 
----
  1
  2
  3
(3 rows)

Pour conclure

Pour industrialiser PostgreSQL sans risque pour la sécurité des données, il est important de prévoir au plus tôt comment celles-ci seront sauvegardées.

Nous avons vu comment l’outil pglift nous permettait d’industrialiser le déploiement de nos sauvegardes, physiques ou logiques, de telle sorte que toute nouvelle instance est prête à être sauvegardées dès son initialisation. Le déploiement de la sauvegarde étant alors industriel, on réduit les risques d’erreurs lors de sa mise en place.


DALIBO

DALIBO est le spécialiste français de PostgreSQL®. Nous proposons du support, de la formation et du conseil depuis 2005.