[{"data":1,"prerenderedAt":168},["ShallowReactive",2],{"seo-verification":3,"blog-postgresql-15-17-migration-docker-vps-fr":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"id":7,"slug":8,"slugs":9,"title":13,"excerpt":14,"readTime":15,"views":16,"isPinned":17,"publishedAt":18,"category":19,"categories":25,"featuredImage":27,"bgImage":28,"posterImage":29,"relatedSolution":27,"intro":30,"sections":31,"ctaTitle":110,"ctaBody":111,"ctaButton":112,"ctaUrl":113,"relatedPosts":114},315,"postgresql-15-17-migration-docker-vps",{"fr":8,"en":10,"ar":11,"es":12},"migrating-postgresql-15-to-17-in-a-docker-stack","ترقية-postgresql-من-الإصدار-15-إلى-17-في-docker","migrar-postgresql-15-a-17-en-una-stack-docker","Migrer PostgreSQL 15 vers 17 dans une stack Docker","Guide complet pour migrer PostgreSQL 15 vers 17 dans Docker : pg_dump\u002Fpg_restore, extensions incompatibles, checklist zero-downtime — et le cas Supabase.",12,0,false,"2026-08-30T00:00:00+00:00",{"id":20,"name":21,"slug":22,"color":23,"icon":24},6,"Bases de données","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[26],{"id":20,"name":21,"slug":22,"color":23,"icon":24},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fpostgresql-15-17-migration-docker-vps-poster.svg","PostgreSQL 15 atteint sa fin de vie en novembre 2026. Supabase a basculé silencieusement sur `postgres:17` en juin 2026, cassant les stacks auto-hébergées qui ne fixaient pas leur version. Si votre Compose tourne encore sur PG 15, la fenêtre de migration est ouverte — et elle se referme. Ce guide démonte les extensions incompatibles, documente les changements de comportement réels entre PG 15 et PG 17, et donne une procédure de bascule à zéro interruption applicable à n'importe quel Compose de production.",[32,36,47,50,69,72,75,95,98,107],{"type":33,"title":34,"body":35},"h2","Pourquoi migrer maintenant — PG 15 EOL et le signal Supabase","PostgreSQL 15 entre en fin de vie officielle en **novembre 2026**. Passé cette date, la PostgreSQL Global Development Group ne publie plus de correctifs de sécurité ni de corrections de bogues : toute vulnérabilité découverte après la date de fin de vie reste sans patch sur votre version.\n\nMais la migration ne peut plus attendre novembre pour une raison plus immédiate : **Supabase a basculé son image de référence sur `postgres:17` en juin 2026** (discussion #46080 du changelog public). Les stacks qui référençaient `image: supabase\u002Fpostgres` sans tag de version ont reçu PG 17 lors d'un simple `docker compose pull`, sans avertissement visible dans les logs de démarrage. Certains déploiements auto-hébergés ont découvert la rupture au redémarrage — une extension compilée pour PG 15 refusant de charger sous PG 17.\n\nSi votre stack épingle déjà `postgres:15`, vous êtes protégé à court terme. Si elle pointe `postgres:latest` ou un tag flottant de Supabase, elle a peut-être déjà migré sans que vous le sachiez. La vérification se fait en une commande :\n\n```bash\ndocker exec \u003Ccontainer> psql -U postgres -c 'SELECT version();'\n```\n\nLe résultat vous indique la version exacte du serveur en cours d'exécution. Ne supposez pas — vérifiez.",{"type":37,"title":38,"items":39},"ul","Ce qui change réellement entre PG 15 et PG 17",[40,41,42,43,44,45,46],"**`search_path` durci depuis PG 15.1** (ADV-2022-00007) : le schéma `public` n'est plus dans le `search_path` par défaut pour les rôles non-superuser. Toute requête qui supposait `public.ma_table` au lieu de `ma_table` qualifié peut échouer silencieusement en renvoyant 0 résultats au lieu d'une erreur.","**`pg_dump` produit des dumps incompatibles vers le bas** : un dump PG 17 ne se restaure pas sur PG 15. L'inverse est possible, et c'est le sens de la migration. Conserver les dumps PG 15 pendant 30 jours minimum après la bascule.","**Paramètre `wal_level` par défaut élevé à `logical` en PG 16+** : si votre `postgresql.conf` forçait `wal_level = minimal`, le comportement change après migration. Les slots de réplication logique existants peuvent devenir invalides.","**Suppressions de fonctions dépréciées** : `lo_import`, `lo_export` et certaines fonctions `pg_catalog` retirées ou rebaptisées entre PG 15 et PG 17 — vérifier les fonctions utilisées dans vos extensions maison.","**`pg_stat_statements` modifie la normalisation des requêtes** : les dashboards Grafana\u002FPgHero qui agrègent par query fingerprint verront leurs séries repartir de zéro après la migration.","**L'extension `pg_partman`** (gestion de partitionnement) demande une version ≥ 5.x pour PG 17 — la version 4.x n'est pas compatible.","**`timescaledb`** exige une version ≥ 2.13 pour PG 17 ; les versions antérieures refusent de charger et bloquent le démarrage du conteneur.",{"type":33,"title":48,"body":49},"Inventaire des extensions : ce qui passe et ce qui casse","Avant toute migration, extraire la liste des extensions actives dans chaque base :\n\n```bash\ndocker exec \u003Cpg15_container> psql -U postgres -c \\\n  \"SELECT datname, extname, extversion FROM pg_extension e JOIN pg_database d ON d.oid = e.extnamespace ORDER BY datname, extname;\"\n```\n\nLes extensions à vérifier en priorité sur PG 17 :\n\n**Extensions compatibles sans action** : `pgcrypto`, `uuid-ossp`, `hstore`, `ltree`, `citext`, `pg_trgm`, `unaccent`, `intarray`, `tablefunc`, `earthdistance`, `fuzzystrmatch`. Ces extensions sont fournies dans le package `postgresql-contrib` standard et n'ont pas subi de rupture entre PG 15 et PG 17.\n\n**Extensions nécessitant une mise à jour** : `pgvector` (passer à ≥ 0.7.0 pour PG 17), `pg_stat_statements` (intégré, mais l'extension doit être recréée si `shared_preload_libraries` change), `PostGIS` (≥ 3.4 pour PG 17), `TimescaleDB` (≥ 2.13 obligatoire), `pg_partman` (≥ 5.0 obligatoire).\n\n**Extensions non portées ou à compiler** : toute extension compilée maison (`.so`) contre les headers PG 15 doit être recompilée contre les headers PG 17. Le binaire n'est pas rétrocompatible. Si votre image Docker embarque un tel `.so`, reconstruire l'image avant la migration.\n\nLa commande de vérification post-migration :\n\n```bash\ndocker exec \u003Cpg17_container> psql -U postgres -c 'SELECT * FROM pg_available_extensions ORDER BY name;'\n```\n\nComparez la colonne `installed_version` avec votre inventaire PG 15 — une extension manquante (`NULL`) indique un problème de chargement à corriger avant de valider la migration.",{"type":51,"title":52,"steps":53},"steps","Procédure de migration sans interruption : pg_dump\u002Fpg_restore",[54,57,60,63,66],{"title":55,"body":56},"Figer la version de l'image PG 15","Avant toute opération, **épinglez l'image actuelle** dans votre `docker-compose.yml` avec son digest exact. Cela vous permet de revenir à l'état d'origine en une commande :\n\n```bash\ndocker inspect \u003Cpg15_container> --format '{{.Config.Image}}'\n# ex : postgres:15.7\n# Mettre à jour le Compose :\n# image: postgres:15.7\n```\n\nCommittez ce changement dans votre VCS avant de poursuivre. La migration ne peut se faire en place : PG 17 refuse de démarrer sur un `PGDATA` PG 15 (format interne incompatible).",{"title":58,"body":59},"Capturer le dump global et les dumps de bases","Exportez d'abord les objets globaux (rôles, tablespaces), puis chaque base individuellement. Les flags `--no-owner` et `--no-acl` évitent les erreurs de droits lors de la restauration sur un superuser différent :\n\n```bash\n# Objets globaux (rôles, tablespaces)\ndocker exec \u003Cpg15_container> pg_dumpall \\\n  -U postgres \\\n  --globals-only \\\n  > backup_globals.sql\n\n# Chaque base applicative\ndocker exec \u003Cpg15_container> pg_dump \\\n  -U postgres \\\n  --no-owner \\\n  --no-acl \\\n  --format=custom \\\n  --file=\u002Ftmp\u002Fmydb_pg15.dump \\\n  mydb\n\ndocker cp \u003Cpg15_container>:\u002Ftmp\u002Fmydb_pg15.dump .\u002Fmydb_pg15.dump\n```\n\nVérifiez l'intégrité du dump avant d'aller plus loin :\n\n```bash\npg_restore --list mydb_pg15.dump | head -20\n```\n\nUn dump corrompu ou vide ici signifie que la migration s'arrête — ne passez jamais à l'étape suivante sans avoir validé le dump.",{"title":61,"body":62},"Démarrer le conteneur PG 17 en parallèle sur un port différent","Ajoutez un second service dans votre `docker-compose.yml` pour PG 17, **sur un port distinct** (ex : 5433), avec un volume de données neuf. Le service PG 15 reste actif pendant toute cette étape — aucune interruption pour vos applications :\n\n```yaml\n  postgres17:\n    image: postgres:17\n    environment:\n      POSTGRES_USER: ${POSTGRES_USER}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      POSTGRES_DB: ${POSTGRES_DB}\n    volumes:\n      - pg17_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n    ports:\n      - \"5433:5432\"\n    shm_size: 256mb\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER}\"]\n      interval: 5s\n      timeout: 3s\n      retries: 5\n\nvolumes:\n  pg17_data:\n```\n\nLancez uniquement ce service :\n\n```bash\ndocker compose up -d postgres17\ndocker compose exec postgres17 pg_isready\n```",{"title":64,"body":65},"Restaurer sur PG 17 et vérifier les extensions","Restaurez les objets globaux, puis la base :\n\n```bash\n# Rôles et tablespaces\ndocker exec -i \u003Cpg17_container> psql -U postgres \u003C backup_globals.sql\n\n# Restauration de la base\ndocker cp mydb_pg15.dump \u003Cpg17_container>:\u002Ftmp\u002Fmydb_pg15.dump\n\ndocker exec \u003Cpg17_container> pg_restore \\\n  -U postgres \\\n  --no-owner \\\n  --no-acl \\\n  -d mydb \\\n  \u002Ftmp\u002Fmydb_pg15.dump\n```\n\nSi pg_restore signale des erreurs sur des extensions, corrigez-les avant de continuer :\n\n```bash\n# Créer l'extension manquante sur PG 17\ndocker exec \u003Cpg17_container> psql -U postgres -d mydb \\\n  -c 'CREATE EXTENSION IF NOT EXISTS pgvector;'\n\n# Vérifier que toutes les extensions attendues sont là\ndocker exec \u003Cpg17_container> psql -U postgres -d mydb \\\n  -c 'SELECT extname, extversion FROM pg_extension ORDER BY extname;'\n```\n\nLes erreurs de type `ERROR: function X does not exist` lors de la restauration indiquent une extension absente ou incompatible — ne pas ignorer ces messages.",{"title":67,"body":68},"Basculer les applications et valider","Mettez vos applications en mode maintenance ou lecture seule (selon votre architecture), effectuez un dernier dump de cohérence depuis PG 15, puis mettez à jour la variable `DATABASE_URL` (ou `POSTGRES_HOST`\u002F`POSTGRES_PORT`) de chaque service applicatif pour pointer vers le conteneur PG 17 sur le port 5432. Redémarrez les services applicatifs :\n\n```bash\n# Vérification applicative minimale\ndocker compose exec app php artisan db:monitor\n# ou\ncurl -sf http:\u002F\u002Flocalhost\u002Fapi\u002Fhealth | jq .database\n```\n\nSi la validation passe, arrêtez le conteneur PG 15, remappez le port 5432 sur `postgres17`, et supprimez le service `postgres17` en le renommant en service principal. Supprimez l'ancien volume PG 15 après 30 jours de rétention.",{"type":70,"body":71},"tip","**Le search_path est le changement silencieux le plus fréquent.** Depuis PG 15.1, le schéma `public` n'est plus inclus dans le `search_path` par défaut des rôles non-superuser. Si vos migrations Flyway, Liquibase ou vos seeds Artisan échouent avec `relation \"X\" does not exist` après la migration, ajoutez `SET search_path TO public, \"$user\";` en tête de session ou qualifiez explicitement vos tables. Vérifiez aussi le paramètre dans `postgresql.conf` : `search_path = 'public'` (avec guillemets simples) force le comportement attendu pour tous les rôles.",{"type":33,"title":73,"body":74},"Le cas Supabase : postgres:17 sans avertissement","La discussion #46080 du changelog public Supabase (github.com\u002Forgs\u002Fsupabase\u002Fdiscussions\u002F46080) documente le basculement de l'image de référence vers `postgres:17` intervenu en juin 2026. Les stacks auto-hébergées qui référençaient `image: supabase\u002Fpostgres` sans tag de version ont reçu PG 17 lors du prochain `docker compose pull`, sans migration automatique des données.\n\nLe comportement observé : le conteneur PG 17 démarre, refuse de lire le `PGDATA` PG 15 (format incompatible), et s'arrête immédiatement. Docker Compose tente les redémarrages configurés, puis marque le service `unhealthy` ou `exited`. L'application tombe. La cause n'est pas visible dans les logs applicatifs — elle est dans les logs du conteneur postgres :\n\n```bash\ndocker logs \u003Csupabase_db_container> 2>&1 | head -20\n# FATAL: database files are incompatible with server\n# DETAIL: The data directory was initialized by PostgreSQL version 15, which is not compatible with this version 17.\n```\n\nLa bonne pratique pour tout déploiement Supabase auto-hébergé est de fixer le tag à une version mineure précise dans votre `docker-compose.yml` :\n\n```yaml\n  db:\n    image: supabase\u002Fpostgres:15.8.1.040\n    # ou la dernière 17.x une fois la migration effectuée\n    # image: supabase\u002Fpostgres:17.4.1.016\n```\n\nConsultez la page des releases GitHub de Supabase pour identifier le dernier tag de chaque branche majeure. Un tag flottant (`latest`, `15`, `17`) délègue la décision de mise à jour à l'éditeur — sans contrôle de votre côté sur le moment où elle intervient.",{"type":76,"title":77,"headers":78,"rows":82},"comparison","Stratégies de migration : pg_dump\u002Frestore vs pg_upgrade vs réplication logique",[79,80,81],"Stratégie","Durée de coupure","Complexité",[83,87,91],[84,85,86],"pg_dump \u002F pg_restore (ce guide)","5-30 min selon le volume","Faible — outils natifs, reproductible",[88,89,90],"pg_upgrade en place","1-5 min (binaire rapide)","Élevée — exige accès au binaire PG 15 et PG 17 simultanément, difficile en Docker",[92,93,94],"Réplication logique (zero-downtime réel)","\u003C 1 min (bascule en ligne)","Très élevée — nécessite `wal_level = logical`, slots de réplication, synchronisation manuelle",{"type":33,"title":96,"body":97},"Dépannage : erreurs fréquentes et leurs causes","Les erreurs qui reviennent le plus souvent lors d'une migration PG 15 → 17 dans un contexte Docker :\n\n**`FATAL: database files are incompatible with server`**\nCause : le conteneur PG 17 a démarré sur le même volume que PG 15. Le format interne de `PGDATA` n'est pas rétrocompatible. Solution : toujours utiliser un volume neuf pour PG 17 et restaurer via `pg_restore`.\n\n**`ERROR: extension \"timescaledb\" is not available`** (ou `pg_partman`, `pg_cron`)\nCause : l'extension n'est pas compilée pour PG 17 dans l'image officielle `postgres:17`. Solution : utiliser une image dérivée qui embarque les extensions voulues (ex : `timescale\u002Ftimescaledb:latest-pg17`), ou compiler l'extension dans votre propre `Dockerfile`.\n\n**`ERROR: role \"X\" already exists`** lors de la restauration des globals\nCause : le dump `pg_dumpall --globals-only` inclut `CREATE ROLE` pour tous les rôles, et le rôle `postgres` superuser existe déjà dans l'instance PG 17 vierge. Solution : utiliser `--if-not-exists` ou filtrer les lignes `CREATE ROLE postgres` dans `backup_globals.sql` avant restauration.\n\n**`ERROR: relation \"public.X\" does not exist`** dans les applications\nCause : changement du `search_path` par défaut depuis PG 15.1. Solution : ajouter `options=-csearch_path=public` à la chaîne de connexion, ou poser `ALTER ROLE app_user SET search_path = 'public';` après la restauration.\n\n**`pg_restore: error: could not execute query: ERROR: invalid byte sequence for encoding \"UTF8\"`**\nCause : données encodées en `LATIN1` dans PG 15, et l'instance PG 17 a été initialisée en `UTF8` (par défaut). Solution : restaurer d'abord sur une instance PG 17 initialisée avec `POSTGRES_INITDB_ARGS: --encoding=LATIN1 --lc-collate=fr_FR.UTF-8`, ou migrer l'encodage des données dans un script intermédiaire.",{"type":37,"title":99,"items":100},"Checklist de bascule — à valider dans l'ordre avant chaque étape",[101,102,103,104,105,106],"**Avant de commencer** : liste des extensions actives extraite (`pg_extension`), version PG 15 épinglée dans le Compose, dump complet validé (`pg_restore --list` sans erreur).","**Après restauration sur PG 17** : toutes les extensions présentes et à la bonne version, `search_path` vérifié par un rôle applicatif (pas superuser), comptes de lignes comparés sur les 5 tables critiques entre PG 15 et PG 17.","**Avant bascule applicative** : fenêtre de maintenance annoncée, mode lecture seule activé si possible, dernier dump de cohérence capturé depuis PG 15.","**Après bascule applicative** : endpoint `\u002Fhealth` retourne 200 avec `database: ok`, logs applicatifs sans erreur `relation does not exist`, métriques `pg_stat_activity` sur PG 17 confirmant des connexions actives.","**Rétention PG 15** : volume PG 15 conservé 30 jours minimum, dump conservé 90 jours, procédure de rollback documentée (pointer le Compose vers PG 15 et redémarrer).","**Post-migration** : `ANALYZE VERBOSE;` lancé sur toutes les bases pour recalculer les statistiques du planificateur, `autovacuum` validé actif, monitoring adapté à PG 17 (`pg_stat_statements`, `pg_stat_bgwriter`).",{"type":33,"title":108,"body":109},"Gérer la migration dans un portefeuille de stacks clients","Pour une agence qui gère plusieurs environnements clients, la migration PG 15 → 17 n'est pas un événement unique — c'est un chantier à planifier par portefeuille.\n\n**Inventorier d'abord.** La commande suivante liste toutes les versions PostgreSQL en cours sur un VPS qui héberge plusieurs projets Compose :\n\n```bash\ndocker ps --format '{{.Names}}' | xargs -I{} sh -c \\\n  'docker exec {} psql -U postgres -qtAX -c \"SELECT current_setting(\\\"server_version\\\")\" 2>\u002Fdev\u002Fnull && echo \" \u003C- {}\"'\n```\n\nLe résultat identifie en une passe les conteneurs encore sur PG 15 à travers tout le parc.\n\n**Prioriser par risque.** Les stacks avec des extensions compilées maison ou `timescaledb`\u002F`pg_partman` ont besoin d'une image dérivée testée avant la bascule — elles demandent plus de temps. Les stacks avec uniquement `pgcrypto`, `uuid-ossp` et `hstore` basculent en 15 minutes.\n\n**Standardiser l'image.** Définir une image commune dans un registry interne (`registry.yourcompany.com\u002Fpostgres:17-base`) qui embarque les extensions validées par l'agence. Tous les projets Compose du portefeuille pointent cette image : la mise à jour de l'extension (ex : nouvelle version de `pgvector`) se propage en reconstruisant une seule image.\n\nLes sauvegardes quotidiennes incluses sur les offres Agency de ServOrbit donnent un filet de sécurité pour chaque migration : si une stack client présente un comportement inattendu dans les 24 heures suivant la bascule, la restauration repart d'un backup de la veille plutôt que d'un dump manuel préparé dans l'urgence.","Migration planifiée, portefeuille sous contrôle","Les agences qui gèrent plusieurs stacks clients n'ont pas le luxe de découvrir une coupure le soir d'une montée de version automatique. ServOrbit centralise la gestion des VPS des portefeuilles clients — infrastructure maîtrisée, mises à jour planifiées, sauvegardes quotidiennes incluses sur les offres Agency.","Voir les offres agences","\u002Fsolutions\u002Fagences",[115,132,149],{"id":116,"slug":117,"slugs":118,"title":122,"excerpt":123,"readTime":124,"views":16,"isPinned":17,"publishedAt":125,"category":126,"categories":127,"featuredImage":27,"bgImage":28,"posterImage":129,"relatedSolution":130},60,"heberger-supabase-vps",{"fr":117,"en":119,"ar":120,"es":121},"hosting-supabase-on-a-vps","استضافة-supabase-على-خادم-vps","alojar-supabase-en-un-vps","Héberger Supabase sur un VPS en 2026","Self-hostez Supabase sur votre VPS : Postgres, Auth, Storage et API REST avec Envoy Gateway. Migration Kong→Envoy, dépannage URLs S3.",11,"2026-04-21T00:00:00+00:00",{"id":20,"name":21,"slug":22,"color":23,"icon":24},[128],{"id":20,"name":21,"slug":22,"color":23,"icon":24},"\u002Fblog\u002Fcovers\u002Fheberger-supabase-vps-poster.svg",{"categorySlug":22,"appSlug":131},"supabase",{"id":133,"slug":134,"slugs":135,"title":139,"excerpt":140,"readTime":141,"views":16,"isPinned":17,"publishedAt":142,"category":143,"categories":144,"featuredImage":27,"bgImage":28,"posterImage":146,"relatedSolution":147},56,"heberger-postgresql-vps",{"fr":134,"en":136,"ar":137,"es":138},"postgresql-on-a-vps-a-reliable-and-controlled-database","postgresql-على-خادم-vps-قاعدة-بيانات-موثوقة-ومتحكم-بها","alojar-postgresql-en-un-vps","PostgreSQL sur VPS : base fiable et contrôlée","Hébergez PostgreSQL sur VPS : volumes, sauvegardes, accès réseau limité et configuration saine pour vos applications.",4,"2026-04-25T00:00:00+00:00",{"id":20,"name":21,"slug":22,"color":23,"icon":24},[145],{"id":20,"name":21,"slug":22,"color":23,"icon":24},"\u002Fblog\u002Fcovers\u002Fheberger-postgresql-vps-poster.svg",{"categorySlug":22,"appSlug":148},"postgresql-stack",{"id":150,"slug":151,"slugs":152,"title":156,"excerpt":157,"readTime":158,"views":16,"isPinned":17,"publishedAt":159,"category":160,"categories":165,"featuredImage":27,"bgImage":28,"posterImage":167,"relatedSolution":27},282,"docker-compose-depends-on-healthcheck",{"fr":151,"en":153,"ar":154,"es":155},"depends-on-is-not-enough-postgresql-healthcheck-in-compose","depends-on-لا-يكفي-healthcheck-لـ-postgresql-في-compose","docker-compose-healthcheck-postgresql","depends_on ne suffit pas : healthcheck PostgreSQL dans Compose","Pourquoi `depends_on` seul ne garantit pas que PostgreSQL est prêt, et comment configurer un healthcheck fiable avec `service_healthy` pour éviter les races conditions.",8,"2026-08-19T00:00:00+00:00",{"id":161,"name":162,"slug":163,"color":164,"icon":163},3,"Déploiement","deploiement","bg-success\u002F10 text-success",[166],{"id":161,"name":162,"slug":163,"color":164,"icon":163},"\u002Fblog\u002Fcovers\u002Fdocker-compose-depends-on-healthcheck-poster.svg",1788099962662]