Déployer sur un PaaS (image GHCR)
Le guide Coolify / VPS construit l'image Docker sur le serveur. Pour une cible PaaS (DigitalOcean App Platform, Heroku…) — ou pour alléger un VPS — TimePick suit le modèle « image unique tirée » : une image canonique construite et testée en CI, publiée sur GHCR, puis tirée (pull) par tous les hôtes. Cette fiche décrit ce modèle, ses contraintes, et renvoie aux kits de déploiement fournis dans le dépôt.
À lire d'abord : ce qui est prouvé et ce qui ne l'est pas
L'image canonique est publiée et publique sur GHCR depuis la version v0.29.0 (tirage anonyme vérifié, sans identifiants). En revanche, aucun déploiement PaaS réel n'a encore été exécuté avec ces kits — le détail honnête de chaque point est signalé dans les sections concernées.
Le modèle « image unique tirée »
Le workflow CI .github/workflows/docker.yml :
- se déclenche au push d'un tag de version
v*(ou manuellement) ; - construit la variante
amd64, la démarre contre un PostgreSQL de service et attend queGET /healthréponde200(migrations comprises) ; - seulement si ce test passe, construit et publie l'image multi-architecture (
linux/amd64+linux/arm64) surghcr.io/timepick-app/timepick, avec les tagslatest(versions finales uniquement),<tag git>(ex.v0.29.0) etsha-<court>.
Une seule image sert ainsi toutes les cibles : amd64 pour les PaaS, arm64 pour un VPS ARM (le chemin Coolify actuel, en mode pull). Aucun tag latest ou vX.Y cassé ne peut être publié à une flotte qui tire en pull, puisque la publication est conditionnée au test de démarrage.
Image publiée et publique depuis v0.29.0
Depuis la version v0.29.0, l'image est publiée sur GHCR et le paquet est public : docker pull ghcr.io/timepick-app/timepick:<tag> fonctionne en anonyme, sans docker login. Toujours épingler un tag versionné (vX.Y.Z) — jamais latest en production. Le repli « build depuis les sources » documenté dans chaque kit (Heroku heroku.yml, DigitalOcean « build from source ») reste disponible, mais n'est plus nécessaire pour démarrer.
VITE_API_URL déjà réglée — ne pas y toucher
L'image canonique est construite avec VITE_API_URL=/api (chemin relatif) : le frontend appelle l'API en same-origin, quel que soit le domaine. C'est ce qui rend une image unique portable d'un hôte à l'autre. Conséquence : ne jamais définir VITE_API_URL côté hébergeur — c'est une variable de build, déjà figée dans l'image (voir Variables d'environnement).
Contraintes de déploiement (toutes plateformes)
| Contrainte | Détail |
|---|---|
| 1 seule instance (scale = 1) | Les limitations de débit (voir Sécurité) sont maintenues en mémoire, par processus : à N instances, chaque quota est multiplié par N et la protection anti-abus se dilue. Ne pas activer d'autoscaling horizontal. |
| Chevauchement de redéploiement : OK | Les migrations exécutées au démarrage sont sérialisées par un verrou PostgreSQL (advisory lock) : deux conteneurs peuvent se chevaucher pendant un redéploiement sans corruption. C'est le régime permanent à plusieurs instances qui est exclu, pas le redeploy sans coupure. |
Healthcheck HTTP sur GET /health | Répond 200 (JSON) même quand l'email est degraded — sonde de disponibilité fiable. ⚠️ DigitalOcean App Platform sonde par défaut le port 8080 en TCP : déclarer http_port: 3000 et health_check.http_path: /health (fait dans le kit). |
| PostgreSQL managé ≥ 16 | Version minimale supportée par le projet (voir Prérequis). Les offres managées Heroku Postgres et DO Managed PostgreSQL conviennent. |
| Système de fichiers éphémère → S3 + secrets en env | Heroku et DO App Platform n'offrent pas de volume persistant : STORAGE_DRIVER=s3 est obligatoire (sinon les uploads sont perdus à chaque redéploiement) et JWT_SECRET + ENCRYPTION_KEY doivent être des variables d'environnement, posées avant le premier démarrage. Migration d'une installation existante : extraire d'abord les valeurs en place — voir la procédure disque → environnement. |
| Fenêtre de configuration initiale | Dès le premier démarrage, les routes /api/setup/* sont publiques tant qu'aucun administrateur n'existe : dérouler l'assistant immédiatement après le déploiement (créer et vérifier le premier admin par lien de connexion) — voir Configuration initiale. |
Variables d'environnement minimales
Sémantique détaillée dans Variables d'environnement. Sur un PaaS à système de fichiers éphémère, les blocs « secrets » et « S3 » ne sont plus optionnels :
| Variable | Rôle | Requise sur PaaS |
|---|---|---|
DATABASE_URL | PostgreSQL managé ≥ 16 | Oui |
JWT_SECRET | Signature des sessions et des liens de connexion (magic links) | Oui (FS éphémère) |
ENCRYPTION_KEY | 64 caractères hexadécimaux ; chiffre le mot de passe SMTP (et la clé API email) en base | Oui (FS éphémère — installation existante : extraire l'ancienne clé d'abord) |
APP_URL | Base HTTPS publique des liens envoyés par email | Oui |
NODE_ENV | production | Oui |
PORT | Port d'écoute (défaut 3000) — Heroku l'injecte automatiquement, DigitalOcean route via http_port | Selon plateforme |
SMTP_* (7 variables) | Provisionnement initial du serveur d'email (premier démarrage uniquement) | Recommandé (sinon saisie dans l'assistant) |
EMAIL_PROVIDER, EMAIL_API_CREDENTIALS | Fournisseur d'email HTTP (brevo|mailjet|scaleway|sweego|resend) — uniquement si l'hôte bloque le SMTP sortant ; EMAIL_API_KEY reste acceptée en alias déprécié | Non (défaut smtp) |
STORAGE_DRIVER | s3 sur FS éphémère (défaut local) | Oui (=s3) |
S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | Object storage S3-compatible (démarrage refusé si incomplet) | Oui si STORAGE_DRIVER=s3 |
S3_REGION, S3_PUBLIC_BASE_URL | Optionnelles — défauts auto / <endpoint>/<bucket> (Cloudflare R2 : S3_PUBLIC_BASE_URL obligatoire) | Optionnel |
VITE_API_URL | Variable de build, déjà figée à /api dans l'image publiée | Ne pas définir |
Kits par cible (dossier deploy/ du dépôt)
Chaque kit fournit les fichiers de configuration prêts à l'emploi et les étapes manuelles détaillées. Aucun de ces kits n'a encore été exécuté contre un compte réel — ils sont livrés « prêts à lancer », avec leur protocole de vérification.
| Cible | Kit | Note |
|---|---|---|
| DigitalOcean App Platform | deploy/digitalocean/ | App Spec complète (app.yaml) : image GHCR, http_port: 3000, healthcheck HTTP, 1 instance, PostgreSQL managé, STORAGE_DRIVER=s3. ⚠️ Voir l'avertissement SMTP ci-dessous. |
| Heroku | deploy/heroku/ | Deux voies : build via heroku.yml (avec VITE_API_URL=/api en build-arg), ou pull GHCR re-poussé vers le registre Heroku. Dynos à FS éphémère → S3 + secrets en env obligatoires. |
| Coolify en mode pull | deploy/coolify-pull/ | Bascule d'un Coolify « build local » vers l'image GHCR (arm64) pour alléger le VPS. Prérequis avant bascule : secrets en variables d'environnement et uploads hors volume (une nouvelle ressource Coolify monte des volumes neufs et vides). Le build local reste intact en repli. |
DigitalOcean App Platform : SMTP sortant NON VÉRIFIÉ
La politique de DigitalOcean App Platform sur le trafic SMTP sortant (ports 587/465) ne repose, à la date de rédaction, que sur des réponses de forum — elle n'a pas été vérifiée en conditions réelles. Ne pas considérer l'envoi d'emails comme acquis sur cette plateforme tant qu'un lien de connexion (magic link) n'a pas été réellement reçu depuis une application déployée (le protocole de preuve pas à pas est dans le README du kit ; le bouton « Tester la connexion » ne constitue pas une preuve). Si le verdict est négatif, il n'existe pas de repli sur un port SMTP alternatif sur App Platform : la solution est le mode Envoi par API (HTTP), avec des fournisseurs européens en tête de liste.
Rappel : rien ne change pour les installations existantes
Ce modèle est une option supplémentaire. Les défauts de TimePick restent le SMTP et le stockage local des uploads : une installation Coolify/VPS existante (build local, volumes persistants) continue de fonctionner à l'identique, sans aucune action requise.
Pour aller plus loin
- Installation en production (Docker) — kit Docker Compose (app + PostgreSQL), build depuis le
Dockerfile, volumes, healthcheck. - Déploiement Coolify / VPS — le chemin de référence sur VPS.
- Variables d'environnement — référence complète, dont stockage objet et migration des secrets.
- Fournisseurs SMTP — Envoi par API (HTTP) — l'alternative HTTP quand le SMTP sortant est bloqué, fournisseurs européens en tête.