Aller au contenu
medium

Configurer PostgreSQL : mémoire, connexions, WAL et logging

26 min de lecture

Logo PostgreSQL

La configuration par défaut de PostgreSQL est volontairement conservatrice : 128 Mo de shared_buffers, aucun log de requête lente, pas d’écoute réseau distante. C’est un choix de sécurité et de portabilité — un PostgreSQL fraîchement installé fonctionne partout, même sur un Raspberry Pi. Mais un serveur de production avec ces réglages sous-utilise la RAM disponible, ne capture aucun événement utile au diagnostic, et peut surprendre quand le nombre de connexions augmente.

Ce guide vous montre comment adapter la configuration à votre serveur, en partant des paramètres les plus impactants. Chaque modification est testée sur un lab réel (PostgreSQL 18.3, Debian 12, 2 Go de RAM) et accompagnée de la méthode de rechargement adaptée — reload quand c’est possible, restart quand c’est obligatoire.

  • Localiser les fichiers de configuration et comprendre la priorité postgresql.confpostgresql.auto.conf
  • Dimensionner les 4 paramètres mémoire critiques : shared_buffers, work_mem, maintenance_work_mem, effective_cache_size
  • Configurer le logging pour capturer les requêtes lentes, les connexions et les verrous
  • Comprendre l’autovacuum et savoir détecter un retard
  • Recharger la configuration à chaud avec pg_reload_conf() et identifier les paramètres qui exigent un restart

Vous devez configurer PostgreSQL dans ces situations :

  • Vous venez d’installer PostgreSQL et les valeurs par défaut ne correspondent pas à votre serveur (RAM sous-utilisée, pas de log exploitable)
  • Vous mettez un serveur en production et devez garantir des performances correctes et du logging utile
  • Vous devez ouvrir les connexions réseau pour qu’une application distante puisse se connecter
  • Vous constatez des requêtes lentes et avez besoin de les tracer
  • L’autovacuum montre des signes de retard (tables gonflées, n_dead_tup élevé)
  • PostgreSQL installé et le service actif (voir le guide Installation)
  • Accès au rôle superuser (postgres) pour modifier la configuration
  • Connaître les bases de psql (voir le guide Prise en main de psql)

Par défaut, PostgreSQL place postgresql.conf et postgresql.auto.conf dans le data directory (PGDATA). Mais sur Debian/Ubuntu avec les paquets PGDG, le packaging sépare les fichiers de configuration du répertoire de données — une convention spécifique, pas une règle générale PostgreSQL. L’organisation résultante :

FichierEmplacementRôle
postgresql.conf/etc/postgresql/18/main/postgresql.confConfiguration principale du serveur
postgresql.auto.conf/var/lib/postgresql/18/main/postgresql.auto.confÉcrit par ALTER SYSTEMprioritaire
pg_hba.conf/etc/postgresql/18/main/pg_hba.confContrôle d’accès (qui peut se connecter)
pg_ident.conf/etc/postgresql/18/main/pg_ident.confMapping entre utilisateurs système et rôles

Vérifiez les chemins réels avec :

SHOW config_file;
SHOW hba_file;
SHOW data_directory;
config_file
-----------------------------------------
/etc/postgresql/18/main/postgresql.conf
hba_file
-------------------------------------
/etc/postgresql/18/main/pg_hba.conf
data_directory
-----------------------------
/var/lib/postgresql/18/main

C’est le fichier central. Il contient des centaines de paramètres, la plupart commentés (valeur par défaut). Seuls les paramètres décommentés sont effectifs.

Ce fichier est géré par l’administrateur ou un outil de gestion de configuration (Ansible, Puppet, etc.).

Quand vous exécutez ALTER SYSTEM SET shared_buffers = '512MB';, PostgreSQL écrit la valeur dans postgresql.auto.conf — jamais dans postgresql.conf.

# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
shared_buffers = '512MB'
work_mem = '16MB'
maintenance_work_mem = '128MB'
effective_cache_size = '1536MB'

Règle de priorité : postgresql.auto.conf est lu après postgresql.conf. Si un paramètre existe dans les deux fichiers, c’est la valeur de postgresql.auto.conf qui gagne.

Ce fichier définit qui peut se connecter, depuis où, et comment s’authentifier. La configuration par défaut sur Debian :

local all postgres peer
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
local replication all peer
host replication all 127.0.0.1/32 scram-sha-256
host replication all ::1/128 scram-sha-256

Seules les connexions locales (socket Unix) et localhost (127.0.0.1) sont autorisées. Pour ouvrir l’accès réseau, vous devrez modifier à la fois listen_addresses et pg_hba.conf — le détail est dans le guide Sécurisation.

Trois méthodes pour voir la valeur effective d’un paramètre :

SHOW — la plus rapide :

SHOW shared_buffers;
shared_buffers
----------------
512MB

current_setting() — utilisable dans une requête :

SELECT current_setting('work_mem');

pg_settings — la vue complète avec source et contexte :

SELECT name, setting, unit, source, context, pending_restart
FROM pg_settings
WHERE name = 'shared_buffers';

pg_file_settings — voir ce que les fichiers contiennent (pas forcément appliqué) :

SELECT sourcefile, sourceline, name, setting, applied
FROM pg_file_settings
WHERE name = 'shared_buffers'
ORDER BY sourcefile;
sourcefile | sourceline | name | setting | applied
--------------------------------------------------+------------+----------------+---------+---------
/etc/postgresql/18/main/postgresql.conf | 132 | shared_buffers | 128MB | f
/var/lib/postgresql/18/main/postgresql.auto.conf | 3 | shared_buffers | 512MB | t

On voit ici que postgresql.conf contient 128MB (applied = f) mais postgresql.auto.conf impose 512MB (applied = t).

La mémoire est le levier de performance le plus direct. PostgreSQL distingue la mémoire partagée (commune à toutes les connexions) et la mémoire par opération (allouée individuellement).

Le paramètre le plus important. Il définit la taille de la mémoire partagée où PostgreSQL met en cache les pages de données (blocs de 8 Ko) lues depuis le disque.

Valeur par défaut : 128MB — beaucoup trop faible pour un serveur de production.

Règle de dimensionnement : entre 25 % et 40 % de la RAM totale. Sur notre lab avec 2 Go de RAM :

ALTER SYSTEM SET shared_buffers = '512MB';
-- Nécessite un RESTART

Pourquoi pas 50 % ou plus ? Parce que PostgreSQL s’appuie aussi sur le cache du système d’exploitation (page cache Linux). Si shared_buffers consomme trop de RAM, le page cache rétrécit et les performances globales baissent.

Augmenter shared_buffers pousse souvent à augmenter aussi max_wal_size, pour étaler l’écriture des pages modifiées (dirty pages) sur une période plus longue et éviter des checkpoints trop fréquents.

Chaque opération de tri (ORDER BY, DISTINCT, MERGE JOIN) ou de hachage (HASH JOIN, GROUP BY) peut utiliser jusqu’à work_mem de mémoire avant de déborder sur disque (fichiers temporaires).

Valeur par défaut : 4MB

Piège courant : ce n’est pas par connexion, c’est par opération. Une requête complexe avec 5 nœuds de tri peut consommer 5 × work_mem. Et chaque connexion concurrente fait de même.

Calcul prudent : RAM disponible / (max_connections × 2 à 5 opérations). Sur notre lab :

ALTER SYSTEM SET work_mem = '16MB';
-- Prend effet après reload (contexte : user)

Un work_mem trop faible force des tris sur disque (visible dans EXPLAIN ANALYZE avec Sort Method: external merge). Un work_mem trop élevé avec beaucoup de connexions peut épuiser la RAM.

maintenance_work_mem : pour VACUUM et CREATE INDEX

