Configuration de l'environnement de déploiement
Collecter, borner et vérifier les variables d'environnement nécessaires au déploiement.
Cette page est le guide opérateur des variables d'environnement de déploiement. Utilisez-la lors de la restauration d'un environnement production, de la vérification d'une prévisualisation Coolify avec le même câblage fournisseur que la production, ou de la préparation du runtime Docker portable.
Règles
Ne commitez jamais de secrets dans
.env,.env.productionou un fichier suivi.Stockez les secrets production et preview dans le magasin de secrets Coolify/de l'orchestrateur. Pour la base Compose, utilisez un fichier non suivi
.env.self-hostcopié depuis.env.self-host.example.Stockez les secrets locaux dans un
.env.localnon suivi, alimenté depuis la source de secrets de confiance.Gardez les valeurs
NEXT_PUBLIC_*non secrètes: elles sont exposées au bundle navigateur.Préférez les réglages admin/runtime pour les non-secrets opérationnels lorsque le dashboard les supporte. Par exemple, les réglages dépôt GitHub pour l'accès au code se gèrent depuis
/dashboard/admin/billing-settings; les variables d'environnement ne sont que des replis.
Cible De Déploiement
La production et la prévisualisation Yayaw sont auto-hébergées via
Docker/Coolify. Vercel n'est pas une cible de déploiement. N'exécutez pas la CLI
Vercel, ne liez pas ce checkout, n'ajoutez pas de workflow de déploiement Vercel
et ne créez pas de dossier local .vercel. Le vercel.json suivi est conservé
uniquement pour maintenir git.deploymentEnabled: false pendant la suppression
de toute ancienne intégration Git.
Utilisez les environnements GitHub development et production pour les
entrées de build/déploiement d'images, et le magasin de secrets
Coolify/orchestrateur pour les secrets runtime. Copiez les valeurs nécessaires
au développement dans .env.local uniquement sur une machine de confiance.
Vérifiez que chaque clé listée dans .env.example est présente localement:
while IFS='=' read -r key _; do
[[ -z "$key" || "$key" == \#* ]] && continue
grep -q "^${key}=" .env.local || echo "Missing in .env.local: $key"
done < .env.exampleWorkflow Docker Auto-Hébergé
La base portable utilise Docker Compose avec Postgres, MinIO/S3, le serveur app Next.js standalone, un worker Page AI et Caddy:
cp .env.self-host.example .env.self-host
docker compose --env-file .env.self-host -f docker-compose.self-host.yml --profile setup run --rm migrate
docker compose --env-file .env.self-host -f docker-compose.self-host.yml up --buildLes builds Docker utilisent NEXT_BUILD_WORKERS=1 par défaut pour une
génération statique Next.js stable sur de petits hôtes. Sur une machine plus
large, vous pouvez l'augmenter, par exemple:
NEXT_BUILD_WORKERS=2 docker compose --env-file .env.self-host -f docker-compose.self-host.yml up --buildLes builds Docker auto-hébergés fixent aussi
NEXT_STATIC_PAGE_GENERATION_TIMEOUT=180 par défaut. Cela donne aux hôtes lents
assez de temps pour pré-rendre de grosses pages docs sans déclencher le timeout
Next.js de 60 secondes. Baissez-le sur des builders CI rapides ou augmentez-le
uniquement si les logs montrent des pages statiques légitimes en timeout.
Les builds Docker auto-hébergés rendent les pages Fumadocs dynamiquement au lieu de prégénérer toute la documentation pendant la construction d'image. Cela évite aux petits hôtes Coolify ou Docker de dépenser l'essentiel du budget de déploiement sur les docs tout en les servant normalement au runtime.
Les fichiers Compose taguent les cibles app et worker avec YAYAW_APP_IMAGE et
YAYAW_WORKER_IMAGE. Gardez le même tag worker pour les services migrate et
worker afin que Docker ou Coolify puisse réutiliser la cible worker au lieu de
la construire deux fois.
Le déploiement Coolify managé par Yayaw utilise des images précompilées plutôt
qu'un build sur la VM production. GitHub Actions construit les images app et
worker sur des runners ubuntu-24.04-arm pour la plateforme linux/arm64 du
Mac mini, pousse les images vers GHCR, les fait tirer par le serveur Coolify via
le runner auto-hébergé privé, met à jour YAYAW_APP_IMAGE et
YAYAW_WORKER_IMAGE, puis demande à Coolify de lancer le déploiement Compose.
Cela conserve la release automatique sur main, déplace le build coûteux hors
de la VM Mac mini et évite le quota de stockage artifacts GitHub Actions.
Coolify doit stocker ces tags d'image comme valeurs build-time et runtime,
parce que Docker Compose interpole les entrées image: pendant la préparation
du déploiement.
Le profil setup initialise le journal, applique les migrations Drizzle
versionnées, puis lance le seed et
bun run dynamic-data:repair-deployed-storage. Relancez-le avant le démarrage
d'une base auto-hébergée fraîche et après les changements de schéma. Le setup ne
pousse jamais le schéma. Une base sans aucune table publique reçoit, dans une
seule transaction, la fonction préalable de validation des receipts, le schéma
figé de la migration 0050 (src/lib/db/baseline/0050_better_auth_1_7_stable.sql),
l'historique des migrations de 0000 à 0050, puis chaque migration suivante ;
toute erreur sort avec un code non nul et laisse la base vide. La migration 0051
recrée et vérifie l'ensemble des fonctions et triggers propres aux migrations
après réconciliation de la provenance de publication, des têtes de révision et
du singleton runtime-floor. Une ancienne base gérée par push n'est acceptée que
si toute sa forme table/colonne pré-0050 correspond à cette baseline, si elle ne
contient aucune table que seule une migration ultérieure crée, si le préflight
Better Auth passe et si l'attestation de suspension est active ; le setup
enregistre 0049 avant d'appliquer 0050, la même réconciliation 0051 et chaque
migration suivante. Le garde relance le
préflight sur une base déjà migrée
pour détecter les comptes créés par un rollback beta. Pour une base existante
dont 0050 est en attente, il exige BETTER_AUTH_17_WRITES_PAUSED=true avant
db:migrate. Le wrapper Compose résout aussi le contrat /api/ready
actuellement servi via son BETTER_AUTH_RUNTIME_PROBE_URL privé : seule une
preuve exacte stable-1.7 évite une nouvelle suspension sur une base déjà
migrée ; un runtime indisponible, beta ou inconnu exige de nouveau
l'attestation. Une valeur de contrat statique n'est pas considérée comme une
preuve. Le bootstrap d'une base sans aucune table est la seule exception
interne. Cette variable est une attestation opérateur, pas un verrou
runtime : sauvegardez et vérifiez d'abord la base, bloquez les écritures
/api/auth à l'ingress ou en arrêtant l'ancienne app, puis retirez immédiatement
l'attestation après la bascule. L'étape dynamic-data couvre les tables natives
MCP ydm_* qui ne font volontairement pas partie du schéma Drizzle statique.
Ne commitez pas .env.self-host. En production, déplacez les mêmes valeurs dans
le secret manager de l'hôte cible ou l'environnement Compose. Générez
BETTER_AUTH_SECRET avant le premier build Docker, par exemple avec
openssl rand -hex 32; le Dockerfile échoue immédiatement lorsqu'il est vide.
Le flag --env-file rend les mêmes valeurs publiques disponibles aux arguments
de build Docker. BETTER_AUTH_SECRET est aussi passé à next build pour éviter
que l'évaluation des routes Better Auth ne retombe sur un secret de
développement pendant la construction d'image. Reconstruisez l'image quand les
valeurs NEXT_PUBLIC_* changent.
Migration Rolling Coolify
docker-compose.coolify.yml reste l'unité de déploiement historique pendant la
validation du chemin rolling. Il remplace ensemble migrate, l'app, le worker et
Caddy; il ne peut donc pas fournir une release sans interruption. Ne repointez pas
les workflows automatiques development ou production vers les nouvelles
ressources avant que le workflow rolling manuel ait validé le rollback.
Migrez d'abord development:
Sauvegardez Postgres et MinIO. Conservez l'UUID de la ressource Compose Coolify existante afin de réutiliser ses volumes
postgres_dataetminio_data.Activez Connect to Predefined Network sur la ressource d'infrastructure existante. Notez les hostnames résolus
postgres-<resource-uuid>etminio-<resource-uuid>.Créez une Application Coolify Docker Image depuis l'image immuable
ghcr.io/<owner>/yayaw-app:<sha>. Exposez le port conteneur3000, sans mapping de port hôte, sans nom de conteneur, et connectez-la au réseau prédéfini. Configurez les identifiants du registre GHCR dans Coolify si le package est privé; les identifiants de pull temporaires du workflow ne configurent pas le registre de l'Application.Copiez l'environnement runtime de l'app vers cette Application. Remplacez les hôtes de
DATABASE_URLetS3_ENDPOINTpar ceux de l'infrastructure. Utilisez d'abord un domaine development temporaire.Gardez les healthchecks actifs. L'image Docker vérifie
/api/ready, qui ne renvoie200que si Postgres répond et si le SHA immuable de l'image correspond au SHA demandé./api/healthreste limité à la liveness.Configurez une période de grâce d'arrêt de 20 à 30 secondes dans Coolify. Next.js traite
SIGTERMet termine les requêtes en vol pendant cette fenêtre.Déplacez le worker Page AI vers une ressource séparée basée sur
ghcr.io/<owner>/yayaw-worker:<sha>. Il ne doit pas rester dans la stack d'infrastructure qui survit aux releases web.Sur un clone de la base, vérifiez que
drizzle.__drizzle_migrationsreprésente fidèlement les migrations SQL déjà appliquées. Le runner rolling refuse de migrer une base non vide dont le journal est absent ou vide. Ne fabriquez pas de baseline directement en production.Ajoutez
COOLIFY_ROLLING_APPLICATION_UUIDetNEXT_SERVER_ACTIONS_ENCRYPTION_KEYà l'environnement GitHubdevelopment. Générez la seconde valeur une fois avecopenssl rand -base64 32et conservez la même valeur entre les builds.Lancez
Coolify Rolling Deploypourdevelopment. Il tire les deux images avec retry, exécute le garde de bascule Better Auth, les migrations Drizzle versionnées, la vérification du schéma, le seed et la réparation dynamic-data, puis met à jour l'Application Docker Image. Pour l'unique bascule Better Auth 1.7 d'une base existante, ne laissez pas l'ancienne app accepter des écritures auth : bloquez d'abord toutes les écritures/api/authen externe, puis définissez la variable GitHub non secrèteBETTER_AUTH_17_WRITES_PAUSED=true. Retirez-la après la migration. Un échec de déploiement ou du smoke public sur le SHA restaure automatiquement le tag précédent; les changements de base ne sont pas annulés.Après le succès du rollout web, promouvez la ressource worker séparée vers le tag immuable
yayaw-worker:<sha>émis par le workflow. Gardez l'ancien worker actif jusqu'à ce que le processus de remplacement soit sain; toutes les évolutions du worker doivent rester compatibles avec le schéma migré pendant cette bascule.Validez volontairement une mauvaise image ou un échec de readiness et confirmez que l'ancien SHA public reste disponible. Répétez ensuite la procédure en production.
Une fois la nouvelle app et le worker sains, changez la ressource Compose
existante vers docker-compose.coolify.infrastructure.yml. Définissez
MINIO_MEDIA_FQDN sur l'origine publique suivie de /media et
MINIO_ORGANIZATION_LOGOS_FQDN sur la même origine suivie de
/organization-logos; Coolify crée les routeurs de chemin stables vers MinIO.
Retirez le domaine de l'ancien Caddy et assignez le domaine racine à l'Application
Docker Image pendant la même bascule. Le fichier d'infrastructure n'expose aucun
port hôte et ne doit pas être redéployé lors des releases applicatives normales.
Chaque migration de ce chemin doit suivre expand/contract: ajoutez d'abord un schéma compatible, déployez du code tolérant les deux versions, puis supprimez l'ancien schéma seulement après la disparition de tous les anciens conteneurs et workers. Le rollback automatique d'image ne peut pas annuler une migration de base destructive.
Environnements CI Coolify
Les déploiements Coolify auto-hébergés sont pilotés depuis GitHub Actions:
les pull requests déploient vers une application Coolify
developmentpartagée après réussite de la CIles pushes vers
mainévaluent la release production après réussite de la CI. Une flotte reader/freeze est déployée automatiquement ; une flotte exact produit un résumé de promotion requise sans modifier la production
Configurez deux environnements GitHub avec des UUID d'applications Coolify séparés:
| Environnement GitHub | Secret ou variable requis | Usage |
|---|---|---|
development | COOLIFY_URL | Origine API Coolify. |
development | COOLIFY_API_TOKEN | Token autorisé à mettre à jour et déployer l'app development. |
development | COOLIFY_APPLICATION_UUID | UUID de l'app development partagée. |
development | COOLIFY_ROLLING_APPLICATION_UUID | UUID de l'Application Docker Image utilisée par le workflow manuel de validation rolling. |
development | DEVELOPMENT_BASIC_AUTH_USERNAME | Username HTTP Basic de l'app development protégée. |
development | DEVELOPMENT_BASIC_AUTH_PASSWORD | Password HTTP Basic de l'app development protégée. |
development | variable DEVELOPMENT_URL | URL optionnelle de smoke test. Défaut https://dev.yayaw.app; utilisez une URL joignable par le runner interne si le DNS public n'est pas encore routé. |
production | COOLIFY_URL | Origine API Coolify. |
production | COOLIFY_API_TOKEN | Token autorisé à déployer l'app production. |
production | COOLIFY_APPLICATION_UUID | UUID de l'app production. |
production | COOLIFY_ROLLING_APPLICATION_UUID | UUID de l'Application Docker Image production après validation en development. |
development, production | BETTER_AUTH_SECRET | Secret build-time requis par le build de l'image Docker Next.js. |
development, production | DATABASE_URL | URL de base build-time pour l'évaluation des routes serveur. |
development | POSTGRES_ROTATION_DATABASE_URL | URL candidate temporaire utilisée seulement par la rotation des identifiants et la synchronisation runtime. Gardez DATABASE_URL sur l'identifiant fonctionnel courant jusqu'au succès du premier déploiement après rotation. |
development, production | secrets build NEXT_PUBLIC_* | Valeurs publiques intégrées au bundle client Docker. |
development, production | NEXT_SERVER_ACTIONS_ENCRYPTION_KEY | Clé AES base64 stable intégrée pendant next build afin que les instances superposées acceptent les mêmes payloads Server Actions. |
development, production | variable NEXT_BUILD_WORKERS | Nombre de workers de build. Défaut 1 pour stabiliser les builds Docker auto-hébergés. |
development, production | variable NEXT_STATIC_PAGE_GENERATION_TIMEOUT | Timeout de génération statique. Défaut 180. |
development, production | variable COOLIFY_SERVER_SSH_HOST | Override optionnel pour charger les images sur la VM Coolify. Défaut 127.0.0.1 depuis le runner Mac mini. |
development, production | variable COOLIFY_SERVER_SSH_PORT | Port SSH optionnel. Défaut 2222. |
development, production | variable COOLIFY_SERVER_SSH_USER | Utilisateur SSH optionnel. Défaut yannis. |
development, production | variable COOLIFY_SERVER_SSH_KEY_PATH ou secret COOLIFY_SERVER_SSH_KEY | Clé SSH optionnelle. Le runner Mac mini utilise par défaut /Users/yannis/.ssh/coolify_vm_ed25519. |
development, production | variable COOLIFY_SERVER_KNOWN_HOSTS_PATH | Known-hosts optionnel. Défaut /Users/yannis/.ssh/known_hosts_coolify. |
Le préflight de release CMS synchronise aussi le contrat base, stockage privé, preview signé, personnalisation et maintenance worker vers l'app et le worker Coolify. Configurez ces secrets d'environnement GitHub :
S3_ACCESS_KEY_ID,S3_ENDPOINTetS3_SECRET_ACCESS_KEYCMS_PREVIEW_SIGNING_SECRET,CMS_PERSONALIZATION_RATE_LIMIT_SECRETetCMS_PERSONALIZATION_FORM_CONTEXT_SECRET, chacun d'au moins 32 caractères ; gardez le secret de contexte de formulaire dédiéSTORAGE_TRANSFER_SIGNING_SECREToptionnel lorsque les tokens de transfert stockage ne doivent pas partager le secret de signature des previewsDATABASE_URLdans les deux environnementsMINIO_ROOT_PASSWORD,POSTGRES_PASSWORDet le temporairePOSTGRES_ROTATION_DATABASE_URLetPOSTGRES_ROTATION_PREVIOUS_PASSWORDendevelopment
Configurez STORAGE_PROVIDER=s3, STORAGE_MEDIA_BUCKET=media,
STORAGE_MEDIA_STAGING_BUCKET=media-staging,
STORAGE_CMS_FORM_BUCKET=cms-form-submissions,
CMS_PERSONALIZATION_CLIENT_IP_HEADER=x-real-ip,
PAGE_AI_WORKER_MAINTENANCE_MS=60000, S3_REGION=us-east-1 et
S3_FORCE_PATH_STYLE=true comme variables d'environnement GitHub.
STORAGE_PUBLIC_BASE_URL est optionnel si NEXT_PUBLIC_BASE_URL désigne déjà
la même origine publique. S3_SIGNED_PUBLIC_ENDPOINT est une variable
optionnelle pour une origine d'upload direct explicitement configurée. Ne
persistez pas de phase de rollout comme variable GitHub : les workflows de
déploiement et de rollout dérivent et valident la phase sûre reader, freeze
ou exact à partir de la flotte active avant de synchroniser les flags runtime
correspondants.
Préparez une seule fois les identifiants development, hors déploiement :
database_password="$(openssl rand -hex 32)"
storage_password="$(openssl rand -hex 32)"
database_fingerprint="$(
printf '%s' "$database_password" | shasum -a 256 | awk '{print $1}'
)"
rotation_database_url="$(
printf 'postgresql://yayaw:%s@postgres:5432/yayaw' "$database_password"
)"
printf '%s' "$database_password" | gh secret set POSTGRES_PASSWORD --env development
printf '%s' "$rotation_database_url" \
| gh secret set POSTGRES_ROTATION_DATABASE_URL --env development
printf '%s' "$storage_password" | gh secret set MINIO_ROOT_PASSWORD --env development
printf '%s' "$storage_password" | gh secret set S3_SECRET_ACCESS_KEY --env development
gh variable set CMS_DATABASE_CREDENTIALS_FINGERPRINT \
--env development \
--body "$database_fingerprint"Le mot de passe de base est volontairement une valeur hexadécimale brute de
32 octets afin de ne pas nécessiter d'encodage URL. Pour un volume development
existant, laissez DATABASE_URL sur l'identifiant fonctionnel courant pendant
le build des images de PR. Le workflow transmet cette URL active à l'étape de
rotation, qui en extrait le mot de passe brut en mémoire sans l'exposer dans les
logs. Définissez POSTGRES_ROTATION_PREVIOUS_PASSWORD explicitement seulement
si l'URL active emploie un encodage de mot de passe non pris en charge. Le job
de déploiement n'utilise POSTGRES_ROTATION_DATABASE_URL qu'une fois les
images disponibles : il teste l'identifiant candidat via l'alias privé
yayaw-postgres-development; si nécessaire, il utilise un fichier env distant
en permission 0600 pour modifier le rôle, reteste le nouvel identifiant puis
synchronise l'URL candidate vers Coolify. Il s'arrête avant toute mutation des
env Coolify si aucun identifiant ne fonctionne.
Après le premier déploiement development réussi avec l'identifiant tourné, promouvez l'URL candidate conservée puis supprimez le secret candidat temporaire :
printf '%s' "$rotation_database_url" \
| gh secret set DATABASE_URL --env development
gh secret delete POSTGRES_ROTATION_DATABASE_URL --env developmentSupprimez également POSTGRES_ROTATION_PREVIOUS_PASSWORD si le fallback
explicite a été configuré.
Pour un volume neuf sans application en cours d'exécution, définissez aussi
DATABASE_URL avec la même URL candidate avant le premier build. Ne remplacez
jamais seulement DATABASE_URL et POSTGRES_PASSWORD sur un volume existant
avant la fin du workflow de rotation.
Les workflows ont aussi besoin des permissions packages du dépôt: les jobs de
build utilisent packages: write pour publier ghcr.io/<owner>/yayaw-app:<sha>
et ghcr.io/<owner>/yayaw-worker:<sha>, tandis que les jobs de déploiement
Coolify utilisent packages: read pour tirer ces images depuis le runner
auto-hébergé.
L'app development partagée est volontairement mutable: chaque pull request force
la branche technique coolify/development du dépôt vers le SHA de tête de la
PR, met à jour la git_branch de l'app dans Coolify vers cette branche stable
et déploie sans forcer de rebuild. Cela évite les échecs lorsqu'une branche PR
mergée est supprimée avant que Coolify clone le dépôt, tout en laissant BuildKit
réutiliser les layers entre development et production. GitHub Actions sérialise
les déploiements development avec le groupe de concurrence
development-coolify-deploy, ce qui garde l'environnement prévisible pendant
que les nouvelles runs attendent la fin du déploiement en cours.
Gardez l'origine development privée. Préférez un hostname Tailscale-only ou
activez HTTP Basic au niveau de l'app Coolify tout en renvoyant
X-Robots-Tag: noindex, nofollow, noarchive. Le smoke test part du runner
auto-hébergé avec les secrets Basic Auth development; ce runner doit donc
atteindre l'URL protégée.
Socle Production
Ces valeurs sont requises avant qu'un déploiement production puisse servir l'app correctement:
| Variable | Où la récupérer | Indication |
|---|---|---|
NODE_ENV | Env application Coolify | Définir à production pour les conteneurs de production. |
NEXT_PUBLIC_BASE_URL | Configuration reverse proxy/DNS | Définir sur l'origine publique canonique, par exemple https://yayaw.app. |
BETTER_AUTH_SECRET | Secret manager | Valeur longue, aléatoire et stable pour cookies et tokens Better Auth. |
SCIM_CREDENTIAL_HASH_SECRET | Secret manager | Valeur aléatoire, stable, distincte et d'au moins 32 caractères pour les empreintes HMAC des credentials SCIM managés. |
DATABASE_URL | Fournisseur Postgres | Utiliser la connection string poolée production lorsque le fournisseur en expose une. |
BETTER_AUTH_TRUSTED_ORIGINS | Domaines de déploiement | Ajouter les origines preview/local en liste séparée par virgules. L'hôte canonique et sa variante www sont dérivés de NEXT_PUBLIC_BASE_URL. |
CADDY_HOST_PORT | Env application Coolify | Port hôte du conteneur Caddy Compose. Défaut production 3080; utilisez par exemple 3081 pour l'app development partagée. |
MINIO_API_HOST_PORT | Env application Coolify | Port hôte de l'API S3-compatible MinIO. Défaut production 9000; utilisez par exemple 9100 en development. |
MINIO_CONSOLE_HOST_PORT | Env application Coolify | Port hôte de la console MinIO. Défaut production 9001; utilisez par exemple 9101 en development. |
Réglages optionnels de pool base:
| Variable | Quand la définir |
|---|---|
DATABASE_POOL_MAX | Augmenter seulement si le fournisseur DB supporte plus de connexions par runtime. |
DATABASE_IDLE_TIMEOUT_SECONDS | Ajuster le cycle de vie idle pour les déploiements non-serverless. |
DATABASE_MAX_LIFETIME_SECONDS | Ajuster la rotation de connexions pour les runtimes long-lived. |
DATABASE_CONNECT_TIMEOUT_SECONDS | Ajuster le démarrage sur réseaux privés lents. |
DATABASE_STATEMENT_TIMEOUT_SECONDS | Ajouter un timeout serveur par statement pour chaque connexion DB. |
DATABASE_PREPARE_STATEMENTS | Garder false pour les connexions production pooler/serverless sauf support explicite du fournisseur. |
Base De Données
DATABASE_URL est requis par le runtime applicatif, les scripts setup, les seeds
et les opérations Drizzle.
Pour le déploiement Coolify auto-hébergé, la synchronisation de schéma et les
seeds production tournent dans le service Compose migrate avant le démarrage
de l'app et du worker. Gardez le DATABASE_URL production dans Coolify ou le
secret store de l'orchestrateur afin que l'étape de setup résolve les noms de
services Docker internes comme postgres.
Pour les fournisseurs hébergés qui n'exécutent pas la pile Compose auto-hébergée, lancez l'opération de schéma Drizzle choisie depuis un runner capable d'atteindre la base production avant de promouvoir la nouvelle version.
Pour les fournisseurs Postgres hébergés:
Créez la base production.
Ouvrez le panneau des connection strings.
Copiez la connection string poolée lorsqu'elle existe.
Stockez-la comme
DATABASE_URLdans le secret store Coolify/de l'orchestrateur.Utilisez une base de développement séparée dans
.env.localou l'application Coolify de développement.
Si le fournisseur expose des URL directes et poolées, utilisez le mode de connexion recommandé pour les conteneurs long-lived et dimensionnez le pool applicatif selon la limite de la base.
Hôte Canonique Et Auth
Définissez:
NEXT_PUBLIC_BASE_URL=https://yayaw.app
BETTER_AUTH_TRUSTED_ORIGINS=https://*.yayaw.appNEXT_PUBLIC_BASE_URL pilote les URL canoniques, l'URL de base Better Auth, les
métadonnées OAuth, les liens sitemap/robots et les URL absolues de ressources.
Ne définissez pas NEXT_PUBLIC_SITE_URL; l'application ne le lit pas. Ne
définissez pas BETTER_AUTH_URL pour les nouveaux déploiements; Better Auth
reçoit son URL de base depuis NEXT_PUBLIC_BASE_URL.
Le domaine de prévisualisation branch-backed est https://preview.yayaw.app et
suit la branche durable preview.
Les hôtes .eu retirés peuvent rester attachés uniquement comme redirections
308 du reverse proxy vers leurs remplaçants .app, comme yayaw.eu et
www.yayaw.eu vers yayaw.app. Ils ne doivent pas servir de trafic
dashboard/auth ni figurer dans BETTER_AUTH_TRUSTED_ORIGINS.
Préflight De Bascule Better Auth 1.7
Avant d'appliquer la migration Better Auth 1.7 à une restauration récente de production ou à la base de bascule, arrêtez les écritures d'authentification et exécutez :
bun run ba:1-7:preflightCe préflight en lecture seule inventorie les providers de comptes et leurs
futurs issuers, les clients/ressources OAuth, les codes Device Authorization et
les lignes SCIM legacy. Il bloque les issuers inconnus, collisions d'identité,
métadonnées OAuth dangereuses, codes device dupliqués, liens de ressources
orphelins, clients machine sans propriétaire utilisateur durable ni référence
d'organisation correspondant à une adhésion vérifiée, ou données SCIM legacy
non vides. Le client machine dont le propriétaire n'appartient qu'à une seule
organisation est backfillé de façon déterministe ; zéro ou plusieurs adhésions
exigent un choix explicite dans oauth_client.reference_id, sans jamais
réenregistrer le client. Relancez-le juste avant la migration, une fois la
fenêtre de maintenance ouverte.
Better Auth 1.7.3 restaure l'identité des comptes sur
(provider_id, account_id) et cesse d'écrire le champ temporaire issuer
introduit par les versions 1.7.0 à 1.7.2. La migration 0052 refuse les paires
provider/compte dupliquées, remplace l'index unique temporaire fondé sur
issuer et conserve account.issuer nullable pour permettre un rollback. Ne
rendez pas cette colonne obligatoire et ne l'utilisez plus pour rechercher un
compte.
Provisionnement SCIM Managé
SCIM managé est activé par le flag seedé enable-scim-plugin. Définissez un
SCIM_CREDENTIAL_HASH_SECRET stable et distinct dans chaque environnement
activé avant de démarrer l'application :
openssl rand -hex 32Stockez le résultat dans le gestionnaire de secrets de l'orchestrateur et transmettez-le au runtime applicatif. Les définitions Compose Coolify et auto-hébergée portable transmettent cette valeur. Ne l'exposez jamais comme variable publique ou de build. Better Auth stocke des empreintes HMAC-SHA256 versionnées : changer le secret sans migration des données invalide tous les tokens managés.
Un administrateur disposant de organization-settings:manage crée et fait
tourner les connexions depuis /dashboard/organization/settings. Configurez le
fournisseur d'identité avec le bearer token affiché une seule fois et :
https://yayaw.app/api/auth/scim/v2Le token brut n'est renvoyé qu'à la création ou à la rotation et doit être copié avant d'actualiser le tableau de bord. Chaque credential expire par défaut après 365 jours, permet la lecture et l'écriture des Users/Groups SCIM et peut être révoqué pendant qu'un credential renouvelé reste actif. Le décommissionnement est irréversible. Les groupes SCIM restent des données de provisionnement : aucun nom, membre ou attribut de rôle ne crée un rôle, binding ou permission Yayaw.
Stripe Billing
Stripe a trois secrets requis lorsque la facturation est activée:
| Variable | Où la récupérer | Notes |
|---|---|---|
STRIPE_SECRET_KEY | Stripe Dashboard, Developers, API keys | Utilisez sk_live_... en production et sk_test_... en staging/local. |
STRIPE_WEBHOOK_SECRET | Stripe Dashboard, Developers, Webhooks, signing secret endpoint abonnement | Utilisé par l'endpoint webhook Better Auth Stripe. |
STRIPE_ONE_TIME_WEBHOOK_SECRET | Stripe Dashboard, Developers, Webhooks, signing secret endpoint achat ponctuel | Utilisé par l'endpoint custom one-time checkout. |
Créez deux endpoints webhook Stripe pour la production:
https://yayaw.app/api/auth/stripe/webhook
https://yayaw.app/api/billing/stripe/webhookUtilisez le signing secret du premier endpoint pour STRIPE_WEBHOOK_SECRET et
celui du second pour STRIPE_ONE_TIME_WEBHOOK_SECRET. Ne réutilisez pas le
secret d'un endpoint pour l'autre.
L'endpoint abonnement doit recevoir les événements de checkout abonnement et de
cycle de vie billing. L'endpoint one-time a seulement besoin de
checkout.session.completed pour les achats lifetime.
Les prix des produits de facturation sont gérés depuis
/dashboard/admin/billing-products ou via le plan de contrôle MCP. Les Stripe
Price IDs sont stockés en base après synchronisation catalogue; ce ne sont pas
des secrets de déploiement.
Liens Livrables D'Accès Au Code
Ces valeurs sont des URL non secrètes optionnelles affichées sur
/dashboard/organization/code-access après un achat payant qui débloque l'accès
au code:
| Variable | Exemple | Quand l'utiliser |
|---|---|---|
BILLING_CODE_ACCESS_REPOSITORY_URL | https://github.com/Yayaw-eu/yayaw | Lien vers le dépôt privé après attribution de l'accès GitHub. |
BILLING_CODE_ACCESS_DOWNLOAD_URL | https://github.com/Yayaw-eu/yayaw/releases | Lien vers artifacts de release ou archives. |
BILLING_CODE_ACCESS_DOCUMENTATION_URL | https://docs.yayaw.app | Lien vers la documentation de setup ou d'usage. |
BILLING_CODE_ACCESS_SUPPORT_URL | mailto:[email protected] | Lien support pour les problèmes d'accès. |
Laissez une valeur vide lorsque le livrable requiert un provisioning manuel.
GitHub Code Access
L'accès dépôt production doit utiliser une GitHub App, pas un personal access
token. L'app permet à Yayaw d'inviter les clients payants dans le dépôt
configuré avec l'accès lecture seule pull, sans stocker les identifiants
GitHub d'un utilisateur humain.
Créer La GitHub App
Créez l'app depuis le compte qui possède le dépôt privé:
GitHub organization -> Settings -> Developer settings -> GitHub Apps -> New GitHub AppValeurs recommandées:
| Champ | Valeur |
|---|---|
| GitHub App name | Yayaw Code Access |
| Homepage URL | URL de production, par exemple https://yayaw.app |
| Webhook | Désactivé sauf future fonctionnalité GitHub callbacks |
| Where can this GitHub App be installed | Only on this account |
Permissions de dépôt:
| Permission | Accès |
|---|---|
| Administration | Read and write |
| Metadata | Read-only, accordé automatiquement par GitHub |
Administration: Read and write est requis car Yayaw appelle l'API GitHub
collaborators pour inviter le username demandé dans le dépôt. L'app n'a pas
besoin de permission Contents pour le flux actuel: le client reçoit l'accès
dépôt normal via son propre compte GitHub.
Installer La GitHub App
Après création:
Générez une clé privée depuis les réglages de l'app et téléchargez le
.pem.Installez l'app sur le compte ou l'organisation propriétaire.
Sélectionnez uniquement le dépôt auquel les clients payants doivent accéder.
Copiez l'App ID depuis les réglages.
Copiez l'installation ID depuis l'URL d'installation.
L'URL d'installation ressemble généralement à ceci pour une organisation:
https://github.com/organizations/Yayaw-eu/settings/installations/12345678Le segment numérique final est l'ID d'installation de la GitHub App.
Stocker Le Secret GitHub App
Stockez la clé privée dans le magasin de secrets Coolify/de l'orchestrateur pour
chaque environnement cible. Utilisez son éditeur de secret multiligne afin que
le PEM ne passe jamais en argument de commande. Pour un .env.local ou fichier secret
auto-hébergé, collez la clé multiligne entre guillemets ou échappez les sauts de
ligne sur une ligne:
printf 'BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY="%s"\n' "$(perl -0pe 's/\n/\\n/g' yayaw-code-access.pem)" >> .env.localLe runtime accepte les séquences \n échappées et les reconvertit en vrais
sauts de ligne avant de signer le JWT GitHub App. Si une plateforme aplatit le
PEM en une ligne avec des espaces entre header, body et footer, Yayaw le
normalise en PEM au runtime.
Configurer Les Valeurs Non Secrètes GitHub
Préférez l'UI admin pour les valeurs non secrètes:
/dashboard/admin/billing-settingsDéfinissez:
| Réglage admin | Valeur |
|---|---|
| Enabled | On |
| Repository | Yayaw-eu/yayaw ou le dépôt cible exact |
| GitHub App ID | App ID numérique depuis les réglages GitHub App |
| GitHub App installation ID | Installation ID numérique depuis l'URL d'installation |
Des replis d'environnement existent pour les déploiements qui ont besoin de config au démarrage:
BILLING_CODE_ACCESS_GITHUB_REPOSITORY=Yayaw-eu/yayaw
BILLING_CODE_ACCESS_GITHUB_APP_ID=123456
BILLING_CODE_ACCESS_GITHUB_APP_INSTALLATION_ID=12345678
BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"Repli Token Local Ou Staging
Utilisez BILLING_CODE_ACCESS_GITHUB_TOKEN uniquement pour les environnements
locaux ou staging sans GitHub App:
BILLING_CODE_ACCESS_GITHUB_REPOSITORY=Yayaw-eu/yayaw
BILLING_CODE_ACCESS_GITHUB_TOKEN=github_pat_...Le propriétaire du token doit pouvoir ajouter des collaborateurs au dépôt. Pour un token fine-grained, accordez l'accès au seul dépôt cible et incluez la permission d'administration de dépôt nécessaire aux invitations. N'utilisez pas ce repli en production sauf contournement d'incident avec plan de rotation.
RESEND_API_KEY active les e-mails transactionnels d'invitation, reset, magic
link et facturation.
Pour la récupérer:
Vérifiez le domaine d'envoi dans Resend pour l'e-mail production.
Créez une clé API server-side dans Resend.
Stockez-la comme
RESEND_API_KEYdans l'environnement cible.Envoyez un e-mail de test depuis la surface admin des e-mails transactionnels après déploiement.
Définissez EMAIL_SENDER sur un expéditeur vérifié du domaine Resend, par
exemple [email protected]. Définissez EMAIL_SUPPORT sur l'adresse support
affichée dans les modèles et EMAIL_USERNAME sur le nom affiché. Ces variables
sont des défauts bootstrap et replis; après la première connexion admin, Admin >
Site Settings > Email est prioritaire au runtime.
L'absence de RESEND_API_KEY ne casse pas le traitement des webhooks de
facturation, mais ignore les e-mails transactionnels.
Fournisseurs OAuth
Configurez OAUTH_PROVIDER_ENCRYPTION_KEYS dans les secrets d'exécution Coolify
sur chaque instance avant de configurer les fournisseurs. Générez une clé dédiée
avec openssl rand -base64 32, précédée d'un identifiant (key1:<valeur>). Ce
secret ne sert pas au build et doit être distinct de BETTER_AUTH_SECRET.
Les identifiants Google, GitHub et Microsoft se gèrent dans Administration >
Réglages du site avec le droit global auth-provider:manage et un code TOTP.
Enregistrez un brouillon, déclarez l'URL de retour affichée chez le fournisseur,
testez puis activez. Modifier un fournisseur ne nécessite ni build ni redémarrage.
La configuration initiale ou la rotation de la clé nécessite un redémarrage.
L'application OAuth GitHub est distincte de la GitHub App d'accès au code.
Utilisez des applications distinctes pour la préproduction et la production.
Sauvegardez les clés séparément de la base. Lors d'une rotation, gardez les deux
clés (nouvelle:<valeur>,ancienne:<valeur>) jusqu'au nouvel enregistrement, test et
activation de chaque fournisseur. Conservez les clés des anciennes sauvegardes.
Object Storage
Le stockage média est optionnel jusqu'à l'utilisation d'upload, miniatures,
logos ou génération d'images. Définissez STORAGE_PROVIDER=s3 pour MinIO, S3,
R2 ou un autre fournisseur compatible S3.
Variables communes:
| Variable | Où la récupérer |
|---|---|
STORAGE_PROVIDER | Définir sur s3. |
STORAGE_MEDIA_BUCKET | Nom du bucket des ressources média. Défaut media. |
STORAGE_MEDIA_STAGING_BUCKET | Bucket privé des téléversements directs en staging. Le self-host fourni utilise media-staging. |
STORAGE_CMS_FORM_BUCKET | Bucket privé durable des pièces jointes client. Le self-host fourni utilise cms-form-submissions. |
STORAGE_PUBLIC_BASE_URL | Origine publique des objets pour stockage compatible S3, servant /<bucket>/<key>. Utilisez l'origine app ou CDN seulement si ces préfixes de bucket sont routés vers le stockage objet. |
STORAGE_TRANSFER_SIGNING_SECRET | Secret dédié facultatif des grants privés same-origin opaques. Il se replie sur le secret de preview ou d'auth et doit fournir au moins 32 caractères en production. |
CMS_PERSONALIZATION_FORM_CONTEXT_SECRET | Secret dédié obligatoire pour la provenance éphémère des formulaires CMS publics. Générez au moins 32 caractères aléatoires et ne l'exposez jamais au navigateur ou au contenu CMS. |
S3_SIGNED_PUBLIC_ENDPOINT | Origine d'API S3 publique explicite et facultative pour les uploads signés directs quand l'hébergeur app ne supporte pas la limite CMS de 250 Mo. Ne jamais renseigner l'endpoint Docker interne. |
Pour un stockage S3 same-origin ou derrière CDN, proxifiez chaque préfixe de
bucket public vers le stockage objet avant le repli app. Les buckets publics
intégrés sont /media/* pour les médias téléversés et
/organization-logos/* pour les logos d'organisation. Si
STORAGE_PUBLIC_BASE_URL pointe directement vers une origine de stockage objet
dédiée, cette origine doit servir les mêmes chemins /<bucket>/<key>.
Créez les buckets de staging et de formulaires avant d'activer leurs écritures
et ne leur accordez aucun accès anonyme. Limitez le nettoyage par cycle de vie
du fournisseur au bucket ou préfixe de staging ; le worker Page AI pilote le
ledger de rétention à 180 jours des photos de formulaires et reprend les
suppressions interrompues. Définissez
CMS_PERSONALIZATION_CLIENT_IP_HEADER=x-real-ip avec le Caddy fourni, qui
écrase ce header, ou utilisez un autre header mono-valeur possédé par le proxy
seulement après avoir vérifié que l'ingress retire les valeurs client. Gardez
le worker actif avec PAGE_AI_QUEUE_DRIVER=db-worker en self-host.
Les transferts privés utilisent par défaut un grant applicatif same-origin
opaque, donc S3_ENDPOINT peut rester privé. Sur une plateforme dont la limite
de corps entrant est plus faible, définissez S3_SIGNED_PUBLIC_ENDPOINT sur
une origine d'API S3 HTTPS publique et sûre ; seuls les uploads deviennent des
PUT directs conditionnels, tandis que les téléchargements privés restent
derrière le proxy applicatif. Autorisez l'origine app, PUT, Content-Type,
Content-Length et If-None-Match dans la politique CORS de ce bucket. Les
octets de staging promus restent un tombstone sans écrasement pendant
l'expiration du grant et une stabilisation de 24 heures. Le reaper répète la
suppression et n'enregistre le nettoyage qu'après son DELETE final.
Activez les écritures de prototypes exacts dans cet ordre :
Déployez le reader compatible avec
CMS_LEGACY_PROTOTYPE_WRITES_ENABLED=trueetCMS_PROTOTYPE_RUNTIME_V2_WRITES_ENABLED=false.Vous pouvez lancer le workflow manuel
CMS Prototype Runtime Rolloutavecreaderet le SHA complet déjà déployé pour prouver la flotte reader.Lancez ensuite ce workflow de confiance avec
exact. Il vérifie ou crée les buckets privés, synchronisefreeze, redéploie chaque ressource app/worker typée sur le SHA complet demandé et enregistre UUID de ressource, UUID de déploiement, digest, nom d'image, commit et jeu complet d'identifiants des conteneurs actifs dans un reçu durable version 1. Chaque conteneur actif doit prouver séparément le SHA et les valeurs legacy, v2, staging et opération de rollout attendues.Après avoir revérifié qu'aucun déploiement plus récent n'a invalidé la preuve de drain, le workflow fait passer le reçu de
issuedàactivating, synchronise legacyfalse, v2true, staged uploadstrueetCMS_PROTOTYPE_ROLLOUT_OPERATION_ID, puis redéploie et revérifie toutes les ressources. L'UUID de déploiement exact doit être nouveau ; UUID de ressource typée, digest, nom d'image et SHA complet restent identiques.Le workflow vérifie l'état runtime final, réinspecte tous les conteneurs exacts et transmet ce JSON frais directement à la consommation du reçu dans la même étape shell. La consommation active le floor de base avant toute écriture de gate. Seule l'opération consommée courante peut autoriser un nouveau
prototypeGateV2.
Le workflow récupère ses scripts de control plane à un SHA de workflow
immuable : le SHA capturé par GitHub lors du déclenchement d'un run manuel, ou
le SHA de control plane immuable transmis par le parent qui autorise un enfant
réutilisable. L'autorisation prouve que ce SHA reste un ancêtre du main
courant récupéré ; l'avancement de main ne peut pas modifier les scripts d'un
run déjà autorisé. Le SHA de déploiement demandé est une preuve, jamais du code
de workflow exécutable. Le dispatcher de reçus durables est copié depuis ce
checkout épinglé, vérifié par checksum, puis exécuté dans un conteneur one-shot
durci attaché au réseau Docker Coolify privé avec l'image worker construite
depuis ce même SHA de control plane immuable. Son entrypoint est remplacé et
son ID d'image est épinglé avant tout accès à la base. Gardez Postgres privé ;
le rollout n'exige ni URL ni port de base de données public. Si une exécution
échoue après activating, le handler d'échec peut seulement restaurer puis
redéployer freeze. Reprenez explicitement la même opération avec
resume_operation_id et sa chaîne de confirmation. La reprise exige le même
environnement, dépôt, SHA de déploiement, ressources typées et images, ainsi
qu'une nouvelle tentative de preuve live avec sa propre expiration. Le parent
immuable conserve le SHA de control plane qui l'a émis ; la tentative enfant
reprise enregistre et valide le SHA immuable de workflow/control plane capturé
pour sa propre autorisation. Elle ne peut ni créer un reçu actif concurrent ni
revenir au reader.
Un dispatch exact autonome en production n'est accepté que si son SHA déployé
correspond au SHA immuable de workflow/control plane capturé pour ce run. Ce
garde s'exécute avant toute mutation des writers : une demande de reprise
obsolète ne force donc pas la flotte live en freeze. Si main a avancé au-delà
du SHA déployé d'un reçu activating, déployez le descendant strict en
préservant freeze, puis utilisez la supersession explicite ci-dessous au lieu
de reprendre l'ancien SHA.
Si la flotte a déjà avancé vers un SHA descendant strict alors qu'un ancien
reçu reste activating, ne reprenez pas l'ancien SHA et ne faites pas de
rollback. Lancez exact sur le descendant live avec
superseded_operation_id, superseded_deploy_sha et la confirmation
exact:<nouveau-sha>:supersede:<ancien-sha>:<ancien-operation-id>. Le workflow
prouve l'ascendance Git, redéploie et revérifie le descendant en freeze, puis
crée et active atomiquement le reçu de remplacement en liant l'ancien comme
terminal superseded. Le remplacement conserve les UUID de ressources typées
et prouve de nouveaux UUID de déploiement. La supersession ne fait jamais
avancer le runtime floor ; seule la consommation du remplacement le fait.
Le déploiement automatique de production garde bootstrap/reader en reader et
peut préserver freeze uniquement lorsque la configuration Coolify et chaque
conteneur app/worker live prouvent déjà freeze. Lorsque la production est déjà
en exact, le run automatique se termine maintenant sans erreur avant build,
migration ou rollout et indique qu'une promotion CMS est requise, avec le SHA
live vérifié et le SHA cible demandé dans son résumé. Le résumé mène directement
à la page du workflow CMS Production Promotion, où l'opérateur doit
sélectionner main, vérifier les trois valeurs prêtes à copier, puis valider
manuellement Run workflow. La confirmation exacte reste obligatoire. Il ne
modifie ni ne préserve jamais exact implicitement. Le déploiement de PR
development partagé suit les mêmes règles reader/freeze et n'active jamais
exact. Si main avance avant la validation, utilisez les valeurs de son run
différé réussi le plus récent : les anciennes valeurs échouent volontairement
face au nouveau SHA de workflow sélectionné.
Pour une évolution ultérieure du renderer en development :
Lancez
CMS Prototype Runtime Rolloutavecfreezesur le SHA complet actuellement déployé.Relancez le déploiement de la PR cible. Sous le lock partagé
development-coolify-deploy, il préserve freeze, déploie le nouveau SHA immuable de la PR, puis prouve que chaque conteneur app/worker live expose ce SHA et les flags freeze. Une ressource Compose development unique est prouvée comme un seul déploiement contenant les deux images attendues, avec un digest d'image agrégé stable et le jeu complet d'IDs de conteneurs app/worker.Lancez
CMS Prototype Runtime Rolloutavecexactsur ce même SHA de PR. Il crée et consomme le reçu suivant, fait avancer l'opération courante et conserve l'opération initiale du floor.
Pour une évolution ultérieure du renderer en production, déclenchez l'unique
workflow CMS Production Promotion avec le SHA complet actuellement déployé,
le SHA immuable de main capturé par le déclenchement du workflow et
promote-exact:<sha-courant>:<sha-cible>. Il conserve le lock
rolling-coolify-production pendant toute l'opération et appelle les workflows
réutilisables de confiance dans l'ordre : preuve et redéploiement du SHA courant
en freeze, build/migration/rollout de la cible en conservant freeze, puis
émission et consommation du reçu exact de la cible. Un échec interrompt la
chaîne ; un échec d'activation exacte restaure freeze sans créer un second reçu.
Le parent prouve que ce SHA capturé appartient toujours à main, puis épingle
le même SHA du workflow/control plane dans chaque enfant : un nouveau commit
sur main ne peut donc pas changer les scripts de déploiement ni l'image worker
attendue par le reçu en cours de promotion. Les trois workflows manuels de bas
niveau restent disponibles pour une reprise ou une supersession explicite de
reçu.
Le job normal ne simule jamais cette transition. Une fois le floor actif, n'utilisez jamais reader comme reset et ne réactivez jamais les writers legacy.
Les migrations 0047_cms_prototype_runtime_floor et
0049_cms_prototype_rollout_receipt_supersession rendent cette frontière
durable. Elles persistent les reçus parents et tentatives immuables,
contraignent chaque état à sens unique issued → activating → consumed, ou
la branche de récupération terminale explicite activating → superseded,
puis verrouillent les writers de sessions pendant le backfill sûr de tout gate
exact préexistant avec une opération initiale/courante synthétique
irréversible. La consommation d'un reçu active le singleton indépendant. Son
timestamp d'activation et sa première
opération sont immuables ; l'ID du premier contexte reste nul jusqu'au premier
nouveau gate exact, puis devient immuable. Les reçus consommés ultérieurs ne
peuvent que faire avancer current_v2_rollout_operation_id. Les sessions
exactes existantes continuent leurs mises à jour ordinaires sans rejouer la GUC
transactionnelle ; un gate exact nouvellement ajouté doit porter l'opération
courante. La chaîne synthétique de backfill ne constitue jamais une
autorisation suffisante : l'opération courante doit aussi correspondre à un
reçu réellement consommé avant l'ajout d'un autre gate exact. Le floor ne peut
être ni supprimé, ni tronqué, ni réinitialisé, ni
annulé par l'expiration/suppression de la session d'origine. Des triggers de
base de données et des gardes writer dans la
même transaction sérialisent les écritures legacy de session, composant, page,
review, publication et média sur ce singleton. Réinitialiser un flag
d'environnement ne peut donc pas rouvrir les mutations legacy. Les lectures
legacy, dry-runs purs, lectures idempotentes d'uploads terminés,
annulations/expirations et nettoyages média restent disponibles. Un commit
média refusé par ce floor supprime l'objet promu, tandis que le nettoyage d'un
upload préparé reste repris par le reaper.
Le workflow manuel constitue aussi la frontière de sûreté des binaires en
rolling : chaque ressource doit exécuter le SHA immuable en freeze avant
qu'une ressource passe en exact. Ne modifiez pas ces flags directement et ne
contournez pas ce workflow, car les anciens binaires ne contiennent pas toutes
les gardes applicatives du floor.
Le préflight stockage de l'image worker requiert HeadBucket, l'optionnel
CreateBucket, GetBucketPolicy, GetBucketAcl, PutObject, GetObject et
DeleteObject sur les buckets staging et formulaires. Il n'affiche jamais les
identifiants de stockage ni les erreurs provider complètes.
Pendant le setup, bun run seed téléverse les ressources par défaut de variables
globales de site dans le bucket média sous global/site-variables/* et écrit ces
URL publiques dans l'entrée site-variables/main. Les URL de ressources
personnalisées existantes sont préservées lorsque le seed est relancé.
Variables de stockage compatible S3:
| Variable | Où la récupérer |
|---|---|
S3_ENDPOINT | Endpoint fournisseur, par exemple http://minio:9000. |
S3_REGION | Région fournisseur. Utilisez us-east-1 pour MinIO sauf configuration différente. |
S3_ACCESS_KEY_ID | Identifiant de stockage server-side. |
S3_SECRET_ACCESS_KEY | Identifiant de stockage server-side. |
S3_FORCE_PATH_STYLE | Définir true pour MinIO et la plupart des endpoints compatibles S3 locaux. |
N'exposez jamais S3_ACCESS_KEY_ID ou S3_SECRET_ACCESS_KEY au navigateur ni
dans une variable NEXT_PUBLIC_*.
S3_SIGNED_PUBLIC_ENDPOINT est optionnel et n'est jamais déduit de
S3_ENDPOINT. En production, sa présence impose HTTPS, un hostname DNS public
multi-label, sans credentials, query ni fragment. Il active uniquement les PUT
directs signés ; les downloads restent derrière le proxy same-origin de l'app.
Avant de définir cette variable, configurez le CORS du bucket pour l'origine de
l'app, PUT et les headers Content-Type, Content-Length et
If-None-Match. STORAGE_TRANSFER_SIGNING_SECRET peut fournir un secret dédié
aux tokens opaques de transfert ; sinon le runtime se replie volontairement sur
CMS_PREVIEW_SIGNING_SECRET, puis BETTER_AUTH_SECRET. Le secret effectif en
production doit contenir au moins 32 caractères.
Domaines Publics
Les domaines publics d'organisation utilisent PUBLIC_DOMAIN_PROVIDER.
| Variable | Usage |
|---|---|
PUBLIC_DOMAIN_PROVIDER | Définir à manual-dns pour le chemin supporté de vérification DNS auto-hébergée. |
APP_MANAGED_HOSTS | Hôtes possédés par l'app, séparés par virgules, que les clients ne peuvent pas réclamer. |
RESERVED_PUBLIC_DOMAIN_SUFFIXES | Suffixes supplémentaires que les clients ne peuvent pas réclamer. |
PUBLIC_DOMAIN_CNAME_TARGET | Cible CNAME DNS manuel affichée aux opérateurs. |
PUBLIC_DOMAIN_IPV4_TARGETS | Cibles A-record DNS manuel séparées par virgules. |
PUBLIC_DOMAIN_TXT_PREFIX | Préfixe TXT de vérification, défaut _yayaw. |
manual-dns génère un challenge TXT de propriété et vérifie le DNS public. Il
n'automatise ni les changements chez le fournisseur DNS ni l'émission de
certificats TLS.
OpenAI
Les fonctions de génération IA requièrent:
OPENAI_API_KEY=Créez la clé depuis la page API keys de la plateforme OpenAI et stockez-la côté serveur dans l'environnement cible. Elle active le repli IA de composants, Page AI et la génération d'images lorsque les feature flags et réglages runtime correspondants sont actifs.
Toggles runtime IA optionnels:
| Variable | Défaut | Usage |
|---|---|---|
OPENAI_COMPONENTS_AI_FALLBACK | true | Active le repli IA pour inférence et classification de composants. |
OPENAI_IMAGE_GENERATION_ENABLED | true | Active la génération d'images du page builder lorsqu'une clé API existe. |
OPENAI_IMAGE_MODEL | gpt-image-1.5 | Modèle de génération d'images. |
PAGE_AI_QUEUE_DRIVER | Auto | direct localement ou db-worker avec le worker Coolify long-lived. La production choisit db-worker; le driver legacy vercel-queue exige une valeur explicite et n'est jamais déduit de variables VERCEL_* résiduelles. |
PAGE_AI_DEEP_REFINEMENT | false | Toggle qualité interne, non exposé dans les réglages admin. |
PAGE_AI_WORKER_POLL_MS | 1500 | Intervalle de polling pour le driver worker base de données. |
PAGE_AI_WORKER_MAINTENANCE_MS | 60000 | Intervalle de rétention durable, nettoyage des objets orphelins et purge des buckets anti-abus. |
PAGE_AI_WORKER_ID | Vide | Identifiant optionnel pour un worker long-lived. |
Analytics
Choisissez un fournisseur analytics et un mode de capture:
NEXT_PUBLIC_ANALYTICS_PROVIDER=umami
NEXT_PUBLIC_ANALYTICS_CAPTURE_MODE=hybridFournisseurs supportés: posthog, umami, none. Le mode de capture peut être
hybrid, server ou client. hybrid est le défaut et enregistre des
événements facturation/auth fiables côté serveur tout en gardant l'analytics
produit navigateur. server ne rend pas de scripts analytics navigateur et
envoie les vues CMS plus les conversions depuis le serveur applicatif. client
conserve la capture uniquement navigateur.
Les événements de conversion serveur incluent checkout_started,
purchase_completed, subscription_started, subscription_updated,
subscription_cancel_scheduled, subscription_canceled et payment_failed.
Les événements de facturation incluent revenue, value, currency, plan,
product_key et les IDs Stripe pour la réconciliation. Les événements Stripe en
mode test sont suivis avec billing_mode=test et is_test_data=true, mais ne
comptent pas comme conversion, revenue ou value sauf activation du réglage
admin Track Stripe Test Billing Analytics. Le montant test original reste dans
test_amount_cents et test_revenue pour le débogage.
PostHog
La capture client et les feature flags utilisent les valeurs PostHog publiques:
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
NEXT_PUBLIC_POSTHOG_ENABLE_LOCAL=falseLes analytics dashboard côté serveur utilisent les valeurs PostHog privées:
POSTHOG_PERSONAL_API_KEY=
POSTHOG_PROJECT_ID=
POSTHOG_API_HOST=https://eu.posthog.com
POSTHOG_ORG_ID_PROPERTY=organization_id
POSTHOG_FLAG_LOOKUP_TIMEOUT_MS=Récupérez la clé projet publique et l'ID projet depuis les réglages PostHog.
Créez une personal API key avec l'accès suffisant à l'API Query et stockez-la
comme POSTHOG_PERSONAL_API_KEY. Gardez
POSTHOG_ORG_ID_PROPERTY=organization_id sauf changement de la propriété
d'événement analytics dans le produit. Définissez
POSTHOG_FLAG_LOOKUP_TIMEOUT_MS uniquement si les lookups de feature flags
PostHog côté serveur ont besoin d'un timeout différent du défaut 1500
millisecondes.
Umami
La capture client utilise les valeurs Umami publiques:
NEXT_PUBLIC_UMAMI_HOST_URL=https://analytics.example.com
NEXT_PUBLIC_UMAMI_SCRIPT_URL=
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_DOMAINS=
NEXT_PUBLIC_UMAMI_AUTO_TRACK=trueLes lectures dashboard côté serveur et la capture serveur utilisent les valeurs Umami privées:
UMAMI_API_URL=https://analytics.example.com
UMAMI_WEBSITE_ID=
UMAMI_API_TOKEN=
UMAMI_API_KEY=
UMAMI_USERNAME=
UMAMI_PASSWORD=
UMAMI_CMS_EVENT_PAGE_SIZE=1000Utilisez soit UMAMI_API_TOKEN (ou l'alias legacy UMAMI_API_KEY), soit
UMAMI_USERNAME plus UMAMI_PASSWORD pour les lectures de données dashboard.
La capture d'événements serveur utilise Umami /api/send; elle a donc seulement
besoin de UMAMI_API_URL et UMAMI_WEBSITE_ID.
Plan De Contrôle MCP
Le MCP production ne requiert pas de variable d'environnement dans le runtime
applicatif. Les clients se connectent à /api/mcp avec une clé API Better Auth
ou un access token OAuth.
Les tests stdio locaux peuvent utiliser:
YAYAW_MCP_API_KEY=
YAYAW_MCP_LOCAL_USER_ID=Créez, inspectez et révoquez les clés MCP avec:
bun run mcp:keyStockez les clés client de confiance hors du dépôt et passez-les au client comme
YAYAW_MCP_API_KEY.
Transforms Runtime Dynamic Data
Définissez DYNAMIC_DATA_RUNTIME_TRANSFORM_SECRET uniquement lorsque des routes
runtime dynamiques déployées utilisent des transforms de valeur hmac_sha256,
par exemple un code de pairing transformé en code_hash stocké.
DYNAMIC_DATA_RUNTIME_TRANSFORM_SECRET=$(openssl rand -hex 32)Stockez la valeur comme secret server-only dans l'orchestrateur. Sa rotation change les futures sorties HMAC; faites-la donc avec une migration de données spécifique au produit ou une fenêtre d'expiration des pairing sessions.
Mode Maintenance
Le mode maintenance est optionnel:
MAINTENANCE_MODE=false
MAINTENANCE_MODE_END_DATE=Définissez MAINTENANCE_MODE=true uniquement lorsque l'app doit servir la
surface de maintenance. Utilisez MAINTENANCE_MODE_END_DATE pour le contexte
opérateur.
Métadonnées De Déploiement
Les déploiements Coolify/auto-hébergés fournissent des métadonnées statiques:
| Variable | Usage |
|---|---|
DEPLOYMENT_PROVIDER | Utilisez static dans Coolify et local en développement local. |
DEPLOYMENT_URL | URL publique de déploiement. |
DEPLOYMENT_ENV | Label d'environnement, par exemple production ou preview. |
DEPLOYMENT_GIT_COMMIT_SHA | SHA de commit pour statut dashboard/plan de contrôle. |
DEPLOYMENT_GIT_COMMIT_REF | Ref ou nom de branche. |
Checklist De Déploiement
Avant promotion:
L'environnement cible possède chaque variable requise de
.env.exampleou.env.self-host.example.La prévisualisation a soit des valeurs fournisseurs équivalentes à la production, soit des valeurs staging explicites.
NEXT_PUBLIC_BASE_URLcorrespond à l'origine HTTPS publique de l'environnement.Stripe a deux endpoints webhook et chaque secret d'endpoint est mappé à la variable correspondante.
L'accès au code GitHub a soit les réglages admin plus
BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY, soit les replis d'environnement explicites pour dépôt, App ID, installation ID et clé privée.Les variables Resend, PostHog, S3, OpenAI et OAuth ne sont présentes que lorsque ces fonctions sont activées.
Coolify ou l'orchestrateur possède le
DATABASE_URLruntime utilisé par le service Composemigrate, et les environnements GitHub possèdent la valeur build-timeDATABASE_URLrequise parnext build.Un nouveau déploiement ou redémarrage de conteneur a été déclenché après la dernière modification de variable.
Lancez les barrières qualité locales avant de merger des changements de config de déploiement:
bun run check
bun run docs:generate
bun run docs:check-links
bun run docs:check-translations
bun run docs:llm:check
bunx tsc --noEmit
bun run build