Tutoriel

Migrer PostgreSQL depuis RDS, Cloud SQL ou un BaaS vers un VPS

Bases de données13 min de lecture6 étapes

La facture RDS a franchi un seuil douloureux, ou Supabase vient de revoir ses tarifs : le signal est clair, il est temps d'évaluer l'auto-hébergement. PostgreSQL sur un VPS dédié restitue un coût fixe et prévisible, supprime les restrictions d'extensions et élimine le verrou fournisseur. Ce guide couvre la migration depuis les trois sources managées les plus répandues — AWS RDS, Google Cloud SQL et Supabase — avec les commandes exactes, les pièges propres à chaque source, et une procédure de bascule progressive qui préserve la disponibilité de bout en bout.

Sommaire· Pourquoi quitter une base managée1/10
  1. 01Pourquoi quitter une base managée
  2. 02Ce que vous allez garder, ce que vous allez gérer vous-même
  3. 03Avant de migrer — checklist des pré-requis
  4. 04Migrer depuis AWS RDS
  5. 05Migrer depuis Google Cloud SQL
  6. 06Migrer depuis Supabase
  7. 07Commandes par source
  8. 08Bascule progressive sans downtime
  9. 09Vérification post-migration
  10. 10Pour aller plus loin

Pourquoi quitter une base managée

Les bases managées résolvent un problème réel : elles éliminent les opérations du premier jour (installation, réplication, sauvegardes automatiques). Mais leur modèle économique est conçu pour que la facture suive la croissance de façon non linéaire. Sur AWS RDS, chaque composant se facture séparément : le compute de l'instance, le stockage gp3 au Go, les IOPS provisionnés, le nœud secondaire Multi-AZ, les sauvegardes au-delà de la fenêtre incluse, les transferts de données sortants. Une instance db.r6g.xlarge en Multi-AZ avec 200 Go de stockage gp3 dépasse facilement 800 $/mois sur us-east-1 ; à 500 Go, on franchit 3 000 $/mois. La même charge sur un VPS dédié tourne à une fraction de ce coût.

Le coût n'est pas la seule variable. Les contraintes d'extensions sont un frein technique concret : pgvector sur RDS est disponible depuis 2023, mais pg_graphql ou d_btree_gist dans certaines versions ne le sont pas. Sur Supabase, le catalogue est plus large, mais il est lié à la plateforme — déplacer la base signifie ré-évaluer chaque extension. La conformité des données est un troisième moteur : stocker une base de données dans le datacenter de votre choix, sans transfert inter-région facturable, change la donne pour les architectures soumises au RGPD ou à des politiques de résidence des données. Enfin, la latence entre la base et l'application diminue mécaniquement quand les deux résident sur la même machine ou dans le même réseau privé — une optimisation gratuite sur un VPS, impossible sans frais supplémentaires sur une offre managée.

Ce que vous allez garder, ce que vous allez gérer vous-même

  • Vous gardez : PostgreSQL lui-même dans sa version de votre choix, toutes vos données, vos extensions, vos rôles et privilèges, vos chaînes de connexion une fois la variable DATABASE_URL mise à jour.
  • Vous gardez : les performances — sur un VPS NVMe bien dimensionné, les lectures et écritures aléatoires de PostgreSQL sont au moins équivalentes à celles d'une instance managée de même gabarit.
  • Vous gérez : les sauvegardes — pg_dump en cron, ou pg_basebackup pour une copie physique ; les fichiers doivent être stockés hors du serveur (stockage objet, second VPS).
  • Vous gérez : les mises à jour mineures (correctifs de sécurité) et majeures (migration de version) — la fréquence est faible, la procédure est documentée, mais personne ne les applique à votre place.
  • Vous gérez : les connexions SSL — générer un certificat, configurer ssl = on dans postgresql.conf, imposer TLS dans pg_hba.conf ; sans cela, les données transitent en clair.
  • Vous gérez : le monitoring — pg_stat_statements, pg_stat_activity, alertes sur l'espace disque et les connexions actives ; les tableaux de bord des fournisseurs disparaissent avec la base managée.