Section intitulée « maintenance_work_mem : pour VACUUM et CREATE INDEX »

Mémoire allouée aux opérations de maintenance : VACUUM, CREATE INDEX, ALTER TABLE ADD FOREIGN KEY. Ces opérations sont ponctuelles mais consomment beaucoup de mémoire pour être efficaces.

Valeur par défaut : 64MB

Règle : 2 à 4 fois work_mem, sans dépasser 1 à 2 Go. Plus la table est grosse, plus un VACUUM bénéficie de mémoire :

ALTER SYSTEM SET maintenance_work_mem = '128MB';
-- Prend effet après reload (contexte : user)

effective_cache_size : estimation pour le planificateur

Section intitulée « effective_cache_size : estimation pour le planificateur »

Ce paramètre n’alloue aucune mémoire. C’est une indication donnée au planificateur de requêtes sur la quantité totale de cache disponible (shared_buffers + page cache OS). Il influence le choix entre un scan séquentiel et un scan d’index.

Valeur par défaut : 4GB

Règle : environ 50 à 75 % de la RAM totale :

ALTER SYSTEM SET effective_cache_size = '1536MB';
-- Prend effet après reload (contexte : user)

Si la valeur est trop basse, le planificateur favorisera des scans séquentiels même quand un index serait plus rapide.

Tableau : valeurs recommandées selon la RAM disponible

Section intitulée « Tableau : valeurs recommandées selon la RAM disponible »
RAM totaleshared_bufferswork_memmaintenance_work_memeffective_cache_size
2 Go512 Mo8–16 Mo128 Mo1,5 Go
4 Go1 Go16–32 Mo256 Mo3 Go
8 Go2 Go32–64 Mo512 Mo6 Go
16 Go4 Go64–128 Mo1 Go12 Go
32 Go8 Go128–256 Mo2 Go24 Go

Valeur par défaut : 100

Chaque connexion PostgreSQL correspond à un processus système (fork). 100 connexions = 100 processus, chacun avec sa mémoire (work_mem, catalogues système, état de session). Au-delà de 200-300 connexions, les performances chutent à cause du context switching et de la contention mémoire.

SHOW max_connections;
max_connections
-----------------
100

Si votre application a besoin de plus de 100 connexions, la solution n’est pas d’augmenter max_connections à 1 000. La bonne approche est d’introduire un connection pooler comme PgBouncer entre l’application et PostgreSQL.

Par défaut, PostgreSQL n’écoute que sur localhost :

SHOW listen_addresses;
listen_addresses
------------------
localhost

Pour autoriser les connexions réseau (depuis une application sur un autre serveur) :

ALTER SYSTEM SET listen_addresses = '*';
-- Nécessite un RESTART (contexte : postmaster)

'*' signifie « écouter sur toutes les interfaces ». Vous pouvez aussi cibler une IP précise : '192.168.122.60'.

Valeur par défaut : 3

Réserve 3 connexions pour les superusers, même quand max_connections est atteint. C’est votre filet de secours pour vous connecter et diagnostiquer quand le serveur est saturé. Ne descendez jamais en dessous de 2.

Le WAL (Write-Ahead Log) est le mécanisme qui garantit la durabilité des données : chaque modification est d’abord écrite dans le journal WAL avant d’être appliquée aux fichiers de données. Les checkpoints sont les moments où PostgreSQL synchronise les pages modifiées en mémoire vers le disque.

Valeur par défaut : replica

Ce paramètre contrôle la quantité d’informations écrites dans le WAL :

NiveauUsageConséquence
minimalServeur standalone sans sauvegarde PITRWAL minimal, pas de réplication possible
replicaDéfaut recommandé — réplication physique, pg_basebackup, PITRSuffisant pour 95 % des cas
logicalRéplication logique, CDC (Change Data Capture)WAL plus volumineux

Gardez replica sauf besoin explicite de réplication logique. Passer de minimal à replica nécessite un restart.

