[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fuXYMboDuHd1yVfg2r4Jn3OybO2-rU3qm1olX3uNEOrk":3,"$fDN32ajgbZgWcses4RmxRIEiiHzpaH65p9_3efXNxeQw":21},{"id":4,"slug":5,"titleFr":6,"titleEn":7,"titleKm":8,"contentFr":9,"contentEn":10,"contentKm":8,"excerptFr":11,"excerptEn":12,"excerptKm":8,"tags":13,"createdAt":20,"updatedAt":20},124,"migrer-base-donnees-scaleway-serverless-sql","Migrer ma base de données sans perdre un octet : Neon → Scaleway Serverless SQL","Migrating my database without losing a byte: Neon → Scaleway Serverless SQL",null,"## La promesse laissée en suspens\n\nDans [« Comment j'ai migré 7 applis vers Scaleway »](/blog/migration-gcp-vercel-scaleway), j'avais laissé une phrase en l'air : *« la base de données suivra bientôt vers Scaleway Serverless SQL »*. Voilà, c'est fait. Et c'est le chapitre le plus délicat de toute la migration : une base de données, ça ne se redéploie pas — ça se **déplace**, avec tout ce qu'elle contient, sans en perdre une miette.\n\n## Règle d'or : zéro octet perdu\n\nMa base `neondb` héberge tout — articles, expériences, compétences, médiathèque, activités Strava, pompes… Le principe non négociable : **on ne coupe rien tant que tout n'est pas vérifié.**\n\nLa procédure : `pg_dump` de Neon → restore dans une base Serverless SQL fraîche → **comparaison ligne par ligne**, table par table. 14 tables, comptages identiques des deux côtés. Neon reste **100% intact** comme filet de sécurité jusqu'à validation complète.\n\nPetit détail piquant : Neon tourne en PostgreSQL 17, Scaleway en 16. Un `pg_dump` version 16 refuse de lire un serveur 17 → je suis passé par une image Docker `postgres:17` pour le dump. Cinq minutes de galère évitées.\n\n## L'astuce pour dé-risquer : changer le driver *avant* l'URL\n\nMes apps parlaient à Neon via son driver « edge » (`@neondatabase/serverless`, en HTTP) — qui ne parle **qu'à** Neon. Scaleway, lui, veut du PostgreSQL standard. Il fallait donc passer à `node-postgres`.\n\nLe truc malin : **`node-postgres` fonctionne aussi sur Neon.** J'ai donc déployé le changement de driver en gardant le pointage vers Neon — les apps tournaient toujours, rien de cassé. La bascule finale n'était alors plus qu'un simple **changement d'URL**. Le risque, découpé en deux étapes inoffensives au lieu d'un grand saut.\n\n## Les deux pièges qui m'ont coûté une heure\n\n**1. L'username n'est pas celui qu'on croit.** Sur Scaleway Serverless SQL, on s'authentifie avec une clé IAM. Instinctivement, j'ai mis l'*access key* comme utilisateur. `FATAL: password authentication failed`, encore et encore, malgré une config identique à une autre base qui marchait. La doc, enfin lue à la loupe : *« logins are IAM principal IDs »*. L'username, c'est **l'ID de l'application IAM**, pas l'access key. Une ligne dans la doc, une heure de ma vie.\n\n**2. Le `search_path` vide.** Serverless SQL démarre avec un `search_path` vide. Résultat : `SELECT ... FROM blog_posts` échoue avec « relation does not exist »… alors que la table est bien là, dans le schéma `public`. Le fix tient en une ligne, exécutée à chaque connexion : `SET search_path TO public`.\n\n## La bascule, prouvée noir sur blanc\n\nComme plusieurs apps partagent la même base, on ne bascule pas l'une sans les autres : re-sync final, changement d'URL sur toutes en même temps (via la console, pour ne pas écraser les autres secrets), vérification.\n\nComment être *sûr* que les écritures partent bien vers Scaleway et plus vers Neon ? Une **sentinelle** : j'écris une ligne via l'API, puis je regarde où elle atterrit. Présente dans Scaleway ✅, absente de Neon ✅. La preuve est faite, la sentinelle supprimée.\n\n## La boucle est bouclée\n\nCompute, stockage, et maintenant **base de données** : tout mon écosystème perso est désormais souverain, français, scale-to-zero, sous `chetana.fr`. Data et calcul enfin **co-localisés à Paris** — fini les allers-retours transatlantiques vers un serveur américain à chaque requête.\n\nNeon reste quelques jours en filet, le temps d'être serein, puis extinction. Le grand chantier de la souveraineté est terminé. 🇫🇷","## The promise left hanging\n\nIn [“How I migrated 7 apps to Scaleway”](/blog/migration-gcp-vercel-scaleway) I left a sentence dangling: *“the database will soon follow to Scaleway Serverless SQL.”* Now it's done. And it's the trickiest chapter of the whole migration: you don't redeploy a database — you **move** it, with everything inside, without losing a crumb.\n\n## Golden rule: zero bytes lost\n\nMy `neondb` database holds everything — articles, experiences, skills, media library, Strava activities, push-ups… The non-negotiable principle: **cut nothing until everything is verified.**\n\nThe procedure: `pg_dump` from Neon → restore into a fresh Serverless SQL database → **row-by-row comparison**, table by table. 14 tables, identical counts on both sides. Neon stays **100% intact** as a safety net until full validation.\n\nA spicy little detail: Neon runs PostgreSQL 17, Scaleway runs 16. A version-16 `pg_dump` refuses to read a version-17 server → I dumped through a `postgres:17` Docker image. Five minutes of pain avoided.\n\n## The de-risking trick: swap the driver *before* the URL\n\nMy apps talked to Neon via its “edge” driver (`@neondatabase/serverless`, over HTTP) — which only talks **to** Neon. Scaleway wants standard PostgreSQL. So I had to switch to `node-postgres`.\n\nThe clever bit: **`node-postgres` works on Neon too.** So I deployed the driver change while still pointing at Neon — the apps kept running, nothing broke. The final cutover was then just a **URL change**. The risk, sliced into two harmless steps instead of one big leap.\n\n## The two pitfalls that cost me an hour\n\n**1. The username isn't what you think.** On Scaleway Serverless SQL you authenticate with an IAM key. Instinctively, I used the *access key* as the username. `FATAL: password authentication failed`, over and over, despite a config identical to another database that worked. The docs, finally read closely: *“logins are IAM principal IDs.”* The username is **the IAM application ID**, not the access key. One line in the docs, one hour of my life.\n\n**2. The empty `search_path`.** Serverless SQL starts with an empty `search_path`. Result: `SELECT ... FROM blog_posts` fails with “relation does not exist”… even though the table is right there, in the `public` schema. The fix is one line, run on each connection: `SET search_path TO public`.\n\n## The cutover, proven in black and white\n\nSince several apps share the same database, you don't switch one without the others: final re-sync, URL change on all of them at once (via the console, to avoid wiping the other secrets), verification.\n\nHow to be *sure* writes go to Scaleway and no longer to Neon? A **sentinel**: I write a row via the API, then check where it lands. Present in Scaleway ✅, absent from Neon ✅. Proof made, sentinel deleted.\n\n## The loop is closed\n\nCompute, storage, and now the **database**: my entire personal ecosystem is now sovereign, French, scale-to-zero, under `chetana.fr`. Data and compute finally **co-located in Paris** — no more transatlantic round-trips to an American server on every query.\n\nNeon stays a few more days as a safety net, until I'm at peace, then shutdown. The great sovereignty project is complete. 🇫🇷","Le chapitre final de la migration souveraine : déplacer une base PostgreSQL complète vers Scaleway Serverless SQL, zéro perte — avec les deux pièges (username = ID app IAM, search_path vide) qui m'ont coûté une heure.","The final chapter of the sovereign migration: moving a full PostgreSQL database to Scaleway Serverless SQL, zero loss — with the two pitfalls (username = IAM app ID, empty search_path) that cost me an hour.",[14,15,16,17,18,19],"scaleway","postgresql","migration","serverless-sql","souveraineté","devops","2026-07-13T14:14:23.303711",[]]