Avant de migrer — checklist des pré-requis

Avant de lancer le premier pg_dump, quatre points conditionnent le succès de la migration.

Version PostgreSQL source vs destination. Un dump produit par pg_dump d'une version N peut être restauré sur une version N ou supérieure, jamais inférieure. Vérifiez la version source : psql -h <endpoint> -U <user> -c 'SELECT version();'. Sur le VPS, installez la même version majeure ou supérieure depuis le dépôt PGDG. Une migration de version (ex. PostgreSQL 14 → 17) est possible en une passe, mais les changements de comportement (types de données, fonctions dépréciées) méritent un test applicatif complet avant la bascule.

Extensions actives. Listez les extensions installées sur la source : SELECT extname, extversion FROM pg_extension ORDER BY extname;. Certaines sont universellement disponibles (uuid-ossp, hstore, pgcrypto). D'autres nécessitent un paquet système supplémentaire (postgis → postgresql-17-postgis-3) ou une compilation depuis les sources (pgvector → paquet postgresql-17-pgvector sur Debian/Ubuntu). Les extensions propres à Supabase (pg_graphql, pg_net, pgsodium) n'existent pas dans un PostgreSQL vanilla : planifiez leur remplacement avant de migrer.

Taille de la base. SELECT pg_size_pretty(pg_database_size('<base>')); donne la taille logique. Le dump pg_dump -Fc produit un fichier compressé significativement plus petit. Prévoyez sur le VPS l'espace disque nécessaire pour le dump lui-même, la base restaurée, et 20 % de marge pour les WAL et les index temporaires de restauration.

Connexions actives. Une base sous trafic ne peut pas être dumpée proprement avec des transactions longues en cours. Vérifiez : SELECT count(*), state FROM pg_stat_activity GROUP BY state;. Si des connexions en état idle in transaction persistent depuis plusieurs minutes, identifiez et terminez ces sessions avant de lancer le dump de migration.

Migrer depuis AWS RDS

RDS impose TLS sur les connexions entrantes (rds.force_ssl = 1 par défaut sur les instances récentes). Le flag --sslmode=require est donc indispensable sur pg_dump, sans quoi la connexion est refusée ou établie sans chiffrement selon la configuration de l'instance.

Utilisez de préférence un endpoint de lecture (Read Replica) pour le dump : cela évite d'ajouter de la charge sur l'instance primaire pendant la migration.

# Dump depuis RDS (endpoint de lecture si disponible)
export PGPASSWORD='votre-mot-de-passe'
pg_dump \
  -h votre-instance.xxxx.rds.amazonaws.com \
  -U votre_user \
  --sslmode=require \
  -Fc \
  -f /tmp/dump-rds.pgc \
  votre_base

Puis restaurez sur le VPS en parallèle pour accélérer l'import :

pg_restore \
  --no-owner \
  --no-privileges \
  -h localhost \
  -U votre_user_vps \
  -d votre_base \
  -j 4 \
  /tmp/dump-rds.pgc

--no-owner et --no-privileges sont indispensables : RDS crée des rôles internes (rdsadmin, rds_superuser) absents de tout PostgreSQL hors AWS. Sans ces flags, pg_restore échoue en tentant de réassigner la propriété des objets à ces rôles inexistants.

Variables RDS non transférables : rds.force_ssl, rds.log_retention_period, rds_superuser et tout paramètre préfixé rds. sont des paramètres de système propriétaire AWS — ils n'existent pas dans postgresql.conf standard. Si votre application vérifie la valeur de rds.force_ssl via SHOW rds.force_ssl, elle obtiendra une erreur sur le VPS. Remplacez cette vérification par SHOW ssl.

Migrer depuis Google Cloud SQL

Cloud SQL propose deux voies d'export : le Cloud SQL Auth Proxy (recommandé pour les connexions programmatiques) et l'export vers un bucket GCS (recommandé pour les dumps volumineux ou les migrations unidirectionnelles).

