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 :
| Type | Nom | Contenu |
|---|---|---|
| A | timepick | <IP_DU_SERVEUR> |
Ce qui donne timepick.votre-domaine.tld → <IP_DU_SERVEUR>.
Vérifiez la propagation avant de continuer :
dig timepick.votre-domaine.tld +shortLe 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.
- Créez un projet (ex. « TimePick »).
- Dans ce projet → + Add Resource → Database → PostgreSQL → choisissez PostgreSQL 17.
- Renseignez :
- Name :
timepick-db - Initial Database :
timepick(sinon la base créée s'appellepostgrespar défaut)
- Name :
- 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.
- Barre latérale → Sources → + Add → GitHub App.
- 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).
- 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 :
- + Add Resource → Application.
- Choisissez la source GitHub configurée à l'étape précédente → sélectionnez le dépôt TimePick → branche
main. - Build Pack : Dockerfile — Coolify détecte automatiquement le
Dockerfileà la racine du dépôt (détail du build : Installation en production (Docker)). - 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.tldCoolify 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.
| Variable | Rôle | Exemple |
|---|---|---|
VITE_API_URL | URL de l'API vue par le navigateur (injectée au build) | https://timepick.votre-domaine.tld/api |
NODE_ENV | Mode d'exécution | production |
PORT | Port interne écouté par le conteneur | 3000 |
DATABASE_URL | URL interne de la base créée à l'étape 2 | postgres://postgres:...@timepick-db-xxxx:5432/timepick |
JWT_SECRET | Secret de signature des sessions | valeur générée (voir ci-dessous) |
ENCRYPTION_KEY | Clé de chiffrement (identifiants SMTP en base) | valeur générée (voir ci-dessous) |
APP_URL | URL publique de l'application | https://timepick.votre-domaine.tld |
EMAIL_FROM | Adresse d'expédition des emails | noreply@votre-domaine.tld |
SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASSWORD, SMTP_FROM_NAME, SMTP_FROM_EMAIL | Identifiants du fournisseur d'envoi d'emails | voir Étape 7 |
Générez les deux secrets avec openssl, chacun dans son propre appel :
openssl rand -hex 32openssl rand -hex 32La 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_URLdoit avoir la case Available at Buildtime cochée — Vite en a besoin au moment du build, pas seulement à l'exécution.NODE_ENVdoit 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_USERetSMTP_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 :
- + Add → Volume Mount.
- Name :
timepick-uploads, Destination Path :/app/server/uploads(laisser Source Path vide). - Add.
Étape 9 — Healthcheck
Dans l'onglet Healthcheck, vérifiez les valeurs par défaut :
- Method
GET· Path/health· Port3000· Return Code200
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 :
curl -I https://timepick.votre-domaine.tldcurl https://timepick.votre-domaine.tld/healthLe 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, puispsql -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
- Variables d'environnement — référence complète.
- Configuration SMTP par fournisseur — Gmail, Brevo, OVH en détail.
- Configuration initiale (première utilisation) — assistant de configuration.
- Mise à jour — redéploiements suivants.
- Sauvegarde et restauration — protéger les données en production.