Coolify OVH & Migration Supabase sans catastrophe.
Tout ce qu'il faut activer sur le nouveau Coolify, et tout ce qui peut planter si tu oublies de le faire pendant la migration Supabase. Format QQOQCP, liens entre sections.
Partie 01 — Paramétrage Coolify OVH
Source Git — GitHub App Étape zéro
Sans ça, Coolify ne peut pas puller tes repos. À configurer avant tout déploiement d'application — avant même les Shared Variables.
Qui
Qui fait l'action ?
Toi, une seule fois au niveau Team. Ensuite chaque app hérite automatiquement de l'accès.
Quoi
C'est quoi concrètement ?
Une connexion OAuth entre Coolify et GitHub. Une GitHub App installée sur ton compte GitHub donne à Coolify l'accès à tous tes repos privés en une fois.
Où
Où configurer ?
coolify.<ton-domaine> → Sources → Add New → GitHub App → suivre le flow OAuth. GitHub te redirige vers Coolify automatiquement à la fin.
Quand
À quel moment ?
En premier, avant tout le reste. Première action après l'installation Coolify et la configuration SSH du VPS bootstrap.
Comment
Comment ça marche ensuite ?
Quand tu crées une app dans Coolify, tu sélectionnes la GitHub App comme source, tu choisis le repo et la branche. Coolify installe automatiquement un webhook sur le repo GitHub.
Pourquoi
Pourquoi GitHub App et pas Deploy Key ?
Tu as 21+ microservices. Une GitHub App = accès à tous d'un coup. Une Deploy Key = une clé SSH à déposer manuellement sur chaque repo. Pas le même travail.
Ce que ça débloque : l'auto-deploy sur push
Une fois la GitHub App connectée, dans chaque app Coolify tu peux activer Auto Deploy. Le flux devient :
# Flux automatique après configurationgit push origin main
→ GitHub reçoit le push
→ GitHub envoie un webhook à Coolify (via le webhook_secret sécurisé)
→ Coolify pull le nouveau code
→ Coolify rebuild le container
→ Coolify redéploie
→ Notification Telegram "✅ bi.zoomali.io déployé"
Zéro intervention manuelle. Du push au déploiement en production en 2-3 minutes.
Paramètre à activer dans chaque app
Paramètre
Où
Valeur recommandée
Auto Deploy
App → General → Auto Deploy
Activé sur la branche main
Watch branches
App → General → Branch
main uniquement en prod
Preview Deployments
App → General → Preview Deployments
À décider app par app — crée un sous-domaine par PR GitHub
Webhook secret
Coolify génère automatiquement un webhook_secret par GitHub App et le configure côté GitHub. Ne pas modifier manuellement ce secret — il sert à vérifier que les webhooks viennent bien de GitHub et pas d'une source externe.
Bonne nouvelle pour la migration
Tu peux connecter la même GitHub App sur le Coolify OVH et sur le Coolify Hostinger (si tu l'as). Tes repos GitHub ne changent pas — seule la destination du déploiement change quand tu bascules le DNS.
Configurer avant le premier déploiement d'application. Tout le reste en dépend.
Qui
Qui fait l'action ?
Toi, dans l'interface Coolify OVH. Une seule fois.
Quoi
C'est quoi concrètement ?
Des variables d'environnement définies une fois au niveau Team, injectées automatiquement dans toutes les apps qui les référencent.
Où
Où dans l'interface ?
coolify.<ton-domaine> → Team Settings → Shared Variables
Quand
À quel moment ?
Avant tout déploiement d'app. C'est la première chose à faire après l'installation Coolify.
Comment
Comment ça marche ?
Tu crées la variable au niveau Team. Dans chaque app, au lieu de coller la valeur, tu références la Shared Variable. Coolify l'injecte au démarrage du container.
Pourquoi
Pourquoi c'est important ?
Quand Supabase change d'IP, tu modifies une ligne au lieu de mettre à jour 21 apps manuellement.
Règle de décision
Si une variable apparaît dans 2 apps ou plus → Shared Variable. Si elle est vraiment spécifique à une seule app (ex: un domaine, un feature flag) → reste au niveau app.
Juste après les Shared Variables. Avant le premier déploiement réel.
Comment
Quelle config ?
Bot Token : @zoomali_bot. Chat ID : ton ALLOWED_CHAT_ID. Événements : Deployment Success + Deployment Failed + Container Stopped.
Pourquoi
Pourquoi c'est important ?
Tu n'as plus à surveiller l'interface. Tu sais en temps réel si un déploiement a planté sans te connecter au VPS.
Bonus
Le bot de monitoring que tu as déjà sur OVH (@zoomali_bot) peut recevoir les notifications Coolify dans le même chat. Une seule conversation pour tout surveiller.
Tes apps et Supabase sont sur le même VPS. Fais-les parler en interne, pas par Internet.
Qui
Qui fait l'action ?
Claude Code (configuration Docker) + toi (validation des variables d'env dans chaque app).
Quoi
C'est quoi concrètement ?
Les apps qui tournent sur le même serveur Coolify partagent un réseau Docker interne. Communication directe sans passer par DNS public.
Où
Où configurer ?
Dans les variables d'env de chaque app, remplacer l'URL Supabase publique par l'adresse interne Docker.
Quand
À quel moment ?
Au moment du déploiement de Supabase sur OVH. Avant de migrer les apps.
Comment
Quelle config concrète ?
Toutes les apps Coolify d'un même serveur sont déjà sur le réseau coolify. Il suffit d'utiliser le nom du service comme hostname.
Pourquoi
Pourquoi c'est important ?
Plus rapide (pas de round-trip Internet). Plus sécurisé (pas exposé). Économise ta bande passante OVH.
Exemple concret
# Avant (passe par Internet)SUPABASE_DB_URL=postgresql://postgres:password@db.zoomali.io:5432/postgres# Après (réseau Docker interne — plus rapide, plus sûr)SUPABASE_DB_URL=postgresql://postgres:password@supabase-db:5432/postgres
Attention
L'URL interne ne fonctionne que pour les connexions container-à-container. L'URL publique reste nécessaire pour le client JavaScript côté navigateur (il ne peut pas accéder au réseau Docker).
Mettre une app en ligne visible sur Internet mais accessible uniquement à certains utilisateurs. Deux méthodes actives selon le contexte, une méthode avancée en réserve.
Pourquoi pas la restriction par IP ?
L'IP de ton téléphone change constamment (4G, wifi maison, wifi café). Ton FAI peut aussi changer ton IP fixe. Tu passerais ton temps à mettre à jour la liste. Les méthodes ci-dessous sont bien plus robustes et ne dépendent d'aucun appareil spécifique.
Méthode A — Supabase Auth Méthode principale
Pour toutes les apps que tu développes toi-même. Tu as déjà l'infra — autant l'utiliser.
Qui
Qui est concerné ?
Toi seul pour les outils perso, ou tes utilisateurs pour les apps SaaS. Contrôle total depuis Supabase Dashboard → Auth → Users.
Quoi
C'est quoi concrètement ?
Une page de login qui appelle supabase.auth.signInWithPassword(). Si la session est valide → contenu affiché. Sinon → redirect vers le login.
Où
Où configurer ?
Dans le code de l'app. Un middleware vérifie la session Supabase sur chaque route protégée. Zéro configuration côté Coolify ou serveur.
Quand
Cas d'usage ?
Ce guide HTML mis en ligne → login perso bi.zoomali.io → accès équipe saas.zoomali.io → accès clients Tout outil Next.js/React que tu codes
Comment
Exemple concret ?
Page /login + middleware Next.js qui vérifie supabase.auth.getSession(). Si pas de session → redirect /login. Le navigateur mémorise la session.
Pourquoi
Pourquoi c'est le meilleur choix ?
Infra déjà en place. Ton compte Supabase existe. Tu te connectes avec ton email/password habituel. Fonctionne sur laptop et mobile sans rien installer.
Pour les services que tu déploies mais dont tu ne contrôles pas le code. Pas de code, mais nécessite de hasher le mot de passe au terminal.
Correction v4
Il n'existe pas de bouton "Basic Auth" dans Settings. Deux chemins selon ta version de Coolify : toggle natif dans Configuration → General → HTTP Basic Authentication (ajouté en beta.434, avril 2025) ou, pour les déploiements Docker Compose, labels Caddy dans Configuration → Advanced → Labels. Dans les deux cas, le mot de passe doit être hashé en bcrypt — pas de texte clair.
Qui
Qui est concerné ?
Toi seul. Le login/password est défini dans Coolify et mémorisé par le navigateur sur chaque appareil.
Quoi
C'est quoi concrètement ?
Avant même que la page se charge, le navigateur affiche une fenêtre système login/password. C'est Caddy qui bloque, pas l'app. Rien à coder dans l'app.
Où
Où configurer ?
Option 1 (UI native, beta.434+)
App → Configuration → General → HTTP Basic Authentication → activer + username + mot de passe hashé.
Option 2 (Docker Compose / toutes versions)
App → Configuration → Advanced → Labels → ajouter : caddy_0.basicauth.0_MON_USER=$$2a$$14$$hash...
(les $ du hash bcrypt doivent être doublés en $$ dans compose)
Quand
Cas d'usage ?
n8n → interface d'admin workflows Supabase Studio → interface BDD Tout outil open source déployé sans auth intégrée Coolify → sécurité supplémentaire si souhaité
Comment
L'expérience utilisateur ?
Fenêtre grise système du navigateur. Moche mais efficace. Le navigateur mémorise les credentials — tu ne le vois qu'une fois par appareil.
Pourquoi
Pourquoi pas pour les apps que tu codes ?
Pas de gestion de rôles, pas de sessions élégantes, pas de "Se souvenir de moi". Dès que tu maîtrises le code → Supabase Auth. Basic Auth = filet de sécurité rapide.
Générer le hash bcrypt (obligatoire)
Caddy refuse les mots de passe en clair. Commande à lancer une fois sur n'importe quelle machine où Caddy est installé (ou sur le VPS OVH) :
# Génère un hash bcrypt du mot de passe
caddy hash-password -p "ton_mot_de_passe"
# Sortie : $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx# → coller ce hash dans l'UI ou dans le label (en doublant les $ dans compose)
Piège docker-compose
Dans un fichier docker-compose, le signe $ est un caractère spécial. Le hash $2a$14$abc... doit être écrit $$2a$$14$$abc... dans les labels — sinon Docker tronque la valeur et Caddy refuse l'auth.
Méthode C — Tailscale Option avancée — plus tard
À ne pas activer maintenant pour ne pas complexifier la stack. Documenté ici pour référence future.
Tailscale est un VPN mesh qui fait disparaître une app d'Internet complètement. Seuls les appareils connectés à ton réseau Tailscale peuvent y accéder. Gratuit jusqu'à 100 appareils.
Cas d'usage futurs uniquement
N'utilise pas Tailscale pour n8n (besoin de webhooks entrants) ni pour Supabase (besoin d'accès client navigateur). Réservé à des outils d'admin que tu veux faire sortir d'Internet entièrement — si ce besoin émerge.
Récap — Quelle méthode pour quel service dans ta stack
Service
Méthode
Raison
bi.zoomali.io, saas.zoomali.io
Supabase Auth
Apps que tu codes, gestion des rôles utilisateurs
Ce guide HTML et outils perso
Supabase Auth
Ton compte Supabase existant, visible mais privé
n8n
Basic Auth Coolify
Doit rester public pour les webhooks entrants
Supabase Studio
Basic Auth Coolify
Service tiers, API doit rester publique
Coolify
Login natif Coolify
Sécurité renforcée dans les versions récentes — suffisant
Tailscale
Option future
Ne pas activer maintenant — complexité inutile à ce stade
Actuellement, seul saas.zoomali.io est autorisé comme redirect URI. Toutes tes autres apps échouent silencieusement à la redirection post-login.
Qui
Qui est impacté ?
Tout utilisateur qui tente de se connecter via une app autre que saas.zoomali.io.
Quoi
C'est quoi le problème ?
Supabase Auth refuse les redirections vers des URLs non listées dans GOTRUE_URI_ALLOW_LIST. L'utilisateur reste bloqué après login sans message d'erreur clair.
Où
Où modifier ?
Hostinger (décision validée) : variable d'env du conteneur gotrue. OVH : dans les Shared Variables Coolify dès le déploiement Supabase.
Quand
À quel moment ?
Sur Hostinger : maintenant (décision déjà validée). Sur OVH : au déploiement Supabase, avant de migrer la première app.
Comment
Quelle valeur cible ?
Wildcard https://*.zoomali.io/** + https://*.zoomali.fr/** couvre tous les sous-domaines actuels et futurs.
Pourquoi
Pourquoi c'est urgent ?
C'est probablement un bug existant en prod aujourd'hui. Et sur OVH, si tu l'oublies, aucune app ne pourra authentifier ses utilisateurs.
Procédure sur Hostinger
Redémarrer uniquement le conteneur gotrue, pas toute la stack Supabase. Tester le login sur saas.zoomali.io immédiatement après.
Le pendant du JWT_SECRET pour le chiffrement des données en base. Même logique : doit être identique entre Hostinger et OVH.
Qui
Qui est concerné ?
Toutes les données chiffrées via l'extension pgsodium dans PostgreSQL (Supabase Vault).
Quoi
C'est quoi le problème ?
Si la clé de chiffrement pgsodium diffère entre Hostinger et OVH, les données chiffrées importées depuis Hostinger sont illisibles sur OVH.
Où
Où récupérer la valeur ?
Sur Hostinger : variable SERVICE_PASSWORD_VAULTENC dans le fichier .env Supabase. Même transmission hors-repo que JWT_SECRET.
Quand
À quel moment ?
En même temps que le JWT_SECRET — les deux se récupèrent en même temps avant le déploiement Supabase OVH.
Comment
Comment s'en assurer ?
Après le déploiement Supabase OVH, faire un test de lecture sur une donnée chiffrée importée. Si elle est lisible : OK. Si elle retourne une erreur de déchiffrement : clé incorrecte.
Pourquoi
Pourquoi c'est critique ?
Les données corrompues par une mauvaise clé sont silencieusement illisibles. Pas d'erreur visible côté app — juste des champs vides ou des valeurs incorrectes.
À récupérer en même temps que JWT_SECRET
Ces deux variables voyagent ensemble. Crée une request dans vps-migration-bridge pour que Claude Hostinger les récupère simultanément et te les transmette hors-repo.
Tes 24 utilisateurs ont des UUID qui servent de clé étrangère dans toutes tes tables métier. Changer ces UUID = casser toutes les relations de données.
Qui
Qui est impacté ?
Tous les 24 utilisateurs, et toutes les tables métier qui référencent leur UUID (ex: bi_users.auth_id, bi_invoices.user_id).
Quoi
C'est quoi le problème ?
Si on recrée les utilisateurs via l'interface Supabase, de nouveaux UUID sont générés. Toutes les foreign keys existantes pointent vers des utilisateurs fantômes.
Où
Quelle table migrer ?
Tables dans l'ordre : auth.users → auth.identities → auth.refresh_tokens. Puis les tables métier du schema public.
Quand
Dans quel ordre ?
Auth en premier, avant de migrer les tables métier. Les UUID doivent exister côté OVH avant d'importer les données qui les référencent.
Comment
Comment procéder ?
Export pg_dump des tables auth.* depuis Hostinger. Import via psql sur OVH avec --data-only. Les hashes bcrypt sont préservés → mots de passe inchangés.
Pourquoi
Pourquoi les bcrypt ?
Les mots de passe sont stockés en hash bcrypt. Si on les préserve, les utilisateurs se connectent avec leur mot de passe actuel sans rien remarquer.
Bonne nouvelle
Tu n'as que 24 utilisateurs. Le dump/import de auth.* prend 2 minutes. Le risque est faible en volume, mais la procédure doit être respectée dans l'ordre.
n8n utilise SQLite, pas PostgreSQL. Un pg_dump ne fonctionnera pas. La migration se fait par copie du volume Docker complet.
Où
Quel fichier copier ?
Le volume Docker n8n sur Hostinger. Identifié par Claude Hostinger dans l'inventaire. Copie via scp ou rsync entre VPS.
Quand
À quel moment ?
Après nettoyage du binaryData (décision validée). Le conteneur n8n Hostinger doit être arrêté pendant la copie.
Comment
Procédure ?
1) Arrêter n8n Hostinger. 2) Copier le volume. 3) Démarrer n8n OVH avec ce volume. 4) Vérifier les workflows. 5) Arrêter n8n Hostinger définitivement.
Pourquoi
Pourquoi arrêter le conteneur ?
SQLite est un fichier. Si n8n écrit dessus pendant la copie, tu peux obtenir un fichier corrompu ou incomplet. L'arrêt est impératif.
Fenêtre d'indisponibilité
n8n sera indisponible pendant la copie du volume. Prévoir cette fenêtre à un moment creux. Durée estimée : 15-30 minutes selon la taille résiduelle après nettoyage binaryData.
Tes 4 bots Telegram configurés dans n8n sont en mode webhook. Après migration, ils pointent toujours vers l'ancienne URL n8n Hostinger.
Qui
Qui est impacté ?
Les 4 bots Telegram configurés dans n8n via des credentials. Tous leurs workflows qui reçoivent des messages s'arrêteront de répondre.
Quoi
Quel est le problème ?
Telegram envoie les messages à une URL webhook enregistrée. Cette URL contient le hostname n8n Hostinger. Elle doit pointer vers le nouveau hostname n8n OVH.
Où
Où faire la mise à jour ?
Via l'API Telegram directement : setWebhook avec la nouvelle URL. Ou via les credentials n8n si le workflow le permet.
Quand
À quel moment ?
Juste après la migration n8n et la vérification que n8n OVH répond correctement. Avant de couper n8n Hostinger.
Comment
Commande concrète ?
curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" -d "url=https://n8n.zoomali.io/webhook/<id>" — à répéter pour chacun des 4 bots.
Pourquoi
Pourquoi ce n'est pas automatique ?
Telegram ne sait pas que tu as déménagé. Il continue d'envoyer au webhook enregistré, indéfiniment, jusqu'à ce que tu le mettes à jour manuellement.
Si tes apps permettent la connexion via Google ou Microsoft, les redirect URIs OAuth doivent inclure les nouveaux domaines OVH.
Qui
Qui est impacté ?
Tout utilisateur qui utilise "Se connecter avec Google" ou "Se connecter avec Microsoft" dans tes apps.
Quoi
Quel est le problème ?
Google et Microsoft ont une liste blanche des URLs autorisées après login OAuth. Si le nouveau domaine n'est pas dans la liste, la connexion échoue avec une erreur redirect_uri_mismatch.
Avant le cutover DNS de chaque app concernée. Ajouter les nouvelles URLs sans supprimer les anciennes pendant la transition.
Comment
Procédure ?
Ajouter les nouvelles URLs OVH dans les consoles Google/Microsoft. Garder les URLs Hostinger actives pendant toute la période de transition. Supprimer les anciennes seulement après décommissionnement Hostinger.
Pourquoi
Pourquoi avant le cutover ?
La propagation DNS peut prendre quelques minutes. Pendant ce temps, certains utilisateurs arrivent sur l'ancien serveur, d'autres sur le nouveau. Les deux doivent fonctionner simultanément.
Migrer les apps une par une — cutover DNS progressifPour chaque app : ajouter redirect URI OAuth si besoin → déployer OVH → tester → baisser TTL → couper DNS. → S-07 / C-06
Décommissionner HostingerSeulement quand 0 trafic depuis plusieurs jours et tous les backups S3 confirmés.
Le principe directeur
Un changement à la fois. Si quelque chose casse, tu sais immédiatement laquelle des étapes est en cause — et tu peux reculer sans impacter le reste.