Via Cloud SQL Auth Proxy (connexion directe, adapté aux bases < 10 Go) :

# Démarrer le proxy en arrière-plan
cloud_sql_proxy -instances=mon-projet:europe-west1:mon-instance=tcp:5433 &

# Dump via le proxy (port local 5433)
export PGPASSWORD='votre-mot-de-passe'
pg_dump \
  -h 127.0.0.1 \
  -p 5433 \
  -U votre_user \
  -Fc \
  -f /tmp/dump-cloudsql.pgc \
  votre_base

Via export GCS (recommandé pour les bases volumineuses) :

# Export depuis la console gcloud (format SQL ou CSV)
gcloud sql export sql mon-instance \
  gs://mon-bucket/dump-cloudsql.sql \
  --database=votre_base \
  --offload

# Téléchargement local
gsutil cp gs://mon-bucket/dump-cloudsql.sql /tmp/

# Import sur le VPS (format SQL)
psql -h localhost -U votre_user_vps -d votre_base \
  -f /tmp/dump-cloudsql.sql

Notez que gcloud sql export sql produit un fichier SQL brut, pas un fichier au format custom pg_dump -Fc. L'import avec psql -f est plus lent que pg_restore -j mais fonctionne sur ce format. Pour un dump au format custom compatible pg_restore, utilisez obligatoirement le Cloud SQL Auth Proxy avec pg_dump -Fc.

Pièges Cloud SQL : le flag --offload déplace le traitement du dump vers un worker secondaire et réduit l'impact sur l'instance primaire. Sans lui, un dump long peut affecter les performances en production. Autre piège : Cloud SQL crée un utilisateur cloudsqladmin qui apparaît dans certains objets de la base — appliquez --no-owner --no-privileges à la restauration comme pour RDS.

Migrer depuis Supabase

Supabase expose un accès direct à PostgreSQL via une URI de connexion disponible dans les paramètres du projet (Settings > Database > Connection string > URI). Cet accès fonctionne avec pg_dump standard.

# Récupérer l'URI depuis le tableau de bord Supabase
# Format : postgresql://postgres:[PASSWORD]@db.[PROJECT_REF].supabase.co:5432/postgres

export DATABASE_URL="postgresql://postgres:[email protected]:5432/postgres"

pg_dump \
  "$DATABASE_URL" \
  --sslmode=require \
  -Fc \
  --no-owner \
  --no-privileges \
  -f /tmp/dump-supabase.pgc

Extensions présentes sur Supabase absentes d'un VPS vanilla. C'est le principal piège de la migration depuis Supabase. Le catalogue d'extensions de Supabase est significativement plus large que celui d'une installation PostgreSQL standard :

- pgvector : disponible sur Supabase, doit être installé manuellement sur le VPS (sudo apt install postgresql-17-pgvector sur Debian/Ubuntu, puis CREATE EXTENSION vector;).
- pg_graphql : extension Supabase propriétaire qui expose un endpoint GraphQL automatique sur la base. Elle n'existe pas dans les dépôts APT standard. Si votre application l'utilise, vous devez soit compiler l'extension depuis les sources (Rust + cargo-pgrx requis), soit réécrire la couche d'accès GraphQL en dehors de la base.
- pg_net : permet des requêtes HTTP depuis des fonctions SQL. Absente des paquets standard — réécrire les usages en dehors de la base de données.
- pgsodium : chiffrement au niveau de la base (column-level encryption). Présente dans certains dépôts tiers, mais son intégration avec Vault est spécifique à Supabase.
- http : extension communautaire disponible via postgresql-17-http sur Debian/Ubuntu.

Avant de lancer le dump, recensez exhaustivement les extensions actives avec SELECT extname FROM pg_extension WHERE extname NOT IN ('plpgsql'); et vérifiez pour chacune si un paquet APT existe, si une compilation est nécessaire, ou si la fonctionnalité doit migrer hors de la base.

Commandes par source

Faites défiler le tableau