max_wal_size (défaut : 1GB) — volume de WAL accumulé avant de forcer un checkpoint. Plus la valeur est élevée, moins les checkpoints sont fréquents (meilleur débit d’écriture), mais la reprise après crash sera plus longue.

checkpoint_completion_target (défaut : 0.9) — fraction du temps entre deux checkpoints pendant laquelle PostgreSQL étale l’écriture des pages sales. 0.9 signifie que l’écriture est répartie sur 90 % de l’intervalle, ce qui réduit les pics d’I/O.

checkpoint_timeout (défaut : 5min) — intervalle maximum entre deux checkpoints automatiques.

Pour un serveur de production avec un bon sous-système disque :

ALTER SYSTEM SET max_wal_size = '2GB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9; -- déjà la valeur par défaut
-- Prend effet après reload (contexte : sighup)

La configuration par défaut logue les avertissements et erreurs, mais sans collecteur intégré (logging_collector = off) elle reste peu confortable à exploiter en production — pas de rotation automatique, pas de structuration des fichiers, et un risque de logs perdus quand plusieurs processus écrivent en concurrence sur stderr. Voici une configuration réaliste pour la production.

ALTER SYSTEM SET logging_collector = 'on';
ALTER SYSTEM SET log_directory = 'log';
ALTER SYSTEM SET log_filename = 'postgresql-%Y-%m-%d.log';
-- logging_collector nécessite un RESTART (contexte : postmaster)

Les logs sont écrits dans $PGDATA/log/, soit /var/lib/postgresql/18/main/log/ sur Debian :

-rw------- 1 postgres postgres 2885 Apr 13 10:01 postgresql-2026-04-13.log
ValeurCe qui est logué
noneRien (défaut)
ddlCREATE, ALTER, DROPrecommandé en production
modddl + INSERT, UPDATE, DELETE
allToutes les requêtes — uniquement pour le debug
ALTER SYSTEM SET log_statement = 'ddl';

log_min_duration_statement : trouver les requêtes lentes

Section intitulée « log_min_duration_statement : trouver les requêtes lentes »

Le paramètre le plus utile en exploitation. Il trace toutes les requêtes dont la durée dépasse le seuil :

ALTER SYSTEM SET log_min_duration_statement = 500; -- en millisecondes

Avec 500, toute requête prenant plus d’une demi-seconde apparaît dans les logs avec sa durée exacte et son texte SQL complet. C’est le premier outil de diagnostic avant pg_stat_statements.

log_connections, log_disconnections, log_lock_waits

Section intitulée « log_connections, log_disconnections, log_lock_waits »
ALTER SYSTEM SET log_connections = 'all';
ALTER SYSTEM SET log_disconnections = 'on';
ALTER SYSTEM SET log_lock_waits = 'on';
  • log_connections : trace les connexions entrantes. En PostgreSQL 18, ce paramètre accepte des options granulaires : receipt, authentication, authorization, setup_durations, ou all. La valeur on reste acceptée pour compatibilité mais n’active qu’un sous-ensemble. Préférez 'all' pour une visibilité complète.
  • log_disconnections : trace chaque déconnexion avec la durée de session
  • log_lock_waits : trace les requêtes qui attendent un verrou plus longtemps que deadlock_timeout (1 seconde par défaut)

Exemple de ce que les logs montrent avec cette configuration :

2026-04-13 10:01:20.273 [6287] postgres@postgres LOG: connection authorized: user=postgres database=postgres application_name=psql
2026-04-13 10:01:20.275 [6287] postgres@postgres LOG: disconnection: session time: 0:00:00.003 user=postgres database=postgres host=[local]

Le préfixe détermine les métadonnées ajoutées à chaque ligne de log :

ALTER SYSTEM SET log_line_prefix = '%m [%p] %q%u@%d/%a ';
PlaceholderSignification
%mHorodatage avec millisecondes
%pPID du processus
%qRien si la session n’est pas liée à un client (processus interne)
%uNom du rôle
%dNom de la base de données
%aNom de l’application (application_name)

