[{"data":1,"prerenderedAt":667},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Finfra\u002Fdeployment":186},[4],{"title":5,"path":6,"stem":7,"children":8},"Docs","\u002Fdocs","docs",[9,12,16,20,24,28,50,75,120,145],{"title":10,"path":6,"stem":11},"What is Fougere","docs\u002Findex",{"title":13,"path":14,"stem":15},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":17,"path":18,"stem":19},"An app you already have","\u002Fdocs\u002Fexisting-app","docs\u002F02.existing-app",{"title":21,"path":22,"stem":23},"Bring your schema","\u002Fdocs\u002Fbring-your-schema","docs\u002F03.bring-your-schema",{"title":25,"path":26,"stem":27},"The CLI","\u002Fdocs\u002Fcli","docs\u002F04.cli",{"title":29,"path":30,"stem":31,"children":32,"page":49},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[33,37,41,45],{"title":34,"path":35,"stem":36},"Philosophy","\u002Fdocs\u002Fconcepts\u002Fphilosophy","docs\u002F2.concepts\u002F1.philosophy",{"title":38,"path":39,"stem":40},"The Frond","\u002Fdocs\u002Fconcepts\u002Ffrond","docs\u002F2.concepts\u002F2.frond",{"title":42,"path":43,"stem":44},"The shortest path","\u002Fdocs\u002Fconcepts\u002Fshortest-path","docs\u002F2.concepts\u002F3.shortest-path",{"title":46,"path":47,"stem":48},"The base","\u002Fdocs\u002Fconcepts\u002Fthe-base","docs\u002F2.concepts\u002F4.the-base",false,{"title":51,"path":52,"stem":53,"children":54,"page":49},"Schema","\u002Fdocs\u002Fschema","docs\u002F3.schema",[55,59,63,67,71],{"title":56,"path":57,"stem":58},"Entities","\u002Fdocs\u002Fschema\u002Fentities","docs\u002F3.schema\u002F1.entities",{"title":60,"path":61,"stem":62},"Views","\u002Fdocs\u002Fschema\u002Fviews","docs\u002F3.schema\u002F2.views",{"title":64,"path":65,"stem":66},"The identity card","\u002Fdocs\u002Fschema\u002Fcard","docs\u002F3.schema\u002F3.card",{"title":68,"path":69,"stem":70},"Standard Schema","\u002Fdocs\u002Fschema\u002Fstandard-schema","docs\u002F3.schema\u002F4.standard-schema",{"title":72,"path":73,"stem":74},"How a schema moves","\u002Fdocs\u002Fschema\u002Fevolution","docs\u002F3.schema\u002F5.evolution",{"title":76,"path":77,"stem":78,"children":79,"page":49},"Business","\u002Fdocs\u002Fbusiness","docs\u002F4.business",[80,84,88,92,96,100,104,108,112,116],{"title":81,"path":82,"stem":83},"Handlers","\u002Fdocs\u002Fbusiness\u002Fhandlers","docs\u002F4.business\u002F1.handlers",{"title":85,"path":86,"stem":87},"Ports","\u002Fdocs\u002Fbusiness\u002Fports","docs\u002F4.business\u002F10.ports",{"title":89,"path":90,"stem":91},"Presenters","\u002Fdocs\u002Fbusiness\u002Fpresenters","docs\u002F4.business\u002F2.presenters",{"title":93,"path":94,"stem":95},"Collectors","\u002Fdocs\u002Fbusiness\u002Fcollectors","docs\u002F4.business\u002F3.collectors",{"title":97,"path":98,"stem":99},"Errors","\u002Fdocs\u002Fbusiness\u002Ferrors","docs\u002F4.business\u002F4.errors",{"title":101,"path":102,"stem":103},"Seeds","\u002Fdocs\u002Fbusiness\u002Fseeds","docs\u002F4.business\u002F5.seeds",{"title":105,"path":106,"stem":107},"Facts","\u002Fdocs\u002Fbusiness\u002Ffacts","docs\u002F4.business\u002F6.facts",{"title":109,"path":110,"stem":111},"Storage","\u002Fdocs\u002Fbusiness\u002Fstorage","docs\u002F4.business\u002F7.storage",{"title":113,"path":114,"stem":115},"Writes that stand or fall as one","\u002Fdocs\u002Fbusiness\u002Ftogether","docs\u002F4.business\u002F8.together",{"title":117,"path":118,"stem":119},"Repositories","\u002Fdocs\u002Fbusiness\u002Frepositories","docs\u002F4.business\u002F9.repositories",{"title":121,"path":122,"stem":123,"children":124,"page":49},"Client","\u002Fdocs\u002Fclient","docs\u002F5.client",[125,129,133,137,141],{"title":126,"path":127,"stem":128},"Queries & commands","\u002Fdocs\u002Fclient\u002Fqueries-commands","docs\u002F5.client\u002F1.queries-commands",{"title":130,"path":131,"stem":132},"Forms","\u002Fdocs\u002Fclient\u002Fforms","docs\u002F5.client\u002F2.forms",{"title":134,"path":135,"stem":136},"Session","\u002Fdocs\u002Fclient\u002Fsession","docs\u002F5.client\u002F3.session",{"title":138,"path":139,"stem":140},"invoke","\u002Fdocs\u002Fclient\u002Finvoke","docs\u002F5.client\u002F4.invoke",{"title":142,"path":143,"stem":144},"Back-office","\u002Fdocs\u002Fclient\u002Fadmin","docs\u002F5.client\u002F5.admin",{"title":146,"path":147,"stem":148,"children":149,"page":49},"Infrastructure","\u002Fdocs\u002Finfra","docs\u002F6.infra",[150,154,158,162,166,170,174,178,182],{"title":151,"path":152,"stem":153},"The gradient","\u002Fdocs\u002Finfra\u002Fgradient","docs\u002F6.infra\u002F1.gradient",{"title":155,"path":156,"stem":157},"Surfaces — REST & GraphQL","\u002Fdocs\u002Finfra\u002Fsurfaces","docs\u002F6.infra\u002F2.surfaces",{"title":159,"path":160,"stem":161},"Deployment","\u002Fdocs\u002Finfra\u002Fdeployment","docs\u002F6.infra\u002F3.deployment",{"title":163,"path":164,"stem":165},"Hosts","\u002Fdocs\u002Finfra\u002Fhosts","docs\u002F6.infra\u002F4.hosts",{"title":167,"path":168,"stem":169},"Sources","\u002Fdocs\u002Finfra\u002Fsources","docs\u002F6.infra\u002F5.sources",{"title":171,"path":172,"stem":173},"Lifecycle","\u002Fdocs\u002Finfra\u002Flifecycle","docs\u002F6.infra\u002F6.lifecycle",{"title":175,"path":176,"stem":177},"Identity across Fronds","\u002Fdocs\u002Finfra\u002Fidentity","docs\u002F6.infra\u002F7.identity",{"title":179,"path":180,"stem":181},"Observability","\u002Fdocs\u002Finfra\u002Fobservability","docs\u002F6.infra\u002F8.observability",{"title":183,"path":184,"stem":185},"Testing","\u002Fdocs\u002Finfra\u002Ftesting","docs\u002F6.infra\u002F9.testing",{"id":187,"title":159,"body":188,"description":662,"extension":663,"meta":664,"navigation":510,"path":160,"seo":665,"stem":161,"__hash__":666},"docs_en\u002Fdocs\u002F6.infra\u002F3.deployment.md",{"type":189,"value":190,"toc":657},"minimark",[191,195,204,209,336,343,353,357,368,438,441,572,583,587,603,621,638,648,653],[192,193,159],"h1",{"id":194},"deployment",[196,197,198,199,203],"p",{},"A Fougere app is a Nitro app plus one thing Nitro doesn't know about: ",[200,201,202],"strong",{},"the Frond sources\nare read at runtime"," (scan, AST parse, jiti load). This page is what deploying this very\nsite taught — the checklist, then the Docker realization.",[205,206,208],"h2",{"id":207},"the-runtime-checklist","The runtime checklist",[210,211,212,226,272,292,298,308,322],"ol",{},[213,214,215,221,222,225],"li",{},[200,216,217],{},[218,219,220],"code",{},".output\u002F"," — the Nitro build (",[218,223,224],{},"pnpm build",").",[213,227,228,231,232,235,236,239,240,243,244,247,248,247,251,247,254,257,258,247,261,247,264,267,268,271],{},[200,229,230],{},"The Frond sources"," — next to ",[218,233,234],{},".output",": the scanner reads them from ",[218,237,238],{},"process.cwd()","\nat boot. ",[218,241,242],{},"fronds\u002F**\u002F*.ts",", and when the root itself is the Frond, the convention\ndirectories at the root (",[218,245,246],{},"entities\u002F",", ",[218,249,250],{},"handlers\u002F",[218,252,253],{},"presenters\u002F",[218,255,256],{},"collectors\u002F",",\n",[218,259,260],{},"seeds\u002F",[218,262,263],{},"services\u002F",[218,265,266],{},"repositories\u002F","). Copying ",[218,269,270],{},"fronds\u002F"," alone leaves a flat app with\nno domain and no error — see the boot log below.",[213,273,274,277,278,257,281,247,284,287,288,291],{},[200,275,276],{},"A resolvable node_modules for the fronds"," — those sources import ",[218,279,280],{},"@fougere\u002Fschema",[218,282,283],{},"@fougere\u002Fcore",[218,285,286],{},"better-auth","… by name. ",[218,289,290],{},"pnpm deploy --prod"," produces exactly that\nclosure; hand-picking packages is re-implementing a tracer, don't.",[213,293,294,297],{},[200,295,296],{},"A TS with the compiler JS API"," (5.9 — TS 7 has none) — dynamically imported by the\nscanner, invisible to Nitro's tracer.",[213,299,300,303,304,307],{},[200,301,302],{},"Complete lazy packages"," — Nitro's pnpm trace of ",[218,305,306],{},"jiti"," is partial (its babel\ntransform is loaded on demand). Replace it with the real package.",[213,309,310,316,317,321],{},[200,311,312,313],{},"A writable ",[218,314,315],{},".data\u002F"," — the SQLite file and Nuxt Content's index live there. It is\nthe ",[318,319,320],"em",{},"only"," thing to persist: one volume.",[213,323,324,327,328,331,332,335],{},[200,325,326],{},"Secrets by env"," — ",[218,329,330],{},"SITE_AUTH_SECRET"," (32+ chars), the public ",[218,333,334],{},"SITE_URL",".",[196,337,338,339,342],{},"The verification signal is the boot log — ",[200,340,341],{},"not"," a 200 on pages (SSR renders happily with\nzero fronds loaded):",[344,345,350],"pre",{"className":346,"code":348,"language":349},[347],"language-text","INF [boot:app] scanned 2 frond(s) in 467ms      ← this line\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[]}            ← and one envelope call\n","text",[218,351,348],{"__ignoreMap":352},"",[205,354,356],{"id":355},"docker","Docker",[196,358,359,360,363,364,367],{},"The site ships a working pair — ",[218,361,362],{},"site\u002FDockerfile"," + ",[218,365,366],{},"site\u002Fdocker-compose.yml",":",[344,369,373],{"className":370,"code":371,"language":372,"meta":352,"style":352},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","SITE_AUTH_SECRET='…32+ chars…' SITE_URL='https:\u002F\u002Fyour-domain' \\\n  docker compose -f site\u002Fdocker-compose.yml up -d --build\n","bash",[218,374,375,413],{"__ignoreMap":352},[376,377,380,383,387,391,395,397,400,402,404,407,409],"span",{"class":378,"line":379},"line",1,[376,381,330],{"class":382},"su5hD",[376,384,386],{"class":385},"smGrS","=",[376,388,390],{"class":389},"sjJ54","'",[376,392,394],{"class":393},"s_sjI","…32+ chars…",[376,396,390],{"class":389},[376,398,399],{"class":382}," SITE_URL",[376,401,386],{"class":385},[376,403,390],{"class":389},[376,405,406],{"class":393},"https:\u002F\u002Fyour-domain",[376,408,390],{"class":389},[376,410,412],{"class":411},"sbgvK"," \\\n",[376,414,416,419,422,426,429,432,435],{"class":378,"line":415},2,[376,417,418],{"class":393},"  docker",[376,420,421],{"class":393}," compose",[376,423,425],{"class":424},"stzsN"," -f",[376,427,428],{"class":393}," site\u002Fdocker-compose.yml",[376,430,431],{"class":393}," up",[376,433,434],{"class":424}," -d",[376,436,437],{"class":424}," --build\n",[196,439,440],{},"The Dockerfile realizes the checklist in two stages:",[344,442,446],{"className":443,"code":444,"language":445,"meta":352,"style":352},"language-dockerfile shiki shiki-themes material-theme-lighter github-light github-dark","# build stage: install workspace → build packages → build site\nRUN pnpm --filter \".\u002Fpackages\u002F**\" -r run build \\\n && pnpm --filter \"@fougere\u002Fsite\" run build\n# the frond-resolution closure, by pnpm itself:\nRUN pnpm --filter \"@fougere\u002Fsite\" deploy --prod --legacy \u002Fdeploy\n# complete jiti's partial trace + add TS 5.9\n\n# run stage: node:22-slim, non-root\nCOPY --from=build \u002Frepo\u002Fsite\u002F.output .\u002F.output\nCOPY --from=build \u002Frepo\u002Fsite\u002Ffronds  .\u002Ffronds\nCOPY --from=build \u002Fdeploy\u002Fnode_modules .\u002Fnode_modules\nVOLUME \u002Fapp\u002F.data\nCMD [\"node\", \".output\u002Fserver\u002Findex.mjs\"]\n","dockerfile",[218,447,448,454,469,481,487,499,505,512,518,527,535,543,552],{"__ignoreMap":352},[376,449,450],{"class":378,"line":379},[376,451,453],{"class":452},"sutJx","# build stage: install workspace → build packages → build site\n",[376,455,456,460,463,466],{"class":378,"line":415},[376,457,459],{"class":458},"sw1J6","RUN",[376,461,462],{"class":382}," pnpm --filter ",[376,464,465],{"class":393},"\".\u002Fpackages\u002F**\"",[376,467,468],{"class":382}," -r run build \\\n",[376,470,472,475,478],{"class":378,"line":471},3,[376,473,474],{"class":382}," && pnpm --filter ",[376,476,477],{"class":393},"\"@fougere\u002Fsite\"",[376,479,480],{"class":382}," run build\n",[376,482,484],{"class":378,"line":483},4,[376,485,486],{"class":452},"# the frond-resolution closure, by pnpm itself:\n",[376,488,490,492,494,496],{"class":378,"line":489},5,[376,491,459],{"class":458},[376,493,462],{"class":382},[376,495,477],{"class":393},[376,497,498],{"class":382}," deploy --prod --legacy \u002Fdeploy\n",[376,500,502],{"class":378,"line":501},6,[376,503,504],{"class":452},"# complete jiti's partial trace + add TS 5.9\n",[376,506,508],{"class":378,"line":507},7,[376,509,511],{"emptyLinePlaceholder":510},true,"\n",[376,513,515],{"class":378,"line":514},8,[376,516,517],{"class":452},"# run stage: node:22-slim, non-root\n",[376,519,521,524],{"class":378,"line":520},9,[376,522,523],{"class":458},"COPY",[376,525,526],{"class":382}," --from=build \u002Frepo\u002Fsite\u002F.output .\u002F.output\n",[376,528,530,532],{"class":378,"line":529},10,[376,531,523],{"class":458},[376,533,534],{"class":382}," --from=build \u002Frepo\u002Fsite\u002Ffronds  .\u002Ffronds\n",[376,536,538,540],{"class":378,"line":537},11,[376,539,523],{"class":458},[376,541,542],{"class":382}," --from=build \u002Fdeploy\u002Fnode_modules .\u002Fnode_modules\n",[376,544,546,549],{"class":378,"line":545},12,[376,547,548],{"class":458},"VOLUME",[376,550,551],{"class":382}," \u002Fapp\u002F.data\n",[376,553,555,558,561,564,566,569],{"class":378,"line":554},13,[376,556,557],{"class":458},"CMD",[376,559,560],{"class":382}," [",[376,562,563],{"class":393},"\"node\"",[376,565,247],{"class":382},[376,567,568],{"class":393},"\".output\u002Fserver\u002Findex.mjs\"",[376,570,571],{"class":382},"]\n",[196,573,574,575,578,579,582],{},"State lives in the ",[218,576,577],{},"site-data"," volume mounted at ",[218,580,581],{},"\u002Fapp\u002F.data"," — backups are one file copy.",[205,584,586],{"id":585},"splitting-in-production","Splitting in production",[196,588,589,590,593,594,597,598,602],{},"The same image model applies to a split: the host process serving a Frond needs items\n1–5 for ",[318,591,592],{},"its"," fronds, and the consumer declares it in ",[218,595,596],{},"remotes",". A dead host degrades to\ntyped 503s in pages (",[599,600,151],"a",{"href":601},"\u002Fdocs\u002Finfra\u002Fgradient#behaviour-after-a-split",");\nrestarting it is recovery — the consumer is never rebuilt.",[196,604,605,606,609,610,612,613,616,617,620],{},"Three things get called \"together\" and only one of them is required. The ",[200,607,608],{},"contract"," must\ntravel — the consumer has to know what the host serves. The ",[200,611,218],{}," need not: ",[218,614,615],{},"fougere sync"," reads the host's identity card and rebuilds a live schema locally, which is how a\nFrond written in another language is consumed at all. The ",[200,618,619],{},"ports"," must reach each other,\nand that is the only one a deployment decides.",[196,622,623,624,627,628,631,632,635,636,335],{},"The receiver binds loopback unless told otherwise, so the simplest split puts both\nprocesses on one machine: the same pod, or one image. ",[218,625,626],{},"hosts: ['0.0.0.0']"," widens it in\none line, and a receiver widened that way must be able to establish its caller or it will\nnot start. That is a sixth item for the list above: ",[218,629,630],{},"fougere keys"," once for the system,\n",[218,633,634],{},"fougere grant \u003Cfrond>"," for each Frond that calls, and three environment variables\ninjected at launch — see ",[599,637,175],{"href":176},[639,640,641],"blockquote",{},[196,642,643,644,647],{},"These steps are candidates for a future ",[218,645,646],{},"fougere build"," — a user should not have to\ndiscover them. Until then, this page is the contract.",[196,649,650,651,335],{},"What a deployed process does on the way out — draining the calls in flight before closing\nits connection — is one page over: ",[599,652,171],{"href":172},[654,655,656],"style",{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":352,"searchDepth":415,"depth":415,"links":658},[659,660,661],{"id":207,"depth":415,"text":208},{"id":355,"depth":415,"text":356},{"id":585,"depth":415,"text":586},"What a Fougere app needs at runtime — learned by deploying this site.","md",{},{"title":159,"description":662},"PIXhrV238O4I9BQ9jX-ktGhaSf0J6PuyQ1q21tTd4s0",1788560230879]