SourceCommande pg_dumpPièges connusVariable à reconfigurer
AWS RDS`pg_dump -h <endpoint> -U <user> --sslmode=require -Fc -f dump.pgc <base>``--no-owner --no-privileges` requis ; rôles `rdsadmin`/`rds_superuser` absents du VPS`rds.force_ssl` → remplacer par `ssl = on` dans `postgresql.conf`
Google Cloud SQL`pg_dump -h 127.0.0.1 -p 5433 -U <user> -Fc -f dump.pgc <base>` via Auth Proxy, ou `gcloud sql export sql` → bucket GCS`--offload` pour réduire l'impact sur la primaire ; `--no-owner --no-privileges` contre `cloudsqladmin`Auth Proxy à supprimer ; connexion directe VPS en remplacement
Supabase`pg_dump "$DATABASE_URL" --sslmode=require -Fc --no-owner --no-privileges -f dump.pgc``pg_graphql`, `pg_net`, `pgsodium` absents sur VPS vanilla ; `pgvector` à installer manuellementURL de connexion Supabase → `DATABASE_URL` VPS direct ; Supabase Auth à découpler

Bascule progressive sans downtime

  1. Snapshot avant — sauvegarde de la source à un instant T

    Avant toute opération, prenez un snapshot manuel de l'instance source depuis la console du fournisseur (RDS : Actions > Take snapshot ; Cloud SQL : Operations > Create backup ; Supabase : Settings > Backups > Trigger backup). Ce snapshot est votre filet de sécurité : il permet de revenir à l'état exact de la base avant la migration, indépendamment de ce qui se passe sur le VPS.

  2. pg_dump prod — dump complet de la base de production

    Lancez le dump selon la commande propre à votre source (voir section précédente). Notez l'heure de début et l'heure de fin du dump — toutes les transactions validées après le début du dump ne seront pas dans le fichier. Sur RDS et Cloud SQL, utilisez l'endpoint de lecture si disponible pour limiter l'impact sur la primaire. Vérifiez que le fichier dump.pgc est non-vide et se termine sans erreur : ls -lh /tmp/dump-*.pgc.

  3. pg_restore VPS — restauration et vérification initiale

    Sur le VPS, créez la base cible et restaurez : createdb -U postgres votre_base && pg_restore --no-owner --no-privileges -h localhost -U votre_user -d votre_base -j 4 /tmp/dump-source.pgc. Une fois la restauration terminée, vérifiez les comptages sur les tables critiques : SELECT relname, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 20; sur les deux instances et comparez les chiffres. Un écart de quelques lignes correspond aux transactions survenues pendant le dump — c'est attendu.

  4. Lecture seule côté source — gel de la base d'origine

    Choisissez une fenêtre de bascule (nuit, week-end, creux de trafic). Au début de cette fenêtre, placez la base source en lecture seule pour arrêter l'écart entre source et destination : ALTER DATABASE votre_base SET default_transaction_read_only = on;. Sur RDS, cela peut aussi se faire en promouvant la Read Replica comme primaire et en coupant les writes vers l'ancienne primaire. Avertissez les utilisateurs si nécessaire. Cette étape est le seul instant où un court arrêt des writes est inévitable — elle dure en général moins de 30 secondes.

  5. Vérification des comptages — cohérence avant bascule

    Avec la source en lecture seule, lancez un second dump différentiel ou vérifiez manuellement les comptages table par table : psql -h <source> -U <user> -c 'SELECT count(*) FROM votre_table_principale;' et comparez avec psql -h localhost -U votre_user -c 'SELECT count(*) FROM votre_table_principale;'. Si l'écart persiste, relancez un dump incrémental des enregistrements créés après l'heure de début du premier dump (via un filtre WHERE created_at > '<timestamp>') et importez-les. Continuez jusqu'à convergence.

  6. Bascule DATABASE_URL — l'application pointe sur le VPS

    Mettez à jour la variable d'environnement DATABASE_URL (ou son équivalent dans votre fichier de configuration) pour pointer vers le VPS : postgresql://votre_user:votre-mdp@<IP-VPS>:5432/votre_base?sslmode=require. Redémarrez l'application. Vérifiez que les premières requêtes aboutissent, que les logs ne contiennent pas d'erreurs de connexion, et que les écritures arrivent bien sur le VPS (SELECT count(*) FROM pg_stat_activity WHERE state = 'active';). La source reste disponible en lecture seule pendant encore 24 à 48 heures — ne la supprimez pas immédiatement.

