Skip to content

Déploiement sur un VPS avec Coolify

Ce guide déploie TimePick en production sur un VPS piloté par Coolify, une plateforme d'auto-hébergement open source. Un seul conteneur Node sert à la fois l'application React (fichiers statiques) et l'API (/api), les fichiers uploadés (/uploads) et le contrôle de santé (/health) — tout passe par le même domaine, sans configuration CORS à gérer. Une seule base PostgreSQL, gérée par Coolify à côté de l'application.

Cette fiche couvre la mise en ligne complète, du VPS nu à l'application accessible en HTTPS. Le détail du Dockerfile et du processus de build est décrit dans Installation en production (Docker) ; le détail de chaque variable d'environnement dans Variables d'environnement.

TIP

Coolify n'est pas obligatoire : le même Dockerfile fonctionne sur n'importe quelle plateforme capable de builder une image et d'exposer un port (Railway, Render, un VPS nu avec docker run…). Ce guide se concentre sur Coolify parce qu'il offre une interface graphique complète (base de données, variables, volumes, HTTPS automatique) sans frais de plateforme.

Prérequis

  • Un VPS avec Coolify installé et son proxy actif (Coolify s'installe sur n'importe quel VPS via son script officiel — voir la documentation Coolify pour cette étape préalable, hors périmètre de ce guide).
  • Un nom de domaine dont vous gérez la zone DNS (votre-domaine.tld).
  • L'adresse IP publique du VPS (<IP_DU_SERVEUR>).
  • Un dépôt GitHub contenant le code de TimePick.

Étape 1 — DNS

Chez votre fournisseur DNS, ajoutez un enregistrement de type A qui pointe le sous-domaine choisi vers le VPS :

TypeNomContenu
Atimepick<IP_DU_SERVEUR>

Ce qui donne timepick.votre-domaine.tld<IP_DU_SERVEUR>.

Vérifiez la propagation avant de continuer :

bash
dig timepick.votre-domaine.tld +short

Le résultat doit afficher l'IP du VPS.

WARNING

Si le proxy de votre fournisseur DNS (type Cloudflare) est activé sur cet enregistrement, désactivez-le temporairement (mode « DNS uniquement »). Coolify a besoin d'atteindre le VPS directement sur le port 80 pour émettre le certificat HTTPS via Let's Encrypt. Vous pourrez réactiver le proxy une fois le certificat émis — voir la section Durcissement post-déploiement.

Étape 2 — Créer la base de données PostgreSQL

Dans Coolify, toutes les ressources d'une application vivent dans un projet.

  1. Créez un projet (ex. « TimePick »).
  2. Dans ce projet → + Add ResourceDatabasePostgreSQL → choisissez PostgreSQL 17.
  3. Renseignez :
    • Name : timepick-db
    • Initial Database : timepick (sinon la base créée s'appelle postgres par défaut)
  4. Deploy — attendez que le statut passe à Running.

INFO

TimePick exige PostgreSQL 16 au minimum ; choisissez 17, la version utilisée en production (voir Prérequis).

Une fois la base démarrée, récupérez son URL interne (Configuration → section Network → « Postgres URL (internal) ») — elle sera nécessaire à l'étape 6. Son format ressemble à :

postgres://postgres:<mot_de_passe>@timepick-db-<id>:5432/timepick

Étape 3 — Connecter le dépôt GitHub

Coolify a besoin d'un accès au dépôt pour builder l'image et déclencher les redéploiements automatiques sur git push.

  1. Barre latérale → Sources+ AddGitHub App.
  2. Suivez l'assistant Coolify : il crée une GitHub App, redirige vers GitHub pour la validation, puis demande d'installer cette App sur le dépôt TimePick (choisir Only select repositories).
  3. Retour dans Coolify — la source est configurée et réutilisable pour d'autres projets.

Étape 4 — Créer l'application

Dans le même projet :

  1. + Add ResourceApplication.
  2. Choisissez la source GitHub configurée à l'étape précédente → sélectionnez le dépôt TimePick → branche main.
  3. Build Pack : Dockerfile — Coolify détecte automatiquement le Dockerfile à la racine du dépôt (détail du build : Installation en production (Docker)).
  4. Vérifiez que Ports Exposes est bien réglé sur 3000.

Étape 5 — Domaine de l'application

Dans l'onglet General de l'application, section Domains, remplacez l'URL générée automatiquement par celle configurée à l'étape 1 :

https://timepick.votre-domaine.tld

Coolify provisionne automatiquement le certificat TLS via Let's Encrypt dès que le DNS pointe correctement et que les ports 80/443 sont ouverts.

Étape 6 — Variables d'environnement

Dans l'onglet Environment Variables de l'application, renseignez les variables suivantes. Le rôle et les valeurs par défaut détaillés se trouvent dans Variables d'environnement — seuls les points spécifiques à un déploiement Coolify sont repris ici.

VariableRôleExemple
VITE_API_URLURL de l'API vue par le navigateur (injectée au build)https://timepick.votre-domaine.tld/api
NODE_ENVMode d'exécutionproduction
PORTPort interne écouté par le conteneur3000
DATABASE_URLURL interne de la base créée à l'étape 2postgres://postgres:...@timepick-db-xxxx:5432/timepick
JWT_SECRETSecret de signature des sessionsvaleur générée (voir ci-dessous)
ENCRYPTION_KEYClé de chiffrement (identifiants SMTP en base)valeur générée (voir ci-dessous)
APP_URLURL publique de l'applicationhttps://timepick.votre-domaine.tld
EMAIL_FROMAdresse d'expédition des emailsnoreply@votre-domaine.tld
SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASSWORD, SMTP_FROM_NAME, SMTP_FROM_EMAILIdentifiants du fournisseur d'envoi d'emailsvoir Étape 7

Générez les deux secrets avec openssl, chacun dans son propre appel :

bash
openssl rand -hex 32
bash
openssl rand -hex 32

La première valeur va dans JWT_SECRET, la seconde (différente) dans ENCRYPTION_KEY.

Variables build-time vs runtime

Deux réglages sont indispensables sur Coolify, sans quoi le déploiement échoue ou l'application appelle la mauvaise API :

  • VITE_API_URL doit avoir la case Available at Buildtime cochée — Vite en a besoin au moment du build, pas seulement à l'exécution.
  • NODE_ENV doit avoir cette même case décochée (garder uniquement Available at Runtime) — sinon le build Docker saute les dépendances de développement nécessaires à la compilation et échoue.

Étape 7 — Fournisseurs SMTP

Les variables SMTP_* ne servent qu'au premier démarrage : si elles sont présentes, TimePick les copie une fois en base au démarrage pour pré-remplir l'assistant de configuration initiale. Ensuite, la configuration SMTP se gère exclusivement depuis l'application (Paramètres → Serveur d'email) — modifier ces variables dans Coolify après le premier démarrage n'a plus d'effet. Le détail par fournisseur (dont Gmail) est dans Configuration SMTP par fournisseur.