Résultat dans les logs : 2026-04-13 10:01:20.273 [6287] postgres@postgres/psql LOG: ...

L’ajout de %a permet de distinguer immédiatement les requêtes venant de votre application, de PgBouncer, d’un script cron ou de psql.

PostgreSQL 18 ajoute log_lock_failures, mais sa portée est encore limitée : la documentation indique qu’il couvre actuellement les échecs de verrouillage liés à NOWAIT (par exemple SELECT ... FOR UPDATE NOWAIT qui échoue parce que la ligne est déjà verrouillée). C’est complémentaire de log_lock_waits qui ne trace que les attentes au-delà de deadlock_timeout.

ALTER SYSTEM SET log_lock_failures = 'on';

Configuration de logging recommandée (récapitulatif)

Section intitulée « Configuration de logging recommandée (récapitulatif) »
-- Collecteur (RESTART obligatoire)
ALTER SYSTEM SET logging_collector = 'on';
ALTER SYSTEM SET log_directory = 'log';
ALTER SYSTEM SET log_filename = 'postgresql-%Y-%m-%d.log';
-- Contenu des logs (RELOAD suffit)
ALTER SYSTEM SET log_statement = 'ddl';
ALTER SYSTEM SET log_min_duration_statement = 500;
ALTER SYSTEM SET log_connections = 'all';
ALTER SYSTEM SET log_disconnections = 'on';
ALTER SYSTEM SET log_lock_waits = 'on';
ALTER SYSTEM SET log_lock_failures = 'on';
ALTER SYSTEM SET log_line_prefix = '%m [%p] %q%u@%d/%a ';

PostgreSQL utilise le modèle MVCC (Multi-Version Concurrency Control) : quand vous faites un UPDATE, l’ancienne version de la ligne n’est pas supprimée — elle est marquée comme “morte” (“dead tuple”). C’est ce qui permet aux transactions concurrentes de lire les anciennes valeurs sans bloquer.

Le problème : ces lignes mortes accumulent du bloat (espace gaspillé). Sans nettoyage régulier :

  • Les tables grossissent inutilement sur disque
  • Les scans deviennent plus lents (plus de pages à parcourir)
  • Le risque de transaction ID wraparound augmente — un scénario catastrophe où PostgreSQL refuse toute nouvelle écriture pour protéger l’intégrité des données

L’autovacuum est le processus automatique qui nettoie ces lignes mortes. Il est activé par défaut et c’est rarement une bonne idée de le désactiver.

ParamètreDéfautDescription
autovacuumonActive le processus. Ne le désactivez pas.
autovacuum_naptime1minFréquence de vérification des tables à nettoyer
autovacuum_max_workers3Nombre de workers parallèles
autovacuum_vacuum_threshold50Nombre minimum de lignes mortes avant déclenchement
autovacuum_vacuum_scale_factor0.2Fraction de la table (20 %) qui doit être “morte”
autovacuum_vacuum_cost_delay2msPause entre les cycles I/O (pour limiter l’impact)

Formule de déclenchement : l’autovacuum se déclenche quand n_dead_tup > threshold + scale_factor × n_live_tup. Pour une table de 10 000 lignes : 50 + 0.2 × 10 000 = 2050 lignes mortes avant déclenchement.

autovacuum_vacuum_max_threshold (nouveau PostgreSQL 18)

Section intitulée « autovacuum_vacuum_max_threshold (nouveau PostgreSQL 18) »

PostgreSQL 18 ajoute autovacuum_vacuum_max_threshold (défaut : 100 000 000). Il fixe un plafond absolu au nombre de lignes modifiées avant de forcer un VACUUM, indépendamment du scale_factor. Sur les très grosses tables (des milliards de lignes), scale_factor seul pouvait retarder le VACUUM trop longtemps.