Vérification post-migration

Quatre vérifications à effectuer dans l'heure suivant la bascule.

Comptage de lignes. Sur les dix tables les plus volumineuses, comparez les comptages source et destination. Un écart nul confirme que toutes les transactions ont été capturées ; un écart résiduel de quelques lignes peut indiquer des transactions encore en vol au moment de la bascule — vérifiez les logs applicatifs pour les identifier.

Checksums. Sur les tables critiques (transactions, commandes, données sensibles), calculez un checksum de contrôle : SELECT md5(string_agg(id::text || montant::text, ',' ORDER BY id)) FROM commandes; et comparez le résultat sur les deux instances. Un checksum identique confirme l'intégrité des données ligne à ligne.

Test des extensions. Pour chaque extension utilisée par l'application, exécutez une requête de test minimale : SELECT 'test'::vector(3) pour pgvector, SELECT gen_random_uuid() pour pgcrypto, SELECT ST_AsText(ST_MakePoint(1, 1)) pour PostGIS. Une extension manquante ou mal configurée remonte immédiatement.

Connexions applicatives. Vérifiez dans les logs de l'application que les connexions s'établissent bien sur le VPS et non sur l'ancienne base : grep 'DATABASE_URL\|database connection' /var/log/app/*.log. Vérifiez aussi que les connexions SSL sont actives : SELECT ssl, client_addr FROM pg_stat_ssl JOIN pg_stat_activity USING (pid) WHERE client_addr IS NOT NULL LIMIT 5;.

Rollback sous 5 minutes. Pendant les 24 à 48 heures qui suivent la bascule, la source reste en vie en lecture seule. Si un problème bloquant est détecté, le rollback est immédiat : remettez l'ancienne DATABASE_URL dans la variable d'environnement et redémarrez l'application. Aucune perte de données — la source n'a pas été modifiée depuis la bascule (elle est en lecture seule) et le VPS a enregistré les nouvelles écritures depuis la bascule. Pour récupérer ces écritures du VPS vers la source après un rollback, exportez les enregistrements créés après l'heure de bascule et importez-les manuellement. Ne supprimez jamais la source avant 48 heures de stabilité sur le VPS.

Pour aller plus loin

Ce guide couvre la migration unidirectionnelle — la procédure la plus courante pour quitter un fournisseur managé. Trois sujets complémentaires méritent d'être explorés selon votre contexte.

PostgreSQL auto-hébergé vs Amazon RDS — un comparatif ROI chiffré entre les deux modèles, avec les seuils de taille de base et de trafic à partir desquels l'auto-hébergement devient clairement avantageux.

Héberger PostgreSQL sur un VPS — la configuration détaillée post-installation : postgresql.conf, pg_hba.conf, pgBouncer, monitoring avec pg_stat_statements, sauvegardes automatisées et durcissement réseau.

PostgreSQL haute disponibilité avec Patroni sur VPS — si votre SLA exige un failover automatique, Patroni sur un cluster de trois nœuds (primaire + deux réplicas) reproduit les garanties de Multi-AZ RDS ou Cloud SQL HA, sans le surcoût fournisseur.

Déployez votre base PostgreSQL sur un VPS dédié

Accès root, stockage SSD NVMe, IPv4 incluse, sans surcoût au Go stocké. Choisissez votre configuration et migrez en toute sérénité.

Besoin d'aide ?

Parcourez notre centre d'aide et notre FAQ, ou contactez notre équipe — rappel, WhatsApp ou e-mail. Support en français, anglais et arabe.

Écrire sur WhatsApps'ouvre dans un nouvel onglet