Déploiement
Une app Fougere est une app Nitro plus une chose que Nitro ignore : les sources des Fronds sont lues au runtime (scan, parse AST, chargement jiti). Cette page est ce que le déploiement de ce site même a appris — la checklist, puis la réalisation Docker.
La checklist runtime
.output/— le build Nitro (pnpm build).- Les sources des Fronds — à côté de
.output: le scanner les lit depuisprocess.cwd()au boot.fronds/**/*.ts, et quand la racine est elle-même la Frond, les dossiers de la convention à la racine (entities/,handlers/,presenters/,collectors/,seeds/,services/,repositories/). Ne copier quefronds/laisse une app à plat sans domaine et sans erreur — voir le log de boot plus bas. - Un node_modules résoluble pour les fronds — ces sources importent
@fougere/schema,@fougere/core,better-auth… par leur nom.pnpm deploy --prodproduit exactement cette fermeture ; trier les paquets à la main, c'est réimplémenter un tracer — non. - Un TS avec l'API JS du compilateur (5.9 — TS 7 n'en a pas) — importé dynamiquement par le scanner, invisible au tracer de Nitro.
- Des paquets lazy complets — la trace pnpm de Nitro pour
jitiest partielle (sa transformation babel est chargée à la demande). Le remplacer par le vrai paquet. - Un
.data/writable — le fichier SQLite et l'index Nuxt Content y vivent. C'est la seule chose à persister : un volume. - Les secrets par env —
AUTH_SECRET(32+ caractères), leSITE_URLpublic.
Le signal de vérification est le log de boot — pas un 200 sur les pages (le SSR rend très bien avec zéro frond chargée) :
INF [boot:app] scanned 2 frond(s) in 467ms ← cette ligne
{"jsonrpc":"2.0","id":1,"result":[]} ← et un appel d'enveloppe
Docker
Le site livre une paire qui marche — site/Dockerfile + site/docker-compose.yml :
SITE_AUTH_SECRET='…32+ caractères…' SITE_URL='https://votre-domaine' \
docker compose -f site/docker-compose.yml up -d --build
Le Dockerfile réalise la checklist en deux étages :
# étage build : install workspace → build packages → build site
RUN pnpm --filter "./packages/**" -r run build \
&& pnpm --filter "@fougere/site" run build
# la fermeture de résolution des fronds, par pnpm lui-même :
RUN pnpm --filter "@fougere/site" deploy --prod --legacy /deploy
# compléter la trace partielle de jiti + ajouter TS 5.9
# étage run : node:22-slim, non-root
COPY --from=build /repo/site/.output ./.output
COPY --from=build /repo/site/fronds ./fronds
COPY --from=build /deploy/node_modules ./node_modules
VOLUME /app/.data
CMD ["node", ".output/server/index.mjs"]
L'état vit dans le volume site-data monté sur /app/.data — un backup est une copie de
fichier.
Splitter en production
Le même modèle d'image s'applique au split : le process host qui sert une Frond a besoin
des points 1–5 pour ses fronds, et le consommateur la déclare dans remotes. Un host
mort dégrade en 503 typées dans les pages
(Le gradient) ; le relancer est la
récupération — le consommateur n'est jamais rebuildé.
Trois choses s'appellent « ensemble » et une seule est obligatoire. Le contrat doit
voyager — le consommateur doit savoir ce que l'hôte sert. Le code, non : fougere sync
lit la carte d'identité de l'hôte et reconstruit un schéma vivant en local, ce qui est
d'ailleurs la seule façon de consommer une Frond écrite dans un autre langage. Les
ports doivent se joindre, et c'est le seul des trois qu'un déploiement décide.
Le récepteur écoute en loopback sauf mention contraire : le split le plus simple met donc
les deux process sur une même machine — le même pod, ou une seule image. hosts: ['0.0.0.0'] l'élargit en une ligne — et remet à l'exploitant ce que le défaut retenait :
un récepteur croit l'identité qu'on lui tend, donc l'atteindre à travers un réseau suppose
de refermer ce chemin autrement (mesh, pare-feu, sidecar) tant que l'identité n'est pas
réglée au niveau de la Frond.
Ces étapes sont candidates à un futur
fougere build— un utilisateur ne devrait pas avoir à les découvrir. D'ici là, cette page est le contrat.