SELECT schemaname, relname,
n_dead_tup,
n_mod_since_analyze,
last_autovacuum,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
schemaname | relname | n_dead_tup | n_mod_since_analyze | last_autovacuum | last_autoanalyze
------------+-----------+------------+---------------------+-----------------+------------------
app | clients | 2 | 7 | |
app | commandes | 0 | 5 | |

Signaux d’alerte :

  • n_dead_tup élevé et last_autovacuum ancien (ou NULL) → l’autovacuum ne passe pas assez souvent
  • n_mod_since_analyze élevé → les statistiques du planificateur sont obsolètes (ANALYZE nécessaire)
  • Tables qui grossissent sur disque alors que le nombre de lignes vivantes ne change pas → bloat

Depuis psql (recommandé) :

SELECT pg_reload_conf();
pg_reload_conf
----------------
t

Certains paramètres ont le contexte postmaster : ils ne prennent effet qu’au prochain démarrage du service. Les plus courants :

ParamètreImpact du changement
shared_buffersTaille de la mémoire partagée
max_connectionsLimite de connexions simultanées
listen_addressesInterfaces réseau écoutées
wal_levelNiveau de détail du WAL
logging_collectorActivation du collecteur de logs
portPort TCP

Tous les autres paramètres couverts dans ce guide (mémoire par opération, logging, autovacuum) prennent effet avec un simple reload.

Après un ALTER SYSTEM suivi d’un reload, vérifiez si des paramètres attendent encore un restart :

SELECT name, setting, pending_restart
FROM pg_settings
WHERE pending_restart = true;

Si la requête retourne des lignes, un systemctl restart postgresql@18-main est nécessaire pour les appliquer.

  1. Modifier le paramètre avec ALTER SYSTEM :

    ALTER SYSTEM SET work_mem = '32MB';
  2. Recharger la configuration :

    SELECT pg_reload_conf();
  3. Vérifier dans une nouvelle session :

    SHOW work_mem;
  4. Si pending_restart = true : redémarrer le service

    Fenêtre de terminal
    sudo systemctl restart postgresql@18-main
SymptômeCause probableSolution
SHOW shared_buffers retourne l’ancienne valeur après reloadContexte postmastersystemctl restart postgresql@18-main
Logs vides malgré log_statement = 'all'logging_collector = offActiver + restart
ALTER SYSTEM ignorépostgresql.conf rechargé depuis un outil de CONFIG MANAGEMENT qui ne touche pas auto.confVérifier pg_file_settings — la valeur auto.conf a priorité
Mémoire saturée (OOM killer)shared_buffers + work_mem × connexions > RAMRéduire work_mem ou max_connections
Autovacuum ne passe jamaisSeuil trop haut pour les petites tablesBaisser autovacuum_vacuum_threshold ou le scale_factor
pending_restart reste à trueLe service n’a pas été restart, seulement reloadsystemctl restart postgresql@18-main
  • La configuration par défaut est conservatrice — adaptez toujours shared_buffers, work_mem et le logging à votre serveur.
  • shared_buffers : 25 à 40 % de la RAM, c’est le paramètre le plus impactant sur les performances.
  • work_mem est par opération, pas par connexion — attention à ne pas dépasser la RAM disponible avec beaucoup de connexions.
  • effective_cache_size n’alloue rien — c’est une indication pour le planificateur (50-75 % de la RAM).
  • max_connections au-delà de 200 : pensez PgBouncer plutôt qu’augmenter la valeur.
  • Activez log_min_duration_statement dès le premier jour — c’est le premier outil pour trouver les requêtes lentes.
  • Ne désactivez jamais l’autovacuum — le risque de wraparound est un scénario catastrophe.
  • postgresql.auto.conf a priorité sur postgresql.conf — choisissez une seule méthode de gestion.
  • Vérifiez pending_restart après chaque modification — certains paramètres exigent un restart complet.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn