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.