Deux repères rapides pour cette étape :

  • Brevo — compte gratuit sur brevo.com, section SMTP & API du dashboard :
    • SMTP_HOST=smtp-relay.brevo.com, SMTP_PORT=587, SMTP_SECURE=false (STARTTLS)
    • SMTP_USER = adresse email du compte Brevo, SMTP_PASSWORD = clé SMTP générée (pas le mot de passe du compte)
  • OVH — boîte email existante chez OVH :
    • SMTP_HOST=ssl0.ovh.net, SMTP_PORT=465, SMTP_SECURE=true (SSL)
    • SMTP_USER et SMTP_FROM_EMAIL = adresse email complète, SMTP_PASSWORD = mot de passe de cette boîte

À confirmer

Ces deux jeux de paramètres sont des repères fournis par les fournisseurs eux-mêmes, pas des valeurs vérifiées de bout en bout avec un compte réel sur l'instance de référence de cette documentation. Testez systématiquement l'envoi (bouton « Tester la connexion » de l'assistant de configuration, voir étape 11) avant de considérer le déploiement fonctionnel.

Étape 8 — Volume persistant

Les images insérées dans les emails sont stockées dans /app/server/uploads. Sans volume, elles disparaissent à chaque redéploiement.

Dans l'onglet Persistent Storage (section Storages) de l'application :

  1. + AddVolume Mount.
  2. Name : timepick-uploads, Destination Path : /app/server/uploads (laisser Source Path vide).
  3. Add.

Étape 9 — Healthcheck

Dans l'onglet Healthcheck, vérifiez les valeurs par défaut :

  • Method GET · Path /health · Port 3000 · Return Code 200

Cliquez Save puis Enable Healthcheck (les deux boutons sont distincts — sans le second, Coolify ignore le contrôle de santé même si les valeurs sont enregistrées).

Étape 10 — Premier déploiement

Cliquez Deploy et suivez les logs. Au démarrage, le conteneur exécute automatiquement la préparation de la base (création de la table users si absente) puis les migrations, avant d'accepter des connexions — aucune commande manuelle n'est nécessaire pour ça.

Une fois le statut Running affiché, vérifiez :

bash
curl -I https://timepick.votre-domaine.tld
bash
curl https://timepick.votre-domaine.tld/health

Le healthcheck doit répondre avec un code HTTP 200.

Étape 11 — Premier démarrage : configuration initiale

Fenêtre de sécurité

Tant qu'aucun administrateur n'existe en base, les routes de configuration initiale (/api/setup/*) sont publiques — accessibles sans authentification, protégées seulement par un rate-limit par IP. Un tiers capable de joindre le serveur pendant cette fenêtre pourrait configurer le SMTP ou déclencher la création du premier administrateur à sa place.

Enchaînez immédiatement avec la création du premier administrateur après ce premier déploiement — voir Configuration initiale (première utilisation). Une fois un administrateur créé, ces routes redeviennent inaccessibles (404).

Durcissement post-déploiement

Une fois l'application accessible en HTTPS sans erreur :

  • Proxy / CDN — si vous utilisiez un fournisseur type Cloudflare en mode « DNS uniquement » pour l'émission du certificat (étape 1), réactivez le proxy (mode Full ou Full strict). Le trafic passe alors par le CDN avant d'atteindre le VPS, ce qui masque l'IP d'origine.
  • Pare-feu — ne laissez ouverts que les ports strictement nécessaires (22 pour SSH, 80/443 pour le web, plus les ports d'administration Coolify si vous y accédez encore depuis l'extérieur). Refermez le reste dans le groupe de sécurité de votre fournisseur VPS.
  • HTTPS — vérifiez qu'aucune ressource ne charge encore en HTTP simple et que le certificat se renouvelle automatiquement (Coolify s'en charge via Let's Encrypt).

Accès à la base de données en production

Trois options, du plus rapide au plus confortable :

  • Terminal intégré Coolify — onglet Terminal de la ressource timepick-db, puis psql -U postgres -d timepick. Pratique pour une vérification ponctuelle, sans configuration.
  • Port temporaire — rendre la base brièvement accessible depuis un client GUI local (TablePlus, DBeaver…) via un port public, à refermer immédiatement après usage.
  • pgAdmin — interface web permanente déployée comme service Coolify dans le même projet, accès à la base via le réseau interne Docker (aucun port public requis).

Le détail des sauvegardes régulières et de la restauration est couvert dans Sauvegarde et restauration.

Pour aller plus loin

Publié sous licence FSL-1.1-MIT.