[{"data":1,"prerenderedAt":5855},["ShallowReactive",2],{"navigation":3,"\u002Fdeployment\u002Fvariables":553,"\u002Fdeployment\u002Fvariables-surround":5850},[4,22,64,89,215,503],{"title":5,"path":6,"stem":7,"children":8},"Introduction","\u002Fgetting-started","1.getting-started\u002F1.index",[9,10,14,18],{"title":5,"path":6,"stem":7},{"title":11,"path":12,"stem":13},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation",{"title":15,"path":16,"stem":17},"Key Ideas","\u002Fgetting-started\u002Fkey-ideas","1.getting-started\u002F3.key-ideas",{"title":19,"path":20,"stem":21},"Releases","\u002Fgetting-started\u002Freleases","1.getting-started\u002F4.releases",{"title":23,"path":24,"stem":25,"children":26},"Overview","\u002Fcore-concepts","2.core-concepts\u002F0.index",[27,28,32,36,40,44,48,52,56,60],{"title":23,"path":24,"stem":25},{"title":29,"path":30,"stem":31},"How It All Works","\u002Fcore-concepts\u002Farchitecture","2.core-concepts\u002F1.architecture",{"title":33,"path":34,"stem":35},"The Data Model","\u002Fcore-concepts\u002Fthe-data-model","2.core-concepts\u002F2.the-data-model",{"title":37,"path":38,"stem":39},"Layouts & Pages","\u002Fcore-concepts\u002Flayouts-and-pages","2.core-concepts\u002F3.layouts-and-pages",{"title":41,"path":42,"stem":43},"Dynamic Pages","\u002Fcore-concepts\u002Fdynamic-pages","2.core-concepts\u002F4.dynamic-pages",{"title":45,"path":46,"stem":47},"Components","\u002Fcore-concepts\u002Fcomponents","2.core-concepts\u002F5.components",{"title":49,"path":50,"stem":51},"Draft & Publish Workflow","\u002Fcore-concepts\u002Fdraft-and-publish","2.core-concepts\u002F6.draft-and-publish",{"title":53,"path":54,"stem":55},"The Admin Panel","\u002Fcore-concepts\u002Fadmin-panel","2.core-concepts\u002F7.admin-panel",{"title":57,"path":58,"stem":59},"Caching","\u002Fcore-concepts\u002Fcaching","2.core-concepts\u002F8.caching",{"title":61,"path":62,"stem":63},"Glossary","\u002Fcore-concepts\u002Fglossary","2.core-concepts\u002F9.glossary",{"title":23,"path":65,"stem":66,"children":67},"\u002Fguides","3.guides\u002F0.index",[68,69,73,77,81,85],{"title":23,"path":65,"stem":66},{"title":70,"path":71,"stem":72},"Your First Layout","\u002Fguides\u002Fyour-first-layout","3.guides\u002F1.your-first-layout",{"title":74,"path":75,"stem":76},"Your First Page Template","\u002Fguides\u002Fyour-first-page-template","3.guides\u002F2.your-first-page-template",{"title":78,"path":79,"stem":80},"Your First Component","\u002Fguides\u002Fyour-first-component","3.guides\u002F3.your-first-component",{"title":82,"path":83,"stem":84},"Alternative UI Variants","\u002Fguides\u002Falternative-ui-variants","3.guides\u002F4.alternative-ui-variants",{"title":86,"path":87,"stem":88},"Your First Dynamic Page","\u002Fguides\u002Fyour-first-dynamic-page","3.guides\u002F5.your-first-dynamic-page",{"title":23,"path":90,"stem":91,"children":92},"\u002Fapi","4.api\u002F0.index",[93,94,98,102,120,162,166,170,195,199,203,207,211],{"title":23,"path":90,"stem":91},{"title":95,"path":96,"stem":97},"Bundle Setup","\u002Fapi\u002Fbundle-setup","4.api\u002F02.bundle-setup",{"title":99,"path":100,"stem":101},"Configuration Reference","\u002Fapi\u002Fconfiguration","4.api\u002F03.configuration",{"title":103,"path":104,"stem":105,"children":106,"page":119},"Users","\u002Fapi\u002Fusers","4.api\u002F04.users",[107,111,115],{"title":108,"path":109,"stem":110},"Authentication","\u002Fapi\u002Fusers\u002Fauthentication","4.api\u002F04.users\u002F1.authentication",{"title":112,"path":113,"stem":114},"User Emails","\u002Fapi\u002Fusers\u002Fuser-emails","4.api\u002F04.users\u002F2.user-emails",{"title":116,"path":117,"stem":118},"Access Control","\u002Fapi\u002Fusers\u002Faccess-control","4.api\u002F04.users\u002F3.access-control",false,{"title":45,"path":121,"stem":122,"children":123,"page":119},"\u002Fapi\u002Fcomponents","4.api\u002F05.components",[124,128,149],{"title":125,"path":126,"stem":127},"Creating Components","\u002Fapi\u002Fcomponents\u002Fcreating-components","4.api\u002F05.components\u002F1.creating-components",{"title":129,"path":130,"stem":131,"children":132,"page":119},"Annotations","\u002Fapi\u002Fcomponents\u002Fannotations","4.api\u002F05.components\u002F2.annotations",[133,137,141,145],{"title":134,"path":135,"stem":136},"Publishable","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fpublishable","4.api\u002F05.components\u002F2.annotations\u002F1.publishable",{"title":138,"path":139,"stem":140},"Uploadable","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fuploadable","4.api\u002F05.components\u002F2.annotations\u002F2.uploadable",{"title":142,"path":143,"stem":144},"Timestamped","\u002Fapi\u002Fcomponents\u002Fannotations\u002Ftimestamped","4.api\u002F05.components\u002F2.annotations\u002F3.timestamped",{"title":146,"path":147,"stem":148},"Explicit Allow Only","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fexplicit-allow-only","4.api\u002F05.components\u002F2.annotations\u002F4.explicit-allow-only",{"title":150,"path":151,"stem":152,"children":153,"page":119},"Built Ins","\u002Fapi\u002Fcomponents\u002Fbuilt-ins","4.api\u002F05.components\u002F3.built-ins",[154,158],{"title":155,"path":156,"stem":157},"Collection Component","\u002Fapi\u002Fcomponents\u002Fbuilt-ins\u002Fcollection-component","4.api\u002F05.components\u002F3.built-ins\u002F1.collection-component",{"title":159,"path":160,"stem":161},"Form Component","\u002Fapi\u002Fcomponents\u002Fbuilt-ins\u002Fform-component","4.api\u002F05.components\u002F3.built-ins\u002F2.form-component",{"title":163,"path":164,"stem":165},"Page Data & Nested Pages","\u002Fapi\u002Fdynamic-pages","4.api\u002F06.dynamic-pages",{"title":167,"path":168,"stem":169},"Routes","\u002Fapi\u002Froutes","4.api\u002F07.routes",{"title":171,"path":172,"stem":173,"children":174,"page":119},"Data Fixtures","\u002Fapi\u002Fdata-fixtures","4.api\u002F08.data-fixtures",[175,179,183,187,191],{"title":176,"path":177,"stem":178},"Getting Started with Fixtures","\u002Fapi\u002Fdata-fixtures\u002Fgetting-started","4.api\u002F08.data-fixtures\u002F1.getting-started",{"title":180,"path":181,"stem":182},"Builder Reference","\u002Fapi\u002Fdata-fixtures\u002Fbuilder-reference","4.api\u002F08.data-fixtures\u002F2.builder-reference",{"title":184,"path":185,"stem":186},"Seeding Content","\u002Fapi\u002Fdata-fixtures\u002Fseeding-content","4.api\u002F08.data-fixtures\u002F3.seeding-content",{"title":188,"path":189,"stem":190},"Appending & Multiple Scaffolds","\u002Fapi\u002Fdata-fixtures\u002Fappending","4.api\u002F08.data-fixtures\u002F4.appending",{"title":192,"path":193,"stem":194},"Generating Fixtures","\u002Fapi\u002Fdata-fixtures\u002Fgenerating-fixtures","4.api\u002F08.data-fixtures\u002F5.generating-fixtures",{"title":196,"path":197,"stem":198},"Real-Time (Mercure)","\u002Fapi\u002Freal-time","4.api\u002F09.real-time",{"title":200,"path":201,"stem":202},"HTTP Caching","\u002Fapi\u002Fhttp-caching","4.api\u002F10.http-caching",{"title":204,"path":205,"stem":206},"Orphaned Data","\u002Fapi\u002Forphaned-data","4.api\u002F11.orphaned-data",{"title":208,"path":209,"stem":210},"Console Commands","\u002Fapi\u002Fconsole-commands","4.api\u002F12.console-commands",{"title":212,"path":213,"stem":214},"Debugging & Errors","\u002Fapi\u002Fdebugging","4.api\u002F13.debugging",{"title":23,"path":216,"stem":217,"children":218},"\u002Fnuxt-module","5.nuxt-module\u002F0.index",[219,220,224,237,257,286,307,430,464,467,495,499],{"title":23,"path":216,"stem":217},{"title":221,"path":222,"stem":223},"Module Setup","\u002Fnuxt-module\u002Fmodule-setup","5.nuxt-module\u002F01.module-setup",{"title":225,"path":226,"stem":227,"children":228,"page":119},"Configuration","\u002Fnuxt-module\u002Fconfiguration","5.nuxt-module\u002F02.configuration",[229,233],{"title":230,"path":231,"stem":232},"Nuxt Config","\u002Fnuxt-module\u002Fconfiguration\u002Fnuxt-config","5.nuxt-module\u002F02.configuration\u002F1.nuxt-config",{"title":234,"path":235,"stem":236},"Site Config & SEO","\u002Fnuxt-module\u002Fconfiguration\u002Fsite-config-and-seo","5.nuxt-module\u002F02.configuration\u002F2.site-config-and-seo",{"title":238,"path":239,"stem":240,"children":241,"page":119},"Building Your Ui","\u002Fnuxt-module\u002Fbuilding-your-ui","5.nuxt-module\u002F03.building-your-ui",[242,246,250,253],{"title":243,"path":244,"stem":245},"Layouts","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-layouts","5.nuxt-module\u002F03.building-your-ui\u002F1.creating-layouts",{"title":247,"path":248,"stem":249},"Page Templates","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-page-templates","5.nuxt-module\u002F03.building-your-ui\u002F2.creating-page-templates",{"title":45,"path":251,"stem":252},"\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-components","5.nuxt-module\u002F03.building-your-ui\u002F3.creating-components",{"title":254,"path":255,"stem":256},"CLI Generator","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcwa-cli","5.nuxt-module\u002F03.building-your-ui\u002F4.cwa-cli",{"title":258,"path":259,"stem":260,"children":261,"page":119},"Component Helpers","\u002Fnuxt-module\u002Fcomponent-helpers","5.nuxt-module\u002F04.component-helpers",[262,266,270,274,278,282],{"title":263,"path":264,"stem":265},"Images & Media","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fimages-and-uploads","5.nuxt-module\u002F04.component-helpers\u002F1.images-and-uploads",{"title":267,"path":268,"stem":269},"Collections & Pagination","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fcollections-and-pagination","5.nuxt-module\u002F04.component-helpers\u002F2.collections-and-pagination",{"title":271,"path":272,"stem":273},"HTML Content","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fhtml-content","5.nuxt-module\u002F04.component-helpers\u002F3.html-content",{"title":275,"path":276,"stem":277},"Rich-Text Editor","\u002Fnuxt-module\u002Fcomponent-helpers\u002Frich-text-editor","5.nuxt-module\u002F04.component-helpers\u002F4.rich-text-editor",{"title":279,"path":280,"stem":281},"Real-Time Updates","\u002Fnuxt-module\u002Fcomponent-helpers\u002Freal-time-updates","5.nuxt-module\u002F04.component-helpers\u002F5.real-time-updates",{"title":283,"path":284,"stem":285},"Forms","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fforms","5.nuxt-module\u002F04.component-helpers\u002F6.forms",{"title":287,"path":288,"stem":289,"children":290,"page":119},"Cwa Components","\u002Fnuxt-module\u002Fcwa-components","5.nuxt-module\u002F05.cwa-components",[291,295,299,303],{"title":292,"path":293,"stem":294},"\u003CCwaComponentGroup \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-component-group","5.nuxt-module\u002F05.cwa-components\u002F1.cwa-component-group",{"title":296,"path":297,"stem":298},"\u003CCwaPage \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-page","5.nuxt-module\u002F05.cwa-components\u002F2.cwa-page",{"title":300,"path":301,"stem":302},"\u003CCwaLink \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-link","5.nuxt-module\u002F05.cwa-components\u002F3.cwa-link",{"title":304,"path":305,"stem":306},"\u003CCwaDefaultLayout \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-default-layout","5.nuxt-module\u002F05.cwa-components\u002F5.cwa-default-layout",{"title":308,"path":309,"stem":310,"children":311,"page":119},"Composables","\u002Fnuxt-module\u002Fcomposables","5.nuxt-module\u002F06.composables",[312,315,323,348,368,385,405],{"title":23,"path":313,"stem":314},"\u002Fnuxt-module\u002Fcomposables\u002Foverview","5.nuxt-module\u002F06.composables\u002F0.overview",{"title":316,"path":317,"stem":318,"children":319,"page":119},"Layout","\u002Fnuxt-module\u002Fcomposables\u002Flayout","5.nuxt-module\u002F06.composables\u002F1.layout",[320],{"title":316,"path":321,"stem":322},"\u002Fnuxt-module\u002Fcomposables\u002Flayout\u002Fuse-cwa-layout","5.nuxt-module\u002F06.composables\u002F1.layout\u002F1.use-cwa-layout",{"title":324,"path":325,"stem":326,"children":327,"page":119},"Component","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent","5.nuxt-module\u002F06.composables\u002F2.component",[328,332,336,340,344],{"title":329,"path":330,"stem":331},"Component (recommended)","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-component","5.nuxt-module\u002F06.composables\u002F2.component\u002F1.use-cwa-component",{"title":333,"path":334,"stem":335},"Resource","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-resource","5.nuxt-module\u002F06.composables\u002F2.component\u002F2.use-cwa-resource",{"title":337,"path":338,"stem":339},"File Field","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-file-field","5.nuxt-module\u002F06.composables\u002F2.component\u002F3.use-cwa-file-field",{"title":341,"path":342,"stem":343},"Collection Pagination","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-collection-pagination","5.nuxt-module\u002F06.composables\u002F2.component\u002F4.use-cwa-collection-pagination",{"title":345,"path":346,"stem":347},"Collection Resource (deprecated)","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-collection-resource","5.nuxt-module\u002F06.composables\u002F2.component\u002F9.use-cwa-collection-resource",{"title":349,"path":350,"stem":351,"children":352,"page":119},"Form","\u002Fnuxt-module\u002Fcomposables\u002Fform","5.nuxt-module\u002F06.composables\u002F3.form",[353,356,360,364],{"title":349,"path":354,"stem":355},"\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form","5.nuxt-module\u002F06.composables\u002F3.form\u002F1.use-cwa-form",{"title":357,"path":358,"stem":359},"Form Input","\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form-input","5.nuxt-module\u002F06.composables\u002F3.form\u002F2.use-cwa-form-input",{"title":361,"path":362,"stem":363},"Form Repeated","\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form-repeated","5.nuxt-module\u002F06.composables\u002F3.form\u002F3.use-cwa-form-repeated",{"title":365,"path":366,"stem":367},"Form Collection","\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form-collection","5.nuxt-module\u002F06.composables\u002F3.form\u002F4.use-cwa-form-collection",{"title":369,"path":370,"stem":371,"children":372,"page":119},"Admin Manager","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager","5.nuxt-module\u002F06.composables\u002F4.admin-manager",[373,377,381],{"title":374,"path":375,"stem":376},"Manager Tab","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-resource-manager-tab","5.nuxt-module\u002F06.composables\u002F4.admin-manager\u002F1.use-cwa-resource-manager-tab",{"title":378,"path":379,"stem":380},"Resource Model","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-resource-model","5.nuxt-module\u002F06.composables\u002F4.admin-manager\u002F2.use-cwa-resource-model",{"title":382,"path":383,"stem":384},"Resource Upload","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-resource-upload","5.nuxt-module\u002F06.composables\u002F4.admin-manager\u002F3.use-cwa-resource-upload",{"title":386,"path":387,"stem":388,"children":389,"page":119},"Utilities","\u002Fnuxt-module\u002Fcomposables\u002Futilities","5.nuxt-module\u002F06.composables\u002F5.utilities",[390,394,398,402],{"title":391,"path":392,"stem":393},"Resource Endpoint","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-cwa-resource-endpoint","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F1.use-cwa-resource-endpoint",{"title":395,"path":396,"stem":397},"Query Model","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-query-bound-model","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F2.use-query-bound-model",{"title":399,"path":400,"stem":401},"Resource Route","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-cwa-resource-route","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F3.use-cwa-resource-route",{"title":271,"path":403,"stem":404},"\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-html-content","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F4.use-html-content",{"title":406,"path":407,"stem":408,"children":409,"page":119},"Auth","\u002Fnuxt-module\u002Fcomposables\u002Fauth","5.nuxt-module\u002F06.composables\u002F6.auth",[410,414,418,422,426],{"title":411,"path":412,"stem":413},"Login","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-login","5.nuxt-module\u002F06.composables\u002F6.auth\u002F1.use-login",{"title":415,"path":416,"stem":417},"Forgot Password","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-forgot-password","5.nuxt-module\u002F06.composables\u002F6.auth\u002F2.use-forgot-password",{"title":419,"path":420,"stem":421},"Reset Password","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-reset-password","5.nuxt-module\u002F06.composables\u002F6.auth\u002F3.use-reset-password",{"title":423,"path":424,"stem":425},"Verify Email","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-verify-email","5.nuxt-module\u002F06.composables\u002F6.auth\u002F4.use-verify-email",{"title":427,"path":428,"stem":429},"Resend Verify Email","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-resend-verify-email","5.nuxt-module\u002F06.composables\u002F6.auth\u002F5.use-resend-verify-email",{"title":431,"path":432,"stem":433,"children":434,"page":119},"Cwa Api","\u002Fnuxt-module\u002Fcwa-api","5.nuxt-module\u002F07.cwa-api",[435,438,442,446,449,452,456,460],{"title":23,"path":436,"stem":437},"\u002Fnuxt-module\u002Fcwa-api\u002Foverview","5.nuxt-module\u002F07.cwa-api\u002F1.overview",{"title":439,"path":440,"stem":441},"Resources","\u002Fnuxt-module\u002Fcwa-api\u002Fresources","5.nuxt-module\u002F07.cwa-api\u002F2.resources",{"title":443,"path":444,"stem":445},"Resources Manager","\u002Fnuxt-module\u002Fcwa-api\u002Fresources-manager","5.nuxt-module\u002F07.cwa-api\u002F3.resources-manager",{"title":406,"path":447,"stem":448},"\u002Fnuxt-module\u002Fcwa-api\u002Fauth","5.nuxt-module\u002F07.cwa-api\u002F4.auth",{"title":283,"path":450,"stem":451},"\u002Fnuxt-module\u002Fcwa-api\u002Fforms","5.nuxt-module\u002F07.cwa-api\u002F5.forms",{"title":453,"path":454,"stem":455},"Admin","\u002Fnuxt-module\u002Fcwa-api\u002Fadmin","5.nuxt-module\u002F07.cwa-api\u002F6.admin",{"title":457,"path":458,"stem":459},"Site Config","\u002Fnuxt-module\u002Fcwa-api\u002Fsite-config","5.nuxt-module\u002F07.cwa-api\u002F7.site-config",{"title":461,"path":462,"stem":463},"Orphaned Resources","\u002Fnuxt-module\u002Fcwa-api\u002Forphaned-resources","5.nuxt-module\u002F07.cwa-api\u002F8.orphaned-resources",{"title":108,"path":465,"stem":466},"\u002Fnuxt-module\u002Fauthentication","5.nuxt-module\u002F08.authentication",{"title":468,"path":469,"stem":470,"children":471,"page":119},"Cwa Layer","\u002Fnuxt-module\u002Fcwa-layer","5.nuxt-module\u002F9.cwa-layer",[472,475,479,483,487,491],{"title":23,"path":473,"stem":474},"\u002Fnuxt-module\u002Fcwa-layer\u002Foverview","5.nuxt-module\u002F9.cwa-layer\u002F1.overview",{"title":476,"path":477,"stem":478},"Auth Pages","\u002Fnuxt-module\u002Fcwa-layer\u002Fauth-pages","5.nuxt-module\u002F9.cwa-layer\u002F2.auth-pages",{"title":480,"path":481,"stem":482},"Admin Panel","\u002Fnuxt-module\u002Fcwa-layer\u002Fadmin-panel","5.nuxt-module\u002F9.cwa-layer\u002F3.admin-panel",{"title":484,"path":485,"stem":486},"Styling","\u002Fnuxt-module\u002Fcwa-layer\u002Fstyling","5.nuxt-module\u002F9.cwa-layer\u002F4.styling",{"title":488,"path":489,"stem":490},"Date Pickers","\u002Fnuxt-module\u002Fcwa-layer\u002Fdate-pickers","5.nuxt-module\u002F9.cwa-layer\u002F5.date-pickers",{"title":492,"path":493,"stem":494},"Maintenance","\u002Fnuxt-module\u002Fcwa-layer\u002Fmaintenance","5.nuxt-module\u002F9.cwa-layer\u002F6.maintenance",{"title":496,"path":497,"stem":498},"PWA & Offline","\u002Fnuxt-module\u002Fpwa","5.nuxt-module\u002F91.pwa",{"title":500,"path":501,"stem":502},"Page Caching","\u002Fnuxt-module\u002Fpage-caching","5.nuxt-module\u002F92.page-caching",{"title":504,"path":505,"stem":506,"children":507},"Deploying a CWA Site","\u002Fdeployment","6.deployment\u002F0.index",[508,509,513,517,521,525,529,533,537,541,545,549],{"title":504,"path":505,"stem":506},{"title":510,"path":511,"stem":512},"Docker","\u002Fdeployment\u002Fdocker","6.deployment\u002F1.docker",{"title":514,"path":515,"stem":516},"Kubernetes & Helm","\u002Fdeployment\u002Fkubernetes","6.deployment\u002F2.kubernetes",{"title":518,"path":519,"stem":520},"CI\u002FCD","\u002Fdeployment\u002Fci-cd","6.deployment\u002F3.ci-cd",{"title":522,"path":523,"stem":524},"CI\u002FCD Variables","\u002Fdeployment\u002Fvariables","6.deployment\u002F4.variables",{"title":526,"path":527,"stem":528},"Cloudflare","\u002Fdeployment\u002Fcloudflare","6.deployment\u002F5.cloudflare",{"title":530,"path":531,"stem":532},"Origin Protection","\u002Fdeployment\u002Forigin-protection","6.deployment\u002F5.origin-protection",{"title":534,"path":535,"stem":536},"Load Testing","\u002Fdeployment\u002Fload-testing","6.deployment\u002F6.load-testing",{"title":538,"path":539,"stem":540},"Performance Audit","\u002Fdeployment\u002Fperformance-audit","6.deployment\u002F6.performance-audit",{"title":542,"path":543,"stem":544},"Reading Lighthouse Scores","\u002Fdeployment\u002Freading-lighthouse-scores","6.deployment\u002F6.reading-lighthouse-scores",{"title":546,"path":547,"stem":548},"Faster Functional Tests","\u002Fdeployment\u002Ffaster-functional-tests","6.deployment\u002F7.faster-functional-tests",{"title":550,"path":551,"stem":552},"Troubleshooting","\u002Fdeployment\u002Ftroubleshooting","6.deployment\u002F7.troubleshooting",{"id":554,"title":522,"badge":555,"body":556,"description":5845,"extension":5846,"links":555,"meta":5847,"navigation":639,"path":523,"seo":5848,"stem":524,"__hash__":5849},"docs\u002F6.deployment\u002F4.variables.md",null,{"type":557,"value":558,"toc":5813},"minimark",[559,574,593,598,623,626,777,782,786,789,879,906,923,926,1218,1242,1262,1265,1378,1408,1419,1434,1448,1451,1632,1641,1677,1680,1762,1775,1780,1795,1815,1818,1867,1881,1884,2175,2203,2220,2231,2245,2269,2273,2297,2304,2385,2388,2409,2413,2447,2476,2480,2490,2494,2735,2746,2749,2765,2881,2884,2892,2898,2925,2945,2968,2977,2981,3181,3196,3199,3205,3209,3223,3304,3307,3328,3349,3353,3363,3395,3665,3673,3701,3716,3720,3726,3880,3907,3924,3934,3951,3955,3976,4143,4149,4153,4159,4272,4278,4282,4288,4555,4559,4808,4811,4837,4924,4945,4964,4968,4984,5117,5126,5129,5181,5291,5309,5334,5348,5362,5366,5381,5387,5395,5453,5464,5570,5596,5621,5649,5653,5681,5684,5704,5708,5720,5773,5795,5809],[560,561,562,563,567,568,573],"p",{},"The template's pipeline is configured with CI\u002FCD variables. Set them in ",[564,565,566],"strong",{},"Settings → CI\u002FCD → Variables"," in GitLab. On GitHub, set them as Actions variables, except the ones the workflows map from secrets (see ",[569,570,572],"a",{"href":571},"\u002Fdeployment\u002Fci-cd#github-actions","CI\u002FCD → GitHub Actions",").",[560,575,576,577,581,582,585,586,588,589,592],{},"If you deploy with the template's pipeline, you only set variables. ",[578,579,580],"code",{},"bin\u002Fdevops\u002Fk8s.sh"," turns them into the chart's values and runs Helm. The ",[564,583,584],{},"Helm value"," column below says where each one ends up, so you can read it alongside ",[569,587,514],{"href":515},". A ",[578,590,591],{},"—"," means the variable steers the pipeline and sets no chart value.",[594,595,597],"h2",{"id":596},"minimum-for-a-new-site","Minimum for a new site",[560,599,600,601,604,605,604,608,604,611,614,615,618,619,622],{},"The deploy works out the settings that would otherwise be the same on every site: ",[578,602,603],{},"CWA_CI_INGRESS_ENABLED",", ",[578,606,607],{},"CORS_ALLOW_ORIGIN",[578,609,610],{},"TRUSTED_HOSTS",[578,612,613],{},"MERCURE_CORS_ORIGIN"," and ",[578,616,617],{},"DATABASE_SSL_MODE",". A variable you set still wins. The host-based defaults use the deploy's own hostname, plus ",[578,620,621],{},"KUBE_INGRESS_ALIAS_DOMAINS"," in production, so review apps and staging get their own instead of production's.",[560,624,625],{},"So a new site sets only what is its own:",[627,628,631,664,675,695,717,731,747,759],"ul",{"className":629},[630],"contains-task-list",[632,633,636,641,642,641,645,648,649,614,652,654,655,658,659,663],"li",{"className":634},[635],"task-list-item",[637,638],"input",{"disabled":639,"type":640},true,"checkbox"," ",[564,643,644],{},"The cluster connection:",[578,646,647],{},"KUBE_CONTEXT"," (GitLab agent context), or on GitHub the ",[578,650,651],{},"KUBECONFIG",[578,653,647],{}," secrets and the ",[578,656,657],{},"KUBE_NAMESPACE_PRODUCTION"," secret. See ",[569,660,662],{"href":661},"#kubernetes-and-hostnames","Kubernetes and hostnames",".",[632,665,667,641,669,641,672,663],{"className":666},[635],[637,668],{"disabled":639,"type":640},[564,670,671],{},"The domain:",[578,673,674],{},"KUBE_INGRESS_BASE_DOMAIN",[632,676,678,641,680,641,683,686,687,690,691,663],{"className":677},[635],[637,679],{"disabled":639,"type":640},[564,681,682],{},"The database:",[578,684,685],{},"DATABASE_URL"," with ",[578,688,689],{},"CWA_CI_POSTGRES_ENABLED=\"false\"",". See ",[569,692,694],{"href":693},"#database","Database",[632,696,698,641,700,641,703,604,706,709,710,690,713,663],{"className":697},[635],[637,699],{"disabled":639,"type":640},[564,701,702],{},"The media bucket:",[578,704,705],{},"GCLOUD_JSON",[578,707,708],{},"GCLOUD_BUCKET"," and optionally ",[578,711,712],{},"GCLOUD_PUBLIC_URL",[569,714,716],{"href":715},"#media-storage","Media storage",[632,718,720,641,722,641,725,614,728,663],{"className":719},[635],[637,721],{"disabled":639,"type":640},[564,723,724],{},"The mailer:",[578,726,727],{},"MAILER_DSN",[578,729,730],{},"MAILER_EMAIL",[632,732,734,641,736,739,740,690,743,663],{"className":733},[635],[637,735],{"disabled":639,"type":640},[564,737,738],{},"The first admin:"," at least ",[578,741,742],{},"ADMIN_PASSWORD",[569,744,746],{"href":745},"#application","Application",[632,748,750,641,752,641,755,758],{"className":749},[635],[637,751],{"disabled":639,"type":640},[564,753,754],{},"A trusted certificate:",[578,756,757],{},"CWA_CI_CLUSTER_ISSUER=letsencrypt-prod",", once production's domain points at the cluster.",[632,760,762,764,765,769,770,604,773,776],{"className":761},[635],[637,763],{"disabled":639,"type":640}," Recommended for production: the ",[569,766,768],{"href":767},"#secrets","secrets"," (",[578,771,772],{},"APP_SECRET",[578,774,775],{},"MERCURE_JWT_SECRET"," and the JWT key pair). They're optional: a deploy keeps the release's live values, and generates them only for a new release.",[560,778,779,781],{},[569,780,504],{"href":505}," walks through these in order.",[594,783,785],{"id":784},"naming-rules","Naming rules",[560,787,788],{},"The variables follow three naming rules:",[627,790,791,810,827],{},[632,792,793,798,799,802,803,806,807,663],{},[564,794,795],{},[578,796,797],{},"CWA_CI_*"," are the template's own pipeline and deploy switches, such as ",[578,800,801],{},"CWA_CI_STAGING_ENABLED"," or ",[578,804,805],{},"CWA_CI_PWA_AUTOSCALE_MIN",". The sizing of the php pod is ",[578,808,809],{},"CWA_CI_API_*",[632,811,812,614,817,822,823,663],{},[564,813,814],{},[578,815,816],{},"CWA_API_*",[564,818,819],{},[578,820,821],{},"NUXT_*"," are settings the containers read at runtime, passed through by prefix. See ",[569,824,826],{"href":825},"#site-settings","Site settings",[632,828,829,830,604,832,604,835,604,837,604,840,604,842,604,845,604,848,604,851,604,853,604,855,604,857,604,859,604,862,604,865,604,868,871,872,614,875,878],{},"Site values, secrets and names set by the platform keep their own names: ",[578,831,685],{},[578,833,834],{},"POSTGRES_*",[578,836,772],{},[578,838,839],{},"JWT_*",[578,841,775],{},[578,843,844],{},"MAILER_*",[578,846,847],{},"GCLOUD_*",[578,849,850],{},"ADMIN_*",[578,852,607],{},[578,854,610],{},[578,856,613],{},[578,858,617],{},[578,860,861],{},"CADDY_CACHE_CDN_CONFIG",[578,863,864],{},"GITHUB_TOKEN",[578,866,867],{},"KUBERNETES_VERSION",[578,869,870],{},"HELM_VERSION",", and the ",[578,873,874],{},"KUBE_*",[578,876,877],{},"CI_*"," variables.",[880,881,884],"callout",{"icon":882,"color":883},"i-heroicons-exclamation-triangle","warning",[560,885,886,889,890,604,893,604,896,899,900,663],{},[564,887,888],{},"Template 2.0.0-alpha.16 renamed the switches, and the old names are no longer read."," A project that still sets ",[578,891,892],{},"POSTGRESQL_ENABLED",[578,894,895],{},"CLUSTER_ISSUER",[578,897,898],{},"ENABLE_DATABASE_FIXTURES"," or another old name gets the default instead, without a warning. The full list is in ",[569,901,905],{"href":902,"rel":903},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Freleases\u002Ftag\u002Fv2.0.0-alpha.16",[904],"nofollow","the template 2.0.0-alpha.16 release notes",[880,907,909],{"icon":908},"i-heroicons-information-circle",[560,910,911,914,915,918,919,922],{},[564,912,913],{},"GitLab environment scopes:"," the staging job runs under ",[578,916,917],{},"environment: production",", so a variable scoped to ",[578,920,921],{},"production"," reaches staging too, as well as canary and production.",[594,924,662],{"id":925},"kubernetes-and-hostnames",[927,928,929,947],"table",{},[930,931,932],"thead",{},[933,934,935,939,942,944],"tr",{},[936,937,938],"th",{},"Variable",[936,940,941],{},"Default",[936,943,584],{},[936,945,946],{},"Notes",[948,949,950,974,993,1026,1051,1074,1099,1119,1136,1159,1186,1202],"tbody",{},[933,951,952,957,962,964],{},[953,954,955],"td",{},[578,956,647],{},[953,958,959],{},[564,960,961],{},"required",[953,963,591],{},[953,965,966,967,970,971,973],{},"GitLab agent context, e.g. ",[578,968,969],{},"my-group\u002Fmy-project:my-agent",". On GitHub it's a secret, used with the ",[578,972,651],{}," secret",[933,975,976,981,986,988],{},[953,977,978],{},[578,979,980],{},"KUBE_NAMESPACE",[953,982,983],{},[578,984,985],{},"$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG",[953,987,591],{},[953,989,990,991,973],{},"The pre-created namespace for this environment. On GitHub, staging and production use the ",[578,992,657],{},[933,994,995,999,1003,1012],{},[953,996,997],{},[578,998,674],{},[953,1000,1001],{},[564,1002,961],{},[953,1004,1005,1008,1009],{},[578,1006,1007],{},"ingress.hosts",", through ",[578,1010,1011],{},"DOMAIN",[953,1013,1014,1015,1018,1019,1022,1023],{},"Base domain for ingress URLs, e.g. ",[578,1016,1017],{},"k8s.example.com",". Production is served at it, staging at ",[578,1020,1021],{},"staging.\u003Cit>"," and review apps at ",[578,1024,1025],{},"\u003Cbranch>-review.\u003Cit>",[933,1027,1028,1033,1036,1042],{},[953,1029,1030],{},[578,1031,1032],{},"CI_ENVIRONMENT_URL",[953,1034,1035],{},"set by GitLab",[953,1037,1038,1008,1040],{},[578,1039,1007],{},[578,1041,1011],{},[953,1043,1044,1045,1047,1048,1050],{},"Full URL of this environment. ",[578,1046,1011],{}," is its host. Required unless ",[578,1049,1011],{}," is set; without either, the deploy fails",[933,1052,1053,1058,1063,1071],{},[953,1054,1055],{},[578,1056,1057],{},"CWA_CI_CLUSTER_ISSUER",[953,1059,1060],{},[578,1061,1062],{},"letsencrypt-staging",[953,1064,1065,641,1068],{},[578,1066,1067],{},"ingress.annotations",[578,1069,1070],{},"cert-manager.io\u002Fcluster-issuer",[953,1072,1073],{},"The cert-manager ClusterIssuer. See below",[933,1075,1076,1080,1082,1092],{},[953,1077,1078],{},[578,1079,621],{},[953,1081,591],{},[953,1083,1084,641,1086,604,1089],{},[578,1085,1067],{},[578,1087,1088],{},"server-alias",[578,1090,1091],{},"ingress.tls",[953,1093,1094,1095],{},"Comma-separated extra domains for production. Production's certificate covers them. See ",[569,1096,1098],{"href":1097},"\u002Fdeployment\u002Fci-cd#changing-a-live-sites-hostnames","Changing a live site's hostnames",[933,1100,1101,1106,1111,1116],{},[953,1102,1103],{},[578,1104,1105],{},"CWA_CI_TLS_SECRET_NAME",[953,1107,1108],{},[578,1109,1110],{},"letsencrypt-cert",[953,1112,1113],{},[578,1114,1115],{},"ingress.tls[].secretName",[953,1117,1118],{},"Prefix of the ingress's TLS secret. You don't need to change it when hostnames change",[933,1120,1121,1126,1131,1133],{},[953,1122,1123],{},[578,1124,1125],{},"CWA_CI_TLS_CERTIFICATE_TIMEOUT",[953,1127,1128],{},[578,1129,1130],{},"600s",[953,1132,591],{},[953,1134,1135],{},"How long a deploy waits for a new certificate when production's hostnames change",[933,1137,1138,1142,1147,1152],{},[953,1139,1140],{},[578,1141,603],{},[953,1143,1144],{},[578,1145,1146],{},"\"true\"",[953,1148,1149],{},[578,1150,1151],{},"ingress.enabled",[953,1153,1154,1155,1158],{},"Set ",[578,1156,1157],{},"\"false\""," to deploy without an ingress",[933,1160,1161,1166,1170,1177],{},[953,1162,1163],{},[578,1164,1165],{},"CWA_CI_WWW_REDIRECT",[953,1167,1168],{},[578,1169,1157],{},[953,1171,1172,641,1174],{},[578,1173,1067],{},[578,1175,1176],{},"from-to-www-redirect",[953,1178,1179,1180,614,1183],{},"Sets ingress-nginx's annotation, which redirects between ",[578,1181,1182],{},"example.com",[578,1184,1185],{},"www.example.com",[933,1187,1188,1192,1197,1199],{},[953,1189,1190],{},[578,1191,867],{},[953,1193,1194],{},[578,1195,1196],{},"1.31.0",[953,1198,591],{},[953,1200,1201],{},"The kubectl version the deploy jobs install",[933,1203,1204,1208,1213,1215],{},[953,1205,1206],{},[578,1207,870],{},[953,1209,1210],{},[578,1211,1212],{},"3.19.0",[953,1214,591],{},[953,1216,1217],{},"The Helm version the deploy jobs install",[560,1219,1220,1224,1225,1227,1228,1231,1232,1238,1239,573],{},[564,1221,1222],{},[578,1223,1057],{}," defaults to ",[578,1226,1062],{}," (set by ",[578,1229,1230],{},"setup.sh","), whose certificates browsers don't trust. ",[564,1233,1154,1234,1237],{},[578,1235,1236],{},"letsencrypt-prod"," for production"," once the domain points at the cluster. Until then, a misconfigured domain fails against the staging issuer's much higher limits instead of using up Let's Encrypt's production limits. An empty value turns cert-manager off. Keep the variable on every production site. Both issuers must exist in the cluster under these names (see ",[569,1240,504],{"href":1241},"\u002Fdeployment#prepare-the-cluster",[560,1243,1244,1245,614,1248,1251,1252,1254,1255,573],{},"On GitHub, the workflows' setup actions read ",[578,1246,1247],{},"vars.KUBERNETES_VERSION",[578,1249,1250],{},"vars.HELM_VERSION",". On GitLab, ",[578,1253,1230],{}," sets the defaults only when no CI variable sets them (",[569,1256,1259],{"href":1257,"rel":1258},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F13243aea131252dd9be88174b9d57a5b35a44864",[904],[578,1260,1261],{},"13243ae",[594,1263,1264],{"id":768},"Secrets",[927,1266,1267,1279],{},[930,1268,1269],{},[933,1270,1271,1273,1275,1277],{},[936,1272,938],{},[936,1274,941],{},[936,1276,584],{},[936,1278,946],{},[948,1280,1281,1298,1315,1335,1359],{},[933,1282,1283,1287,1290,1295],{},[953,1284,1285],{},[578,1286,772],{},[953,1288,1289],{},"kept, else generated",[953,1291,1292],{},[578,1293,1294],{},"php.appSecret",[953,1296,1297],{},"Symfony's secret, which signs URLs and other tokens",[933,1299,1300,1305,1307,1312],{},[953,1301,1302],{},[578,1303,1304],{},"JWT_SECRET_KEY",[953,1306,1289],{},[953,1308,1309],{},[578,1310,1311],{},"php.jwt.secret",[953,1313,1314],{},"The private key's contents",[933,1316,1317,1322,1324,1329],{},[953,1318,1319],{},[578,1320,1321],{},"JWT_PASSPHRASE",[953,1323,1289],{},[953,1325,1326],{},[578,1327,1328],{},"php.jwt.passphrase",[953,1330,1331,1332,1334],{},"The private key's passphrase. If you set ",[578,1333,1304],{},", set this too, or the deploy fails",[933,1336,1337,1342,1345,1350],{},[953,1338,1339],{},[578,1340,1341],{},"JWT_PUBLIC_KEY",[953,1343,1344],{},"derived",[953,1346,1347],{},[578,1348,1349],{},"php.jwt.public",[953,1351,1352,1353,1355,1356,1358],{},"When you supply ",[578,1354,1304],{}," and leave this empty, it's derived from the key with ",[578,1357,1321],{},", and the deploy fails if the passphrase doesn't decrypt the key",[933,1360,1361,1365,1367,1375],{},[953,1362,1363],{},[578,1364,775],{},[953,1366,1289],{},[953,1368,1369,604,1372],{},[578,1370,1371],{},"mercure.jwtKey.publisher.key",[578,1373,1374],{},"mercure.jwtKey.subscriber.key",[953,1376,1377],{},"Signs the Mercure publisher and subscriber tokens",[560,1379,1380,1381,1384,1385,604,1390,1397,1398,1401,1402,663],{},"All of them are optional. For each one that isn't a CI variable, ",[578,1382,1383],{},"deploy_secrets"," reads the value from the release's live Secret, and generates a new one only when the release doesn't have one yet (",[569,1386,1389],{"href":1387,"rel":1388},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fissues\u002F144",[904],"#144",[569,1391,1394],{"href":1392,"rel":1393},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F816997eaebb7cfb167172e38065042ee5a42f710",[904],[578,1395,1396],{},"816997e","). The JWT key is kept together with its own passphrase, and the public key is derived from them. So logins, signed URLs and Mercure cookies survive a deploy, and pods on either side of a rollout agree. They're passed to Helm with ",[578,1399,1400],{},"--set",", so they win over a project's ",[569,1403,1405],{"href":1404},"\u002Fdeployment\u002Fci-cd#project-hooks",[578,1406,1407],{},"project_values",[560,1409,1410,1411,1414,1415,1418],{},"Setting them as ",[564,1412,1413],{},"protected, masked"," CI variables (GitHub: secrets) is still worth it for production. A release that is uninstalled, for example with ",[578,1416,1417],{},"CWA_CI_HELM_UNINSTALL"," or a stopped review app, loses its Secret, and its next deploy generates new values. A CI variable also lets you rotate a value on purpose.",[560,1420,1421,1422,614,1424,1426,1427,1429,1430,1433],{},"To rotate a value, set a new one as the CI variable and deploy. Removing the variable doesn't rotate it, because the deploy then keeps the live value. For the JWT key pair, set ",[578,1423,1304],{},[578,1425,1321],{},", and leave ",[578,1428,1341],{}," empty to have it derived. ",[564,1431,1432],{},"A new JWT key pair signs every user out"," when it's deployed, because tokens signed with the old key no longer verify.",[560,1435,1436,1437,1439,1440,1443,1444,573],{},"The ",[578,1438,839],{}," values hold the keys' contents, for the deploy. The ",[578,1441,1442],{},"functional tests"," job unsets them, so they never reach the tests (see ",[569,1445,1447],{"href":1446},"\u002Fdeployment\u002Fci-cd#test","CI\u002FCD → Test",[594,1449,746],{"id":1450},"application",[927,1452,1453,1465],{},[930,1454,1455],{},[933,1456,1457,1459,1461,1463],{},[936,1458,938],{},[936,1460,941],{},[936,1462,584],{},[936,1464,946],{},[948,1466,1467,1486,1509,1529,1549,1570,1590,1611],{},[933,1468,1469,1473,1478,1483],{},[953,1470,1471],{},[578,1472,607],{},[953,1474,1475],{},[578,1476,1477],{},"^https:\u002F\u002F(?:\u003Chosts>)$",[953,1479,1480],{},[578,1481,1482],{},"php.corsAllowOrigin",[953,1484,1485],{},"Regex. Set it only when another origin calls the API from a browser",[933,1487,1488,1492,1497,1502],{},[953,1489,1490],{},[578,1491,610],{},[953,1493,1494],{},[578,1495,1496],{},"^(?:\u003Chosts>|localhost)$",[953,1498,1499],{},[578,1500,1501],{},"php.trustedHosts",[953,1503,1504,1505,1508],{},"Symfony trusted hosts regex. Wrap the alternatives in one anchored group, ",[578,1506,1507],{},"^(?:www\\.example\\.com|php)$",", see the warning below",[933,1510,1511,1515,1521,1526],{},[953,1512,1513],{},[578,1514,613],{},[953,1516,1517,1520],{},[578,1518,1519],{},"https:\u002F\u002F\u003Chost>"," for each host",[953,1522,1523],{},[578,1524,1525],{},"mercure.corsOrigin",[953,1527,1528],{},"Space-separated origins the Mercure hub accepts",[933,1530,1531,1536,1541,1546],{},[953,1532,1533],{},[578,1534,1535],{},"ADMIN_USERNAME",[953,1537,1538],{},[578,1539,1540],{},"admin",[953,1542,1543],{},[578,1544,1545],{},"php.admin.username",[953,1547,1548],{},"Initial admin account username",[933,1550,1551,1555,1559,1564],{},[953,1552,1553],{},[578,1554,742],{},[953,1556,1557],{},[578,1558,1540],{},[953,1560,1561],{},[578,1562,1563],{},"php.admin.password",[953,1565,1566,1567],{},"Initial admin account password — ",[564,1568,1569],{},"change this",[933,1571,1572,1577,1582,1587],{},[953,1573,1574],{},[578,1575,1576],{},"ADMIN_EMAIL",[953,1578,1579],{},[578,1580,1581],{},"hello@cwa.rocks",[953,1583,1584],{},[578,1585,1586],{},"php.admin.email",[953,1588,1589],{},"Initial admin account email",[933,1591,1592,1596,1603,1608],{},[953,1593,1594],{},[578,1595,727],{},[953,1597,1598,1599,1602],{},"— (",[578,1600,1601],{},"null:\u002F\u002Fnull",")",[953,1604,1605],{},[578,1606,1607],{},"php.mailer.dsn",[953,1609,1610],{},"SMTP or SES DSN for transactional email. Unset, nothing is sent",[933,1612,1613,1617,1619,1624],{},[953,1614,1615],{},[578,1616,730],{},[953,1618,591],{},[953,1620,1621],{},[578,1622,1623],{},"php.mailer.email",[953,1625,1626,1627,1631],{},"From address for outgoing email. Production's ",[569,1628,1630],{"href":1629},"#orphan-scan","orphan scan"," also emails it",[560,1633,1634,1637,1638,1640],{},[578,1635,1636],{},"\u003Chosts>"," is the deploy's own hostname, plus ",[578,1639,621],{}," in production, with dots escaped.",[880,1642,1643],{"icon":882,"color":883},[560,1644,1645,1651,1652,1655,1656,1659,1660,1663,1664,604,1667,614,1670,1673,1674,663],{},[564,1646,1647,1648,1650],{},"Anchor every ",[578,1649,610],{}," alternative."," Symfony doesn't anchor the pattern as a whole, so in ",[578,1653,1654],{},"^localhost|caddy|example\\.com$"," only the first alternative is tied to the start and only the last to the end. The middle one matches anywhere in a hostname: the template's old default trusted ",[578,1657,1658],{},"evil-caddy.attacker.net",". Group the alternatives, ",[578,1661,1662],{},"^(?:localhost|caddy|example\\.com)$",". The template's own defaults use this form. Check the value in your CI variables, and in ",[578,1665,1666],{},"api\u002F.env",[578,1668,1669],{},"compose.yaml",[578,1671,1672],{},"helm\u002Fcwa\u002Fvalues.yaml"," if your project predates template ",[578,1675,1676],{},"2.0.0-alpha.3",[594,1678,716],{"id":1679},"media-storage",[927,1681,1682,1694],{},[930,1683,1684],{},[933,1685,1686,1688,1690,1692],{},[936,1687,938],{},[936,1689,941],{},[936,1691,584],{},[936,1693,946],{},[948,1695,1696,1715,1734],{},[933,1697,1698,1702,1707,1712],{},[953,1699,1700],{},[578,1701,705],{},[953,1703,1704],{},[578,1705,1706],{},"{}",[953,1708,1709],{},[578,1710,1711],{},"php.gcloud.jsonKey",[953,1713,1714],{},"Service-account key JSON for the bucket. Store it as a secret",[933,1716,1717,1721,1726,1731],{},[953,1718,1719],{},[578,1720,708],{},[953,1722,1723],{},[578,1724,1725],{},"no-gcloud-bucket",[953,1727,1728],{},[578,1729,1730],{},"php.gcloud.bucket",[953,1732,1733],{},"Google Cloud Storage bucket for uploads",[933,1735,1736,1740,1743,1748],{},[953,1737,1738],{},[578,1739,712],{},[953,1741,1742],{},"bucket URL",[953,1744,1745],{},[578,1746,1747],{},"php.gcloud.publicUrl",[953,1749,1750,1751,1754,1755,690,1758],{},"Base URL for media links, e.g. ",[578,1752,1753],{},"https:\u002F\u002Fcdn.example.com\u002F",". Must end in a slash. If empty, uses ",[578,1756,1757],{},"https:\u002F\u002Fstorage.googleapis.com\u002F\u003Cbucket>\u002F",[569,1759,1761],{"href":1760},"\u002Fdeployment\u002Fkubernetes#media-urls","Kubernetes → Media URLs",[560,1763,1764,1765,1767,1768,1771,1772,663],{},"On GitHub, set ",[578,1766,705],{}," as a ",[564,1769,1770],{},"secret"," and the other two as ",[564,1773,1774],{},"variables",[1776,1777,1779],"h3",{"id":1778},"how-long-media-is-cached","How long media is cached",[560,1781,1782,1783,1786,1787,1790,1791,1794],{},"From api-components-bundle 2.0.0-alpha.15, every file written to the bucket gets its own ",[578,1784,1785],{},"Cache-Control",". Originals get ",[578,1788,1789],{},"public, max-age=31536000, immutable",", because a stored name is never reused. Image variants get ",[578,1792,1793],{},"public, max-age=2592000",", because a regenerated variant keeps its path. Files stored before then keep GCS's one-hour default until you back-fill them (template 2.0.0-alpha.25's upgrade notes).",[880,1796,1797],{"icon":882,"color":883},[560,1798,1799,1802,1803,1806,1807,1810,1811,1814],{},[564,1800,1801],{},"Google Cloud CDN caps these unless you raise its TTLs."," A backend bucket sends browsers at most its ",[564,1804,1805],{},"client TTL"," and keeps its own copy for at most its ",[564,1808,1809],{},"max TTL",". The defaults are one hour and one day, so a CDN created with them serves every file with ",[578,1812,1813],{},"max-age=3600"," whatever the bucket says. Raising a cap never lengthens a file's own TTL, so a year is safe for both.",[560,1816,1817],{},"Create the CDN's backend bucket with both caps at a year, or raise them on an existing one:",[1819,1820,1825],"pre",{"className":1821,"code":1822,"language":1823,"meta":1824,"style":1824},"language-bash shiki shiki-themes github-light github-dark material-theme-palenight","gcloud compute backend-buckets update \u003Cbackend-bucket> --max-ttl=31536000 --client-ttl=31536000\n","bash","",[578,1826,1827],{"__ignoreMap":1824},[1828,1829,1832,1836,1840,1843,1846,1850,1853,1857,1860,1864],"span",{"class":1830,"line":1831},"line",1,[1828,1833,1835],{"class":1834},"sRCss","gcloud",[1828,1837,1839],{"class":1838},"sLL54"," compute",[1828,1841,1842],{"class":1838}," backend-buckets",[1828,1844,1845],{"class":1838}," update",[1828,1847,1849],{"class":1848},"sVlFx"," \u003C",[1828,1851,1852],{"class":1838},"backend-bucke",[1828,1854,1856],{"class":1855},"sPB8G","t",[1828,1858,1859],{"class":1848},">",[1828,1861,1863],{"class":1862},"szhYu"," --max-ttl=31536000",[1828,1865,1866],{"class":1862}," --client-ttl=31536000\n",[560,1868,1869,1870,1873,1874,1877,1878,663],{},"Changes reach the CDN's edges within a few minutes. Invalidate the CDN host afterwards so files it already holds pick up the new headers: ",[578,1871,1872],{},"gcloud compute url-maps invalidate-cdn-cache \u003Curl-map> --host \u003Ccdn host> --path \"\u002F*\"",". To check, ",[578,1875,1876],{},"curl -sI"," a variant through the CDN: it should send ",[578,1879,1880],{},"max-age=2592000",[594,1882,694],{"id":1883},"database",[927,1885,1886,1898],{},[930,1887,1888],{},[933,1889,1890,1892,1894,1896],{},[936,1891,938],{},[936,1893,941],{},[936,1895,584],{},[936,1897,946],{},[948,1899,1900,1922,1938,1977,1994,2018,2042,2062,2082,2107,2127,2155],{},[933,1901,1902,1907,1912,1917],{},[953,1903,1904],{},[578,1905,1906],{},"CWA_CI_POSTGRES_ENABLED",[953,1908,1909],{},[578,1910,1911],{},"true",[953,1913,1914],{},[578,1915,1916],{},"postgresql.enabled",[953,1918,1154,1919,1921],{},[578,1920,1157],{}," to use an external database, which turns off the in-cluster PostgreSQL",[933,1923,1924,1928,1930,1935],{},[953,1925,1926],{},[578,1927,685],{},[953,1929,591],{},[953,1931,1932],{},[578,1933,1934],{},"postgresql.url",[953,1936,1937],{},"Connection string when using an external database",[933,1939,1940,1944,1959,1964],{},[953,1941,1942],{},[578,1943,617],{},[953,1945,1946,1949,1950,1953,1954,1956,1957],{},[578,1947,1948],{},"prefer",", or ",[578,1951,1952],{},"require"," when ",[578,1955,1906],{}," is ",[578,1958,1157],{},[953,1960,1961],{},[578,1962,1963],{},"php.databaseSSL.mode",[953,1965,1966,604,1969,604,1971,604,1973,1976],{},[578,1967,1968],{},"disable",[578,1970,1948],{},[578,1972,1952],{},[578,1974,1975],{},"verify-full",". An external database is reached over the network, so it gets TLS by default",[933,1978,1979,1984,1986,1991],{},[953,1980,1981],{},[578,1982,1983],{},"DATABASE_CA_CERT",[953,1985,591],{},[953,1987,1988],{},[578,1989,1990],{},"php.databaseSSL.ca",[953,1992,1993],{},"CA cert PEM for SSL verification",[933,1995,1996,2005,2007,2015],{},[953,1997,1998,2001,2002],{},[578,1999,2000],{},"DATABASE_CLIENT_CERT"," \u002F ",[578,2003,2004],{},"DATABASE_CLIENT_KEY",[953,2006,591],{},[953,2008,2009,2001,2012],{},[578,2010,2011],{},"php.databaseSSL.cert",[578,2013,2014],{},"php.databaseSSL.key",[953,2016,2017],{},"A client certificate and key, for a database that asks for one",[933,2019,2020,2025,2030,2035],{},[953,2021,2022],{},[578,2023,2024],{},"POSTGRES_USER",[953,2026,2027],{},[578,2028,2029],{},"example",[953,2031,2032],{},[578,2033,2034],{},"postgresql.global.postgresql.auth.username",[953,2036,2037,2038,2041],{},"In-cluster PostgreSQL only: the database user. The name the postgres image, compose and the test jobs use. ",[578,2039,2040],{},"POSTGRES_USERNAME"," is not read",[933,2043,2044,2049,2054,2059],{},[953,2045,2046],{},[578,2047,2048],{},"POSTGRES_PASSWORD",[953,2050,2051],{},[578,2052,2053],{},"!ChangeMe!",[953,2055,2056],{},[578,2057,2058],{},"postgresql.global.postgresql.auth.password",[953,2060,2061],{},"In-cluster PostgreSQL only: the user's password. Any character is safe",[933,2063,2064,2069,2074,2079],{},[953,2065,2066],{},[578,2067,2068],{},"POSTGRES_DB",[953,2070,2071],{},[578,2072,2073],{},"api",[953,2075,2076],{},[578,2077,2078],{},"postgresql.global.postgresql.auth.database",[953,2080,2081],{},"In-cluster PostgreSQL only: the database name",[933,2083,2084,2089,2092,2097],{},[953,2085,2086],{},[578,2087,2088],{},"CWA_CI_POSTGRES_PERSISTENCE",[953,2090,2091],{},"unset (off)",[953,2093,2094],{},[578,2095,2096],{},"postgresql.primary.persistence.enabled",[953,2098,2099,2100,2102,2103,2106],{},"In-cluster PostgreSQL only: ",[578,2101,1911],{}," keeps its data on a volume, ",[578,2104,2105],{},"false"," turns that off. Any other value fails the deploy",[933,2108,2109,2114,2119,2124],{},[953,2110,2111],{},[578,2112,2113],{},"CWA_CI_POSTGRES_PERSISTENCE_SIZE",[953,2115,2116],{},[578,2117,2118],{},"1Gi",[953,2120,2121],{},[578,2122,2123],{},"postgresql.primary.persistence.size",[953,2125,2126],{},"The volume's size",[933,2128,2129,2134,2139,2144],{},[953,2130,2131],{},[578,2132,2133],{},"CWA_CI_POSTGRES_STORAGE_CLASS",[953,2135,2136],{},[578,2137,2138],{},"standard",[953,2140,2141],{},[578,2142,2143],{},"postgresql.primary.persistence.storageClass",[953,2145,2146,2147,2150,2151,2154],{},"The volume's storage class. ",[578,2148,2149],{},"-"," sets none (Bitnami's ",[578,2152,2153],{},"storageClassName: \"\"","), which turns off dynamic provisioning",[933,2156,2157,2162,2167,2172],{},[953,2158,2159],{},[578,2160,2161],{},"CWA_CI_POSTGRES_IMAGE_TAG",[953,2163,2164],{},[578,2165,2166],{},"14",[953,2168,2169],{},[578,2170,2171],{},"postgresql.image.tag",[953,2173,2174],{},"The in-cluster PostgreSQL image tag",[560,2176,2177,2178,2180,2181,2183,2184,2186,2187,2190,2191,2194,2195,2202],{},"With the in-cluster PostgreSQL (",[578,2179,1906],{}," not ",[578,2182,1157],{},"), the deploy writes the three ",[578,2185,834],{}," variables that are set to the chart's ",[578,2188,2189],{},"postgresql.global.postgresql.auth",". Bitnami's subchart creates the database from those values, and the chart builds the API's ",[578,2192,2193],{},"database-url"," from them, percent-encoding each part (",[569,2196,2199],{"href":2197,"rel":2198},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002Fe15f16818a61f9a3f68697808bea5998bf34b0dc",[904],[578,2200,2201],{},"e15f168","). An unset variable keeps the chart's default.",[880,2204,2205],{"icon":882,"color":883},[560,2206,2207,2210,2211,686,2213,2215,2216,663],{},[564,2208,2209],{},"By default the in-cluster PostgreSQL has no volume."," Its data lives in the pod and is lost whenever the pod is replaced, for example after a node restart or a credentials change. Use an external database (",[578,2212,685],{},[578,2214,689],{},") for a real site, or turn on ",[569,2217,2219],{"href":2218},"#in-cluster-postgresql-persistence","persistence",[560,2221,2222,2223,2226,2227,2230],{},"PostgreSQL creates the user and database only when it starts on an empty data directory. Without persistence, ",[564,2224,2225],{},"changing the user or database name on a live site restarts the PostgreSQL pod empty",", so the content has to be loaded again. A new password alone doesn't restart the pod, so the API can't connect until you run ",[578,2228,2229],{},"kubectl delete pod \u003Crelease>-postgresql-0",", which also starts it empty.",[560,2232,2233,2234,2237,2238,802,2241,2244],{},"With persistence, the volume keeps the user, password and database it was created with, and a deploy doesn't change them. Change them in PostgreSQL first, as the ",[578,2235,2236],{},"postgres"," superuser, with ",[578,2239,2240],{},"ALTER USER",[578,2242,2243],{},"ALTER DATABASE"," (renaming a user clears its password, so set it again), then set the new values and deploy.",[560,2246,2247,2248,2250,2251,2254,2255,2257,2258,2261,2262,2261,2265,2268],{},"On GitLab, a ",[578,2249,834],{}," variable with the ",[564,2252,2253],{},"All"," environment scope also reaches the ",[578,2256,1442],{}," job, where it replaces the job's own ",[578,2259,2260],{},"runner","\u002F",[578,2263,2264],{},"password",[578,2266,2267],{},"test",", for both the test database and its URL. Scope these variables to your deploy environments. On GitHub, the deploy jobs don't map them from secrets, so set them as Actions variables; the test job sets its own and ignores them.",[1776,2270,2272],{"id":2271},"in-cluster-postgresql-persistence","In-cluster PostgreSQL persistence",[560,2274,2275,2276,2279,2280,2283,2284,2286,2287,2289,2290,573],{},"The three ",[578,2277,2278],{},"CWA_CI_POSTGRES_*"," persistence variables go to the chart's ",[578,2281,2282],{},"postgresql.primary.persistence",", and only the ones that are set. Unset, the chart's defaults apply: no volume, and ",[578,2285,2138],{}," at ",[578,2288,2118],{}," once it's turned on (",[569,2291,2294],{"href":2292,"rel":2293},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F767519054d9b8473a42eb30144ea6eaeae65d39a",[904],[578,2295,2296],{},"7675190",[560,2298,2299,2300,2303],{},"Kubernetes can't change a StatefulSet's volume once it exists, so Helm would fail part way through. Before anything changes, the deploy compares the variables with the live PostgreSQL and ",[564,2301,2302],{},"stops when they differ",", with the commands to run and what happens to the data. It never deletes anything itself:",[927,2305,2306,2316],{},[930,2307,2308],{},[933,2309,2310,2313],{},[936,2311,2312],{},"Change on a live release",[936,2314,2315],{},"What the deploy tells you to do",[948,2317,2318,2333,2352,2368],{},[933,2319,2320,2323],{},[953,2321,2322],{},"Turn persistence on",[953,2324,2325,2328,2329,2332],{},[578,2326,2327],{},"pg_dump"," the data if you need it, run ",[578,2330,2331],{},"kubectl delete statefulset \u003Crelease>-postgresql -n \u003Cnamespace> --cascade=orphan",", and deploy again. The new pod starts with an empty volume, so load fixtures or restore the dump",[933,2334,2335,2338],{},[953,2336,2337],{},"Turn persistence off",[953,2339,2340,2341,2344,2345,2348,2349],{},"The same ",[578,2342,2343],{},"kubectl delete statefulset … --cascade=orphan",", then deploy. The new pod starts empty. The volume ",[578,2346,2347],{},"data-\u003Crelease>-postgresql-0"," keeps its data until you delete it with ",[578,2350,2351],{},"kubectl delete pvc",[933,2353,2354,2357],{},[953,2355,2356],{},"Change the size",[953,2358,2359,2360,2363,2364,2367],{},"Set it back, or delete the StatefulSet with ",[578,2361,2362],{},"--cascade=orphan"," and deploy: the volume and its data are kept at the old size. To grow it, if the storage class allows expansion, patch the volume with the ",[578,2365,2366],{},"kubectl patch pvc"," command the log prints",[933,2369,2370,2373],{},[953,2371,2372],{},"Change the storage class",[953,2374,2375,2376,2378,2379,2381,2382,2384],{},"Set it back, or ",[578,2377,2327],{},", delete the StatefulSet with ",[578,2380,2362],{}," and the volume with ",[578,2383,2351],{},", deploy, and restore the dump",[560,2386,2387],{},"A first deploy, with no PostgreSQL yet, can use any settings. If the CI can't read the StatefulSet, the deploy prints a warning and skips the check.",[560,2389,2390,2391,2394,2395,2398,2399,2402,2403,2405,2406,2408],{},"Helm leaves volumes behind when it uninstalls a release. ",[564,2392,2393],{},"Stopping a review app also deletes its PostgreSQL volume",", so a branch deployed again starts with fresh data and credentials. Staging and canary keep theirs: a persistent staging volume outlives ",[578,2396,2397],{},"delete staging",", so delete it by hand with ",[578,2400,2401],{},"kubectl delete pvc data-\u003Crelease>-staging-postgresql-0",". On GitLab, a ",[578,2404,2088],{}," scoped to ",[578,2407,921],{}," reaches staging and canary too.",[594,2410,2412],{"id":2411},"composer","Composer",[927,2414,2415,2427],{},[930,2416,2417],{},[933,2418,2419,2421,2423,2425],{},[936,2420,938],{},[936,2422,941],{},[936,2424,584],{},[936,2426,946],{},[948,2428,2429],{},[933,2430,2431,2435,2438,2440],{},[953,2432,2433],{},[578,2434,864],{},[953,2436,2437],{},"unset",[953,2439,591],{},[953,2441,2442,2443,2446],{},"Optional. Authenticates composer's downloads from github.com, in the API image build and the GitLab test jobs, so installs don't hit GitHub's anonymous limit of 60 requests an hour per IP. A fine-grained token with ",[564,2444,2445],{},"no permissions"," is enough. Store it as a masked variable",[560,2448,2449,2451,2452,614,2455,2458,2459,2463,2464,2467,2468,769,2471,573],{},[578,2450,1230],{}," turns the token into ",[578,2453,2454],{},"COMPOSER_AUTH",[578,2456,2457],{},"build_api"," passes that to the build as a secret (see ",[569,2460,2462],{"href":2461},"\u002Fdeployment\u002Fci-cd#build","CI\u002FCD → Build","). ",[564,2465,2466],{},"Leave the variable unset rather than empty:"," an empty token is rejected outright, which is worse than anonymous. GitHub Actions needs nothing: the build step maps the workflow's own ",[578,2469,2470],{},"secrets.GITHUB_TOKEN",[569,2472,2475],{"href":2473,"rel":2474},"https:\u002F\u002Fgitlab.com\u002Fsilverback-web-apps\u002Fcwa\u002Fcomponents-web-app\u002F-\u002Fmerge_requests\u002F12",[904],"!12",[594,2477,2479],{"id":2478},"autoscaling-and-sizing","Autoscaling and sizing",[560,2481,2482,2483,2485,2486,2489],{},"The php pod and the Nuxt pod scale separately. The ",[578,2484,809],{}," variables control php and the ",[578,2487,2488],{},"CWA_CI_PWA_*"," variables control Nuxt.",[1776,2491,2493],{"id":2492},"php-api","php (API)",[927,2495,2496,2508],{},[930,2497,2498],{},[933,2499,2500,2502,2504,2506],{},[936,2501,938],{},[936,2503,941],{},[936,2505,584],{},[936,2507,946],{},[948,2509,2510,2530,2548,2566,2587,2606,2624,2644,2668,2691],{},[933,2511,2512,2517,2522,2527],{},[953,2513,2514],{},[578,2515,2516],{},"CWA_CI_API_REPLICA_COUNT",[953,2518,2519],{},[578,2520,2521],{},"1",[953,2523,2524],{},[578,2525,2526],{},"replicaCount",[953,2528,2529],{},"Used only when autoscaling is off",[933,2531,2532,2537,2541,2546],{},[953,2533,2534],{},[578,2535,2536],{},"CWA_CI_API_AUTOSCALE",[953,2538,2539],{},[578,2540,1911],{},[953,2542,2543],{},[578,2544,2545],{},"autoscaling.enabled",[953,2547],{},[933,2549,2550,2555,2559,2564],{},[953,2551,2552],{},[578,2553,2554],{},"CWA_CI_API_AUTOSCALE_MIN",[953,2556,2557],{},[578,2558,2521],{},[953,2560,2561],{},[578,2562,2563],{},"autoscaling.minReplicas",[953,2565],{},[933,2567,2568,2573,2577,2582],{},[953,2569,2570],{},[578,2571,2572],{},"CWA_CI_API_AUTOSCALE_MAX",[953,2574,2575],{},[578,2576,2521],{},[953,2578,2579],{},[578,2580,2581],{},"autoscaling.maxReplicas",[953,2583,2584,2586],{},[578,2585,2521],{}," on purpose, see below",[933,2588,2589,2594,2599,2604],{},[953,2590,2591],{},[578,2592,2593],{},"CWA_CI_API_AUTOSCALE_CPU_PERCENT",[953,2595,2596],{},[578,2597,2598],{},"90",[953,2600,2601],{},[578,2602,2603],{},"autoscaling.targetCPUUtilizationPercentage",[953,2605],{},[933,2607,2608,2613,2617,2622],{},[953,2609,2610],{},[578,2611,2612],{},"CWA_CI_API_AUTOSCALE_MEMORY_PERCENT",[953,2614,2615],{},[578,2616,2598],{},[953,2618,2619],{},[578,2620,2621],{},"autoscaling.targetMemoryUtilizationPercentage",[953,2623],{},[933,2625,2626,2631,2637,2642],{},[953,2627,2628],{},[578,2629,2630],{},"CWA_CI_API_CPU_REQUEST",[953,2632,2633,2636],{},[578,2634,2635],{},"100m"," on every track",[953,2638,2639],{},[578,2640,2641],{},"php.resources.requests.cpu",[953,2643],{},[933,2645,2646,2651,2661,2666],{},[953,2647,2648],{},[578,2649,2650],{},"CWA_CI_API_MEMORY_REQUEST",[953,2652,2653,2656,2657,2660],{},[578,2654,2655],{},"350Mi"," for production and canary, ",[578,2658,2659],{},"256Mi"," otherwise",[953,2662,2663],{},[578,2664,2665],{},"php.resources.requests.memory",[953,2667],{},[933,2669,2670,2675,2679,2684],{},[953,2671,2672],{},[578,2673,2674],{},"CWA_CI_API_MEMORY_LIMIT",[953,2676,2677,2636],{},[578,2678,2118],{},[953,2680,2681],{},[578,2682,2683],{},"php.resources.limits.memory",[953,2685,1436,2686,2690],{},[569,2687,2689],{"href":2688},"#php-memory-budget","php memory budget"," must fit it",[933,2692,2693,2698,2701,2706],{},[953,2694,2695],{},[578,2696,2697],{},"CWA_CI_API_GOMEMLIMIT",[953,2699,2700],{},"empty",[953,2702,2703],{},[578,2704,2705],{},"php.goMemLimit",[953,2707,2708,2709,2711,2712,2715,2716,802,2719,2722,2723,2726,2727,2730,2731,2734],{},"Empty: what the php memory budget leaves of ",[578,2710,2674],{},", at least ",[578,2713,2714],{},"64MiB",", when the limit is in ",[578,2717,2718],{},"Mi",[578,2720,2721],{},"Gi",". A Go value such as ",[578,2724,2725],{},"800MiB"," overrides it, and ",[578,2728,2729],{},"off"," leaves ",[578,2732,2733],{},"GOMEMLIMIT"," unset",[560,2736,2737,1956,2739,2741,2742,663],{},[578,2738,2572],{},[578,2740,2521],{}," on purpose. The php pod keeps its cache and its Mercure data in memory, so a second pod would not see purges made by the first. See ",[569,2743,2745],{"href":2744},"\u002Fdeployment\u002Fkubernetes#autoscaling","Kubernetes → Autoscaling",[1776,2747,2689],{"id":2748},"php-memory-budget",[560,2750,2751,2752,604,2757,2764],{},"These size the php container's memory (",[569,2753,2756],{"href":2754,"rel":2755},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fissues\u002F136",[904],"#136",[569,2758,2761],{"href":2759,"rel":2760},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F1868035b41a4edd48d42b84c2de736026c8f5ce6",[904],[578,2762,2763],{},"1868035","):",[927,2766,2767,2779],{},[930,2768,2769],{},[933,2770,2771,2773,2775,2777],{},[936,2772,938],{},[936,2774,941],{},[936,2776,584],{},[936,2778,946],{},[948,2780,2781,2804,2834,2861],{},[933,2782,2783,2788,2793,2798],{},[953,2784,2785],{},[578,2786,2787],{},"CWA_CI_API_WORKERS",[953,2789,2790],{},[578,2791,2792],{},"4",[953,2794,2795],{},[578,2796,2797],{},"php.workers",[953,2799,2800,2801,1602],{},"FrankenPHP's worker threads (",[578,2802,2803],{},"FRANKENPHP_WORKER_NUM",[933,2805,2806,2811,2816,2821],{},[953,2807,2808],{},[578,2809,2810],{},"CWA_CI_API_PHP_MEMORY_LIMIT",[953,2812,2813],{},[578,2814,2815],{},"128Mi",[953,2817,2818],{},[578,2819,2820],{},"php.memoryLimit",[953,2822,2823,2824,769,2827,2830,2831,1602],{},"Each request's PHP ",[578,2825,2826],{},"memory_limit",[578,2828,2829],{},"FRANKENPHP_MEMORY_LIMIT",", as ",[578,2832,2833],{},"128M",[933,2835,2836,2841,2846,2851],{},[953,2837,2838],{},[578,2839,2840],{},"CWA_CI_API_IMAGINE_MEMORY_LIMIT",[953,2842,2843],{},[578,2844,2845],{},"320Mi",[953,2847,2848],{},[578,2849,2850],{},"php.imagineMemoryLimit",[953,2852,2853,2854,2857,2858],{},"The limit the bundle raises a request to while it generates an imagine filter. Keep it equal to the bundle's ",[578,2855,2856],{},"imagine.memory_limit",", whose default is ",[578,2859,2860],{},"320M",[933,2862,2863,2868,2873,2878],{},[953,2864,2865],{},[578,2866,2867],{},"CWA_CI_API_RESERVED_MEMORY",[953,2869,2870],{},[578,2871,2872],{},"160Mi",[953,2874,2875],{},[578,2876,2877],{},"php.reservedMemory",[953,2879,2880],{},"opcache, APCu, the binaries and a console command run in the pod",[560,2882,2883],{},"The budget is:",[1819,2885,2890],{"className":2886,"code":2888,"language":2889},[2887],"language-text","workers × PHP memory limit + (imagine limit − PHP memory limit) + reserved + Go ≤ CWA_CI_API_MEMORY_LIMIT\n","text",[578,2891,2888],{"__ignoreMap":1824},[560,2893,2894,2895,2897],{},"Go (Caddy, Souin and Mercure) gets what is left, as ",[578,2896,2733],{},": with the defaults, 4 × 128 + 192 + 160 = 864Mi, which leaves Go 160Mi of 1Gi. The imagine limit counts once: the budget allows for one request generating a filter at a time.",[560,2899,2900,2903,2904,2261,2906,2908,2909,2911,2912,2915,2916,2918,2919,2921,2922,2924],{},[564,2901,2902],{},"The deploy fails before it changes anything in the cluster"," when the budget leaves Go under 64Mi, or when a value isn't a whole number or an ",[578,2905,2718],{},[578,2907,2721],{}," quantity. The log shows the sum and what to change. With a ",[578,2910,2674],{}," in another form, such as ",[578,2913,2914],{},"1G",", the check is skipped and ",[578,2917,2733],{}," is left unset unless ",[578,2920,2697],{}," sets it. So when you raise the workers or the PHP memory limit, raise ",[578,2923,2674],{}," too.",[560,2926,2927,2928,2931,2932,2934,2935,2937,2938,2941,2942,663],{},"Console commands, such as the orphan scan or a ",[578,2929,2930],{},"kubectl exec"," into the pod, don't use ",[578,2933,2829],{},". They run at the ",[578,2936,2826],{}," of ",[578,2939,2940],{},"256M"," in ",[578,2943,2944],{},"api\u002Ffrankenphp\u002Fconf.d\u002F10-app.ini",[880,2946,2947],{"icon":882,"color":883},[560,2948,2949,641,2958,614,2960,2962,2963,2967],{},[564,2950,2951,2952,2954,2955,663],{},"Set the worker count with ",[578,2953,2787],{},", not ",[578,2956,2957],{},"CWA_API_FRANKENPHP_WORKER_NUM",[578,2959,2803],{},[578,2961,2829],{}," are ",[569,2964,2966],{"href":2965},"#reserved-names","reserved names",", so the deploy refuses them as site settings.",[560,2969,2970,2971,2974,2975,663],{},"The template's ",[578,2972,2973],{},"Image"," entity accepts uploads up to 30 megapixels: what the 320M imagine limit can thumbnail with the template's libvips driver. If you switch the driver back to GD, lower it to 20 megapixels. See ",[569,2976,138],{"href":139},[1776,2978,2980],{"id":2979},"nuxt-ssr","Nuxt (SSR)",[927,2982,2983,2995],{},[930,2984,2985],{},[933,2986,2987,2989,2991,2993],{},[936,2988,938],{},[936,2990,941],{},[936,2992,584],{},[936,2994,946],{},[948,2996,2997,3015,3033,3063,3084,3108,3125,3152],{},[933,2998,2999,3004,3008,3013],{},[953,3000,3001],{},[578,3002,3003],{},"CWA_CI_PWA_REPLICA_COUNT",[953,3005,3006],{},[578,3007,2521],{},[953,3009,3010],{},[578,3011,3012],{},"pwa.replicaCount",[953,3014,2529],{},[933,3016,3017,3022,3026,3031],{},[953,3018,3019],{},[578,3020,3021],{},"CWA_CI_PWA_AUTOSCALE",[953,3023,3024],{},[578,3025,1911],{},[953,3027,3028],{},[578,3029,3030],{},"pwa.autoscaling.enabled",[953,3032],{},[933,3034,3035,3039,3048,3053],{},[953,3036,3037],{},[578,3038,805],{},[953,3040,3041,3044,3045,3047],{},[578,3042,3043],{},"2"," for production, ",[578,3046,2521],{}," for canary and every other track",[953,3049,3050],{},[578,3051,3052],{},"pwa.autoscaling.minReplicas",[953,3054,3055,3056,1602],{},"A canary runs beside production, which keeps serving, so one pod is enough (",[569,3057,3060],{"href":3058,"rel":3059},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F095c074eba4a660412fad08c74a6415c11591115",[904],[578,3061,3062],{},"095c074",[933,3064,3065,3070,3077,3082],{},[953,3066,3067],{},[578,3068,3069],{},"CWA_CI_PWA_AUTOSCALE_MAX",[953,3071,3072,2656,3075,2660],{},[578,3073,3074],{},"6",[578,3076,3043],{},[953,3078,3079],{},[578,3080,3081],{},"pwa.autoscaling.maxReplicas",[953,3083],{},[933,3085,3086,3091,3097,3102],{},[953,3087,3088],{},[578,3089,3090],{},"CWA_CI_PWA_AUTOSCALE_CPU_PERCENT",[953,3092,3093,3096],{},[578,3094,3095],{},"175"," (of the CPU request)",[953,3098,3099],{},[578,3100,3101],{},"pwa.autoscaling.targetCPUUtilizationPercentage",[953,3103,3104,3105],{},"Change it together with ",[578,3106,3107],{},"CWA_CI_PWA_CPU_REQUEST",[933,3109,3110,3115,3118,3123],{},[953,3111,3112],{},[578,3113,3114],{},"CWA_CI_PWA_AUTOSCALE_MEMORY_PERCENT",[953,3116,3117],{},"unset (no memory target)",[953,3119,3120],{},[578,3121,3122],{},"pwa.autoscaling.targetMemoryUtilizationPercentage",[953,3124],{},[933,3126,3127,3134,3142,3150],{},[953,3128,3129,2001,3131],{},[578,3130,3107],{},[578,3132,3133],{},"CWA_CI_PWA_CPU_LIMIT",[953,3135,3136,3138,3139],{},[578,3137,2635],{}," on every track \u002F ",[578,3140,3141],{},"1000m",[953,3143,3144,2001,3147],{},[578,3145,3146],{},"pwa.resources.requests.cpu",[578,3148,3149],{},"pwa.resources.limits.cpu",[953,3151],{},[933,3153,3154,3162,3171,3179],{},[953,3155,3156,2001,3159],{},[578,3157,3158],{},"CWA_CI_PWA_MEMORY_REQUEST",[578,3160,3161],{},"CWA_CI_PWA_MEMORY_LIMIT",[953,3163,3164,2656,3166,3168,3169],{},[578,3165,2872],{},[578,3167,2815],{}," otherwise \u002F ",[578,3170,2118],{},[953,3172,3173,2001,3176],{},[578,3174,3175],{},"pwa.resources.requests.memory",[578,3177,3178],{},"pwa.resources.limits.memory",[953,3180],{},[560,3182,3183,3191,3192,663],{},[564,3184,3185,3186,614,3188,3190],{},"Change ",[578,3187,3107],{},[578,3189,3090],{}," together."," The target is a percentage of the request, and request × target should stay near 175m, about one render a second. See ",[569,3193,3195],{"href":3194},"\u002Fdeployment\u002Fkubernetes#nuxt-scales-out","Kubernetes → Nuxt scales out",[560,3197,3198],{},"The minimums, maximums and memory requests depend on the environment, so review apps and staging don't reserve as much as production. Limits are the same everywhere.",[560,3200,3201,3202,3204],{},"The Nuxt HPA scales on CPU only. Server rendering is CPU-bound, and an extra pod doesn't relieve memory, because each pod keeps its own caches. Node also rarely hands memory back, and an HPA only scales down when every metric is under its target, so a memory target would add pods under normal load and then keep them. Set ",[578,3203,3114],{}," if you want one anyway. If you set a variable, your value is used in every environment.",[594,3206,3208],{"id":3207},"orphan-scan","Orphan scan",[560,3210,1436,3211,3215,3216,3219,3220,3222],{},[569,3212,3214],{"href":3213},"\u002Fdeployment\u002Fkubernetes#daily-orphan-scan","daily orphan scan"," is a CronJob that runs ",[578,3217,3218],{},"scan-orphaned"," and emails ",[578,3221,730],{}," when the orphans change.",[927,3224,3225,3237],{},[930,3226,3227],{},[933,3228,3229,3231,3233,3235],{},[936,3230,938],{},[936,3232,941],{},[936,3234,584],{},[936,3236,946],{},[948,3238,3239,3264,3284],{},[933,3240,3241,3246,3252,3257],{},[953,3242,3243],{},[578,3244,3245],{},"CWA_CI_ORPHAN_SCAN",[953,3247,3248,3044,3250,2660],{},[578,3249,1146],{},[578,3251,1157],{},[953,3253,3254],{},[578,3255,3256],{},"cronjobs.orphanScan.enabled",[953,3258,3259,802,3261,3263],{},[578,3260,1146],{},[578,3262,1157],{},". Your value is used on every track",[933,3265,3266,3271,3276,3281],{},[953,3267,3268],{},[578,3269,3270],{},"CWA_CI_ORPHAN_SCAN_SCHEDULE",[953,3272,3273],{},[578,3274,3275],{},"0 3 * * *",[953,3277,3278],{},[578,3279,3280],{},"cronjobs.orphanScan.schedule",[953,3282,3283],{},"Cron schedule",[933,3285,3286,3291,3296,3301],{},[953,3287,3288],{},[578,3289,3290],{},"CWA_CI_ORPHAN_SCAN_TIMEZONE",[953,3292,3293],{},[578,3294,3295],{},"Europe\u002FLondon",[953,3297,3298],{},[578,3299,3300],{},"cronjobs.orphanScan.timeZone",[953,3302,3303],{},"Time zone for the schedule. Needs Kubernetes 1.27+; older clusters use UTC",[560,3305,3306],{},"Staging and canary are off by default because they can share production's database, so their scans would repeat production's alert. Review apps are short-lived.",[560,3308,3309,3310,3312,3313,2941,3316,3319,3320,3323,3324,663],{},"The email goes to ",[578,3311,730],{},", the same address the site sends from (",[578,3314,3315],{},"orphaned_resources.notify.recipients",[578,3317,3318],{},"api\u002Fconfig\u002Fpackages\u002Fsilverback_api_components.yaml","). Both a bare address and the ",[578,3321,3322],{},"Name \u003Caddress>"," form work; see ",[569,3325,3327],{"href":3326},"\u002Fapi\u002Forphaned-data#emailing-when-orphans-change","Emailing when orphans change",[560,3329,3330,3331,3333,3334,3336,3337,3339,3340,3342,3343,3345,3346,878],{},"An empty ",[578,3332,730],{}," means no email. An unset ",[578,3335,727],{}," renders as ",[578,3338,1601],{},", so nothing is sent. The GitHub deploy jobs pass ",[578,3341,727],{}," (a secret), ",[578,3344,730],{}," and the ",[578,3347,3348],{},"CWA_CI_ORPHAN_SCAN*",[594,3350,3352],{"id":3351},"origin-protection","Origin protection",[560,3354,3355,3356,802,3359,3362],{},"These protect php and Nuxt from requests that get past the cache. Every one is optional: leave it unset, or empty, and the default in ",[578,3357,3358],{},"api\u002Ffrankenphp\u002FCaddyfile",[578,3360,3361],{},"worker.Caddyfile"," applies. On GitHub, set them as Actions variables and they reach the deploy without a workflow change.",[560,3364,3365,3366,3369,3370,3373,3374,3377,3378,3381,3382,3385,3386,3390,3391,3394],{},"The Caddy settings are ",[569,3367,3368],{"href":825},"site settings",": set ",[578,3371,3372],{},"CWA_API_\u003CNAME>"," in CI and the php container gets ",[578,3375,3376],{},"\u003CNAME>",", so ",[578,3379,3380],{},"CWA_API_RATE_LIMIT_EVENTS"," arrives as ",[578,3383,3384],{},"RATE_LIMIT_EVENTS",". The local compose stack uses the bare names (see ",[569,3387,3389],{"href":3388},"\u002Fdeployment\u002Fdocker#origin-protection-settings","Docker → Origin protection settings","). The ",[578,3392,3393],{},"CWA_CI_INGRESS_*"," ones are deploy switches, which the deploy turns into ingress annotations.",[927,3396,3397,3409],{},[930,3398,3399],{},[933,3400,3401,3403,3405,3407],{},[936,3402,938],{},[936,3404,941],{},[936,3406,584],{},[936,3408,946],{},[948,3410,3411,3447,3467,3489,3511,3537,3564,3597,3623,3645],{},[933,3412,3413,3418,3423,3428],{},[953,3414,3415],{},[578,3416,3417],{},"CWA_API_CACHE_QUERY_ALLOWLIST",[953,3419,3420],{},[578,3421,3422],{},"page search order",[953,3424,3425],{},[578,3426,3427],{},"siteEnv.apiSecret",[953,3429,3430,3431,3434,3435,3438,3439,3442,3443],{},"Query parameters that pages keep, space-separated. A name also keeps ",[578,3432,3433],{},"name[...]",". Add your pages' own parameters, but never ",[578,3436,3437],{},"cwa_force",". ",[578,3440,3441],{},"*"," turns it off. See ",[569,3444,3446],{"href":3445},"\u002Fdeployment\u002Forigin-protection#query-parameters-on-pages","Query parameters on pages",[933,3448,3449,3454,3458,3462],{},[953,3450,3451],{},[578,3452,3453],{},"CWA_API_RATE_LIMIT_ENABLED",[953,3455,3456],{},[578,3457,1911],{},[953,3459,3460],{},[578,3461,3427],{},[953,3463,3464,3466],{},[578,3465,2105],{}," turns off the per-visitor limit on cache misses",[933,3468,3469,3473,3478,3482],{},[953,3470,3471],{},[578,3472,3380],{},[953,3474,3475],{},[578,3476,3477],{},"3000",[953,3479,3480],{},[578,3481,3427],{},[953,3483,3484,3485,3488],{},"Requests per window per visitor. Cache hits, ",[578,3486,3487],{},"\u002F_nuxt",", Mercure and private addresses aren't counted",[933,3490,3491,3496,3501,3505],{},[953,3492,3493],{},[578,3494,3495],{},"CWA_API_RATE_LIMIT_WINDOW",[953,3497,3498],{},[578,3499,3500],{},"1m",[953,3502,3503],{},[578,3504,3427],{},[953,3506,3507,3508],{},"Window length, as a Go duration such as ",[578,3509,3510],{},"10s",[933,3512,3513,3518,3523,3527],{},[953,3514,3515],{},[578,3516,3517],{},"CWA_API_CADDY_TRUSTED_PROXIES",[953,3519,3520],{},[578,3521,3522],{},"private_ranges",[953,3524,3525],{},[578,3526,3427],{},[953,3528,3529,3530,3533,3534],{},"Proxies whose ",[578,3531,3532],{},"X-Forwarded-For"," is believed. Add a public proxy's ranges, e.g. a Google load balancer: ",[578,3535,3536],{},"private_ranges 35.191.0.0\u002F16 130.211.0.0\u002F22",[933,3538,3539,3544,3547,3551],{},[953,3540,3541],{},[578,3542,3543],{},"CWA_API_CLOUDFLARE_IP_RANGES",[953,3545,3546],{},"Cloudflare's published ranges",[953,3548,3549],{},[578,3550,3427],{},[953,3552,3553,3554,3557,3558,3563],{},"Only requests from these addresses are counted by ",[578,3555,3556],{},"CF-Connecting-IP",". Update it if Cloudflare changes ",[569,3559,3562],{"href":3560,"rel":3561},"https:\u002F\u002Fwww.cloudflare.com\u002Fips\u002F",[904],"its ranges"," before a template release does",[933,3565,3566,3571,3576,3580],{},[953,3567,3568],{},[578,3569,3570],{},"CWA_API_FRANKENPHP_MAX_WAIT_TIME",[953,3572,3573],{},[578,3574,3575],{},"5s",[953,3577,3578],{},[578,3579,3427],{},[953,3581,3582,3583,3586,3587,3438,3590,3442,3593],{},"A request that waits this long for a free php worker gets a ",[578,3584,3585],{},"503"," at once instead of queueing into a ",[578,3588,3589],{},"504",[578,3591,3592],{},"0",[569,3594,3596],{"href":3595},"\u002Fdeployment\u002Forigin-protection#load-shedding","Origin Protection → Load shedding",[933,3598,3599,3604,3606,3613],{},[953,3600,3601],{},[578,3602,3603],{},"CWA_CI_INGRESS_RATE_LIMIT_RPS",[953,3605,2091],{},[953,3607,3608,641,3610],{},[578,3609,1067],{},[578,3611,3612],{},"limit-rps",[953,3614,3615,3616,690,3618,3622],{},"ingress-nginx ",[578,3617,3612],{},[569,3619,3621],{"href":3620},"\u002Fdeployment\u002Forigin-protection#ingress-rate-limits","Origin Protection → Ingress rate limits"," before you set it",[933,3624,3625,3630,3633,3640],{},[953,3626,3627],{},[578,3628,3629],{},"CWA_CI_INGRESS_RATE_LIMIT_BURST_MULTIPLIER",[953,3631,3632],{},"unset (nginx's default, 5)",[953,3634,3635,641,3637],{},[578,3636,1067],{},[578,3638,3639],{},"limit-burst-multiplier",[953,3641,3642,3643],{},"Used only with ",[578,3644,3603],{},[933,3646,3647,3652,3654,3661],{},[953,3648,3649],{},[578,3650,3651],{},"CWA_CI_INGRESS_RATE_LIMIT_CONNECTIONS",[953,3653,2437],{},[953,3655,3656,641,3658],{},[578,3657,1067],{},[578,3659,3660],{},"limit-connections",[953,3662,3642,3663],{},[578,3664,3603],{},[560,3666,3667,3668,3672],{},"See ",[569,3669,3671],{"href":3670},"\u002Fdeployment\u002Forigin-protection#rate-limit-on-cache-misses","Rate limit on cache misses"," for what is counted and how the visitor's address is found.",[880,3674,3675],{"icon":882,"color":883},[560,3676,3677,3680,3681,3684,3685,3688,3689,3694,3695,3698,3699,663],{},[564,3678,3679],{},"The per-visitor rate limit needs the cluster's ingress controller to keep the client address."," Set ",[578,3682,3683],{},"externalTrafficPolicy: Local"," on the ingress controller's own Service, ideally with 2 replicas and a PodDisruptionBudget, as the template's own cluster runs it. With the default ",[578,3686,3687],{},"Cluster",", every request seems to come from a node's private address, so every visitor looks internal and the limit never applies (",[569,3690,3693],{"href":3691,"rel":3692},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fissues\u002F135",[904],"#135","). Check ",[578,3696,3697],{},"visitor_ip"," in php's access log. See ",[569,3700,3621],{"href":3620},[880,3702,3703],{"icon":908},[560,3704,3705,3708,3709,3712,3713,3715],{},[578,3706,3707],{},"CADDY_EDGE"," isn't a deploy variable. Only ",[578,3710,3711],{},"compose.prod.yaml"," sets it, and the chart doesn't pass it, so on Kubernetes it stays ",[578,3714,2105],{},". That's right there, because Caddy sits behind the ingress.",[594,3717,3719],{"id":3718},"cdn-purging","CDN purging",[560,3721,3722,3723,663],{},"Only needed when a CDN caches your pages. See ",[569,3724,526],{"href":3725},"\u002Fdeployment\u002Fcloudflare#purges-must-reach-cloudflare",[927,3727,3728,3740],{},[930,3729,3730],{},[933,3731,3732,3734,3736,3738],{},[936,3733,938],{},[936,3735,941],{},[936,3737,584],{},[936,3739,946],{},[948,3741,3742,3765,3786,3823,3844,3864],{},[933,3743,3744,3748,3753,3758],{},[953,3745,3746],{},[578,3747,861],{},[953,3749,3750],{},[578,3751,3752],{},"strategy hard",[953,3754,3755],{},[578,3756,3757],{},"php.caddy.cdnConfig",[953,3759,3760,3761,3764],{},"Souin's ",[578,3762,3763],{},"cdn"," block, one setting per line. Leave it unset without a CDN. See below",[933,3766,3767,3772,3774,3776],{},[953,3768,3769],{},[578,3770,3771],{},"CLOUDFLARE_API_TOKEN",[953,3773,591],{},[953,3775,591],{},[953,3777,3778,3779,3781,3782,3785],{},"GitLab only, and a name you choose: a masked variable holding a Cloudflare API token scoped to Zone → Cache Purge for the site's zone. ",[578,3780,861],{}," refers to it, so turn on ",[564,3783,3784],{},"Expand variable reference"," for that variable. The template never reads it directly",[933,3787,3788,3793,3798,3802],{},[953,3789,3790],{},[578,3791,3792],{},"CWA_API_CLOUDFLARE_PURGE_PLAN",[953,3794,3795],{},[578,3796,3797],{},"free",[953,3799,3800],{},[578,3801,3427],{},[953,3803,3804,3805,604,3807,604,3810,802,3813,3816,3817,690,3819],{},"Cloudflare only. ",[578,3806,3797],{},[578,3808,3809],{},"pro",[578,3811,3812],{},"business",[578,3814,3815],{},"enterprise",": paces purges to that plan's limit. An unknown value logs an error and uses ",[578,3818,3797],{},[569,3820,3822],{"href":3821},"\u002Fdeployment\u002Fcloudflare#queued-and-paced-purges","Queued and paced purges",[933,3824,3825,3830,3833,3837],{},[953,3826,3827],{},[578,3828,3829],{},"CWA_API_CLOUDFLARE_PURGE_REQUESTS",[953,3831,3832],{},"from the plan",[953,3834,3835],{},[578,3836,3427],{},[953,3838,3839,3840,3843],{},"Purge requests per ",[578,3841,3842],{},"CWA_API_CLOUDFLARE_PURGE_WINDOW",". Set it lower when staging and production, or several sites, share a Cloudflare account",[933,3845,3846,3850,3852,3856],{},[953,3847,3848],{},[578,3849,3842],{},[953,3851,3832],{},[953,3853,3854],{},[578,3855,3427],{},[953,3857,3858,3859,802,3861],{},"A Go duration, such as ",[578,3860,3500],{},[578,3862,3863],{},"1s",[933,3865,3866,3871,3873,3877],{},[953,3867,3868],{},[578,3869,3870],{},"CWA_API_CLOUDFLARE_PURGE_BURST",[953,3872,3832],{},[953,3874,3875],{},[578,3876,3427],{},[953,3878,3879],{},"Purge requests that can be sent at once",[560,3881,3882,3887,3888,3890,3891,604,3893,604,3896,614,3899,3902,3903,3906],{},[564,3883,3884,3886],{},[578,3885,861],{},":"," setting it replaces the default, so repeat ",[578,3889,3752],{},". For Cloudflare: ",[578,3892,3752],{},[578,3894,3895],{},"provider cloudflare",[578,3897,3898],{},"zone_id \u003Czone ID>",[578,3900,3901],{},"api_key $CLOUDFLARE_API_TOKEN",", with no ",[578,3904,3905],{},"email"," line. It is stored in the release's Secret.",[560,3908,1436,3909,3912,3913,3916,3917,3920,3921,3923],{},[578,3910,3911],{},"CWA_API_CLOUDFLARE_PURGE_*"," settings reach php as ",[578,3914,3915],{},"CLOUDFLARE_PURGE_*",", like any ",[569,3918,3919],{"href":825},"site setting",". Locally, compose passes the bare names through when they're set. ",[578,3922,861],{}," keeps its own name, because the deploy checks it before anything changes.",[560,3925,3926,3927,3929,3930,3933],{},"On GitHub, a variable can't refer to another one. Put the token directly in ",[578,3928,861],{},", store that as a secret, and map it in the deploy jobs' ",[578,3931,3932],{},"env:",", because the template's workflows don't map it.",[560,3935,3936,3937,3939,3940,3944,3945,3947,3948,663],{},"On GitLab, scope both variables to ",[578,3938,921],{},", and protect the token only if the deploy branch is protected: see ",[569,3941,3943],{"href":3942},"\u002Fdeployment\u002Fcloudflare#secrets","Cloudflare → Secrets",". A deploy fails before rolling anything out if a ",[578,3946,3763],{}," setting has no value or an unexpanded ",[578,3949,3950],{},"$NAME",[594,3952,3954],{"id":3953},"image-variants","Image variants",[560,3956,3957,3958,3962,3963,2463,3967],{},"These control ",[569,3959,3961],{"href":3960},"\u002Fdeployment\u002Fci-cd#generating-image-variants-after-a-deploy","generating image variants after a deploy",", in one-off Kubernetes Jobs (see ",[569,3964,3966],{"href":3965},"\u002Fdeployment\u002Fkubernetes#image-variants-jobs","Kubernetes → Image Variants Jobs",[3968,3969,3970,3971],"em",{},"Since template 2.0.0-alpha.27 · ",[569,3972,3975],{"href":3973,"rel":3974},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Freleases\u002Ftag\u002Fv2.0.0-alpha.27",[904],"release notes",[927,3977,3978,3990],{},[930,3979,3980],{},[933,3981,3982,3984,3986,3988],{},[936,3983,938],{},[936,3985,941],{},[936,3987,584],{},[936,3989,946],{},[948,3991,3992,4009,4025,4041,4057,4081,4105,4122],{},[933,3993,3994,3999,4002,4004],{},[953,3995,3996],{},[578,3997,3998],{},"CWA_CI_IMAGE_VARIANTS_PRODUCTION",[953,4000,4001],{},"on",[953,4003,591],{},[953,4005,1154,4006,4008],{},[578,4007,1157],{}," to stop generating variants after production deploys",[933,4010,4011,4016,4018,4020],{},[953,4012,4013],{},[578,4014,4015],{},"CWA_CI_IMAGE_VARIANTS_STAGING",[953,4017,2729],{},[953,4019,591],{},[953,4021,1154,4022,4024],{},[578,4023,1146],{}," to run it after staging deploys",[933,4026,4027,4032,4034,4036],{},[953,4028,4029],{},[578,4030,4031],{},"CWA_CI_IMAGE_VARIANTS_CANARY",[953,4033,2729],{},[953,4035,591],{},[953,4037,1154,4038,4040],{},[578,4039,1146],{}," to run it after canary deploys",[933,4042,4043,4048,4050,4052],{},[953,4044,4045],{},[578,4046,4047],{},"CWA_CI_IMAGE_VARIANTS_REVIEW",[953,4049,2729],{},[953,4051,591],{},[953,4053,1154,4054,4056],{},[578,4055,1146],{}," to run it after review deploys",[933,4058,4059,4064,4069,4075],{},[953,4060,4061],{},[578,4062,4063],{},"CWA_CI_IMAGE_VARIANTS_MEMORY",[953,4065,4066],{},[578,4067,4068],{},"1536Mi",[953,4070,4071,4074],{},[578,4072,4073],{},"cronjobs.imageVariants.resources"," (memory request and limit)",[953,4076,4077,4078,1602],{},"Each Job's memory. Raise it if a Job is OOM-killed (exit ",[578,4079,4080],{},"137",[933,4082,4083,4088,4092,4097],{},[953,4084,4085],{},[578,4086,4087],{},"CWA_CI_IMAGE_VARIANTS_MAX_MEMORY",[953,4089,4090],{},[578,4091,2914],{},[953,4093,4094],{},[578,4095,4096],{},"cronjobs.imageVariants.maxMemory",[953,4098,4099,4102,4103],{},[578,4100,4101],{},"imagine:generate --max-memory",": the resident memory at which a Job stops and the next one carries on. Keep it well under ",[578,4104,4063],{},[933,4106,4107,4112,4117,4119],{},[953,4108,4109],{},[578,4110,4111],{},"CWA_CI_IMAGE_VARIANTS_MAX_RUNS",[953,4113,4114],{},[578,4115,4116],{},"50",[953,4118,591],{},[953,4120,4121],{},"The most Jobs one deploy creates. Variants still missing after that are left to the next deploy",[933,4123,4124,4129,4134,4136],{},[953,4125,4126],{},[578,4127,4128],{},"CWA_CI_IMAGE_VARIANTS_TIMEOUT",[953,4130,4131],{},[578,4132,4133],{},"1900",[953,4135,591],{},[953,4137,4138,4139,4142],{},"Seconds the pipeline waits for each Job. The Job itself is stopped after ",[578,4140,4141],{},"cronjobs.imageVariants.activeDeadlineSeconds"," (1800)",[560,4144,4145,4146,4148],{},"As with the cache warm, there's one variable per track, because the staging job runs under ",[578,4147,917],{},". On GitHub, set them as Actions variables.",[594,4150,4152],{"id":4151},"cache-warm","Cache warm",[560,4154,3957,4155,663],{},[569,4156,4158],{"href":4157},"\u002Fdeployment\u002Fci-cd#warming-the-cache-after-a-deploy","the cache warm after a deploy",[927,4160,4161,4173],{},[930,4162,4163],{},[933,4164,4165,4167,4169,4171],{},[936,4166,938],{},[936,4168,941],{},[936,4170,584],{},[936,4172,946],{},[948,4174,4175,4191,4207,4223,4239,4256],{},[933,4176,4177,4182,4184,4186],{},[953,4178,4179],{},[578,4180,4181],{},"CWA_CI_WARM_CACHE_PRODUCTION",[953,4183,4001],{},[953,4185,591],{},[953,4187,1154,4188,4190],{},[578,4189,1157],{}," to stop warming production",[933,4192,4193,4198,4200,4202],{},[953,4194,4195],{},[578,4196,4197],{},"CWA_CI_WARM_CACHE_STAGING",[953,4199,2729],{},[953,4201,591],{},[953,4203,1154,4204,4206],{},[578,4205,1146],{}," to warm staging",[933,4208,4209,4214,4216,4218],{},[953,4210,4211],{},[578,4212,4213],{},"CWA_CI_WARM_CACHE_CANARY",[953,4215,2729],{},[953,4217,591],{},[953,4219,1154,4220,4222],{},[578,4221,1146],{}," to warm canary",[933,4224,4225,4230,4232,4234],{},[953,4226,4227],{},[578,4228,4229],{},"CWA_CI_WARM_CACHE_REVIEW",[953,4231,2729],{},[953,4233,591],{},[953,4235,1154,4236,4238],{},[578,4237,1146],{}," to warm review apps",[933,4240,4241,4246,4251,4253],{},[953,4242,4243],{},[578,4244,4245],{},"CWA_CI_WARM_CACHE_CONCURRENCY",[953,4247,4248],{},[578,4249,4250],{},"3",[953,4252,591],{},[953,4254,4255],{},"Pages requested at once",[933,4257,4258,4263,4265,4267],{},[953,4259,4260],{},[578,4261,4262],{},"CWA_CI_WARM_CACHE_INSECURE",[953,4264,591],{},[953,4266,591],{},[953,4268,1154,4269,4271],{},[578,4270,1146],{}," to skip TLS verification, e.g. for an environment with a self-signed certificate",[560,4273,4274,4275,4277],{},"Set these as GitLab CI\u002FCD variables or GitHub repository variables. There's one variable per track, not one environment-scoped variable, because the template's staging deploy job uses ",[578,4276,917],{},". An environment scope therefore can't tell staging and production apart.",[594,4279,4281],{"id":4280},"performance-audit","Performance audit",[560,4283,4284,4285,663],{},"These control the manual ",[569,4286,4287],{"href":539},"performance audit",[927,4289,4290,4302],{},[930,4291,4292],{},[933,4293,4294,4296,4298,4300],{},[936,4295,938],{},[936,4297,941],{},[936,4299,584],{},[936,4301,946],{},[948,4303,4304,4326,4347,4365,4384,4409,4426,4448,4465,4485,4504,4533],{},[933,4305,4306,4317,4319,4321],{},[953,4307,4308,604,4311,604,4314],{},[578,4309,4310],{},"CWA_CI_PERFORMANCE_AUDIT_PRODUCTION",[578,4312,4313],{},"_STAGING",[578,4315,4316],{},"_REVIEW",[953,4318,4001],{},[953,4320,591],{},[953,4322,1154,4323,4325],{},[578,4324,1157],{}," to remove that environment's audit job (GitLab) or disable it in the workflow (GitHub)",[933,4327,4328,4333,4335,4337],{},[953,4329,4330],{},[578,4331,4332],{},"CWA_CI_PERFORMANCE_AUDIT_CANARY",[953,4334,2729],{},[953,4336,591],{},[953,4338,4339,4340,4342,4343,4346],{},"GitLab only. Set ",[578,4341,1146],{}," to add ",[578,4344,4345],{},"performance audit canary",". Canary shares production's hostname and would measure whichever pod answers",[933,4348,4349,4354,4356,4358],{},[953,4350,4351],{},[578,4352,4353],{},"CWA_CI_PERFORMANCE_AUDIT_URLS",[953,4355,591],{},[953,4357,591],{},[953,4359,4360,4361,4364],{},"Pages to audit instead of the sitemap. Paths or full URLs, comma or space separated. A path such as ",[578,4362,4363],{},"\u002Fblog"," is joined to the environment URL",[933,4366,4367,4372,4376,4378],{},[953,4368,4369],{},[578,4370,4371],{},"CWA_CI_PERFORMANCE_AUDIT_MAX_PAGES",[953,4373,4374],{},[578,4375,4250],{},[953,4377,591],{},[953,4379,4380,4381,4383],{},"How many sitemap pages to audit when ",[578,4382,4353],{}," is unset",[933,4385,4386,4391,4396,4398],{},[953,4387,4388],{},[578,4389,4390],{},"CWA_CI_PERFORMANCE_AUDIT_FORM_FACTORS",[953,4392,4393],{},[578,4394,4395],{},"mobile",[953,4397,591],{},[953,4399,4400,604,4402,802,4405,4408],{},[578,4401,4395],{},[578,4403,4404],{},"desktop",[578,4406,4407],{},"mobile,desktop",". On GitHub you choose this when you start the workflow",[933,4410,4411,4416,4421,4423],{},[953,4412,4413],{},[578,4414,4415],{},"CWA_CI_PERFORMANCE_AUDIT_RUNS",[953,4417,4418],{},[578,4419,4420],{},"5",[953,4422,591],{},[953,4424,4425],{},"Runs per page. Budgets use the median run",[933,4427,4428,4433,4438,4440],{},[953,4429,4430],{},[578,4431,4432],{},"CWA_CI_PERFORMANCE_AUDIT_THROTTLING",[953,4434,4435],{},[578,4436,4437],{},"devtools",[953,4439,591],{},[953,4441,4442,4444,4445],{},[578,4443,4437],{}," (real throttling) or ",[578,4446,4447],{},"simulate",[933,4449,4450,4455,4460,4462],{},[953,4451,4452],{},[578,4453,4454],{},"CWA_CI_PERFORMANCE_AUDIT_CONFIG",[953,4456,4457],{},[578,4458,4459],{},"bin\u002Fdevops\u002Flighthouserc.json",[953,4461,591],{},[953,4463,4464],{},"The Lighthouse CI config. It holds only the budgets; the collect settings are passed on the command line",[933,4466,4467,4472,4477,4479],{},[953,4468,4469],{},[578,4470,4471],{},"CWA_CI_PERFORMANCE_AUDIT_LHCI_VERSION",[953,4473,4474],{},[578,4475,4476],{},"0.15.1",[953,4478,591],{},[953,4480,1436,4481,4484],{},[578,4482,4483],{},"@lhci\u002Fcli"," version",[933,4486,4487,4492,4494,4496],{},[953,4488,4489],{},[578,4490,4491],{},"CWA_CI_PERFORMANCE_AUDIT_INSECURE",[953,4493,591],{},[953,4495,591],{},[953,4497,1154,4498,4500,4501,4503],{},[578,4499,1146],{}," to skip TLS verification, for the sitemap fetch and in Chrome, e.g. for an environment with a ",[578,4502,1062],{}," or self-signed certificate",[933,4505,4506,4511,4516,4518],{},[953,4507,4508],{},[578,4509,4510],{},"CWA_CI_PERFORMANCE_AUDIT_OUTPUT",[953,4512,4513],{},[578,4514,4515],{},"performance-report",[953,4517,591],{},[953,4519,4520,4521,4524,4525,4528,4529,4532],{},"The directory the reports and ",[578,4522,4523],{},"summary.md"," are written to. The CI jobs collect ",[578,4526,4527],{},"performance-report\u002F"," as their artifact, so change it only when running ",[578,4530,4531],{},"performance_audit"," outside the pipeline",[933,4534,4535,4540,4547,4549],{},[953,4536,4537],{},[578,4538,4539],{},"CWA_CI_PERFORMANCE_AUDIT_IMAGE",[953,4541,4542,4543,4546],{},"a pinned ",[578,4544,4545],{},"cypress\u002Fbrowsers"," image",[953,4548,591],{},[953,4550,4551,4552],{},"GitLab only. The job image, which must have Node and Chrome. Set in ",[578,4553,4554],{},".gitlab-ci.yml",[594,4556,4558],{"id":4557},"pipeline-flags","Pipeline flags",[927,4560,4561,4573],{},[930,4562,4563],{},[933,4564,4565,4567,4569,4571],{},[936,4566,938],{},[936,4568,941],{},[936,4570,584],{},[936,4572,946],{},[948,4574,4575,4591,4607,4637,4653,4669,4685,4704,4726,4754,4775,4788],{},[933,4576,4577,4582,4586,4588],{},[953,4578,4579],{},[578,4580,4581],{},"CWA_CI_BUILD_DISABLED",[953,4583,4584],{},[578,4585,2105],{},[953,4587,591],{},[953,4589,4590],{},"Skip the build stage entirely",[933,4592,4593,4598,4602,4604],{},[953,4594,4595],{},[578,4596,4597],{},"CWA_CI_TEST_DISABLED",[953,4599,4600],{},[578,4601,2105],{},[953,4603,591],{},[953,4605,4606],{},"Skip the test stage",[933,4608,4609,4613,4618,4620],{},[953,4610,4611],{},[578,4612,801],{},[953,4614,4615,4617],{},[578,4616,1911],{}," on GitLab, unset (off) on GitHub",[953,4619,591],{},[953,4621,4622,4623,3438,4626,4628,4629,3438,4631,4634,4635],{},"Auto-deploy staging on merge to ",[578,4624,4625],{},"main",[578,4627,4554],{}," defaults it to ",[578,4630,1146],{},[578,4632,4633],{},"ci.yml"," has no default, so on GitHub staging runs only once you set it to ",[578,4636,1911],{},[933,4638,4639,4644,4648,4650],{},[953,4640,4641],{},[578,4642,4643],{},"CWA_CI_CANARY_ENABLED",[953,4645,4646],{},[578,4647,1911],{},[953,4649,591],{},[953,4651,4652],{},"GitLab only. Show the canary job. On GitHub, canary is an option of the manual production workflow",[933,4654,4655,4660,4662,4664],{},[953,4656,4657],{},[578,4658,4659],{},"CWA_CI_REVIEW_DISABLED",[953,4661,591],{},[953,4663,591],{},[953,4665,1154,4666,4668],{},[578,4667,1146],{}," to disable review apps on branches",[933,4670,4671,4676,4678,4680],{},[953,4672,4673],{},[578,4674,4675],{},"CWA_CI_PRODUCTION_DISABLED",[953,4677,591],{},[953,4679,591],{},[953,4681,4339,4682,4684],{},[578,4683,1146],{}," to remove the production job, with its fixture, image variants, cache warm and performance audit jobs",[933,4686,4687,4692,4694,4696],{},[953,4688,4689],{},[578,4690,4691],{},"CWA_CI_DISABLED",[953,4693,591],{},[953,4695,591],{},[953,4697,4698,4699,4701,4702,1602],{},"GitHub only. Set ",[578,4700,1146],{}," to stop the workflows running at all (see ",[569,4703,572],{"href":571},[933,4705,4706,4711,4715,4717],{},[953,4707,4708],{},[578,4709,4710],{},"CWA_CI_FIXTURES_ENABLED",[953,4712,4713],{},[578,4714,2105],{},[953,4716,591],{},[953,4718,1154,4719,4721,4722],{},[578,4720,1146],{}," to load fixtures after deploying: a manual job on GitLab, an automatic step on GitHub. See ",[569,4723,4725],{"href":4724},"\u002Fdeployment\u002Fci-cd#fixtures-on-a-deploy","Fixtures on a deploy",[933,4727,4728,4733,4737,4739],{},[953,4729,4730],{},[578,4731,4732],{},"CWA_CI_FIXTURES_PURGE",[953,4734,4735],{},[578,4736,2105],{},[953,4738,591],{},[953,4740,4741,4742,802,4744,4747,4748,690,4750],{},"Makes the fixture job empty the database before loading. Review apps: ",[578,4743,1146],{},[578,4745,4746],{},"\"force\"",". Production: only ",[578,4749,4746],{},[569,4751,4753],{"href":4752},"\u002Fdeployment\u002Fci-cd#purging-before-a-load","Purging before a load",[933,4755,4756,4761,4765,4770],{},[953,4757,4758],{},[578,4759,4760],{},"CWA_CI_RESET_DATABASE",[953,4762,4763],{},[578,4764,2105],{},[953,4766,4767],{},[578,4768,4769],{},"php.resetDatabase",[953,4771,1154,4772,4774],{},[578,4773,1146],{}," to make the php pod drop the whole schema before it runs migrations, every time it starts. Never leave it on",[933,4776,4777,4781,4783,4785],{},[953,4778,4779],{},[578,4780,1417],{},[953,4782,591],{},[953,4784,591],{},[953,4786,4787],{},"Any value uninstalls the release before the deploy installs it again",[933,4789,4790,4795,4797,4799],{},[953,4791,4792],{},[578,4793,4794],{},"CWA_CI_TRACE",[953,4796,591],{},[953,4798,591],{},[953,4800,4801,4802,4804,4805,1602],{},"Any value makes ",[578,4803,1230],{}," print each command it runs (",[578,4806,4807],{},"set -x",[594,4809,826],{"id":4810},"site-settings",[560,4812,4813,4814,4817,4818,604,4823,604,4830,573],{},"A site's own settings, such as an analytics ID for Nuxt or an API key for a Symfony service, are CI variables. The Kubernetes deploy passes them to the containers by their prefix, so adding one needs no change to ",[578,4815,4816],{},"k8s.sh",", the chart or a workflow (",[569,4819,4822],{"href":4820,"rel":4821},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fissues\u002F125",[904],"#125",[569,4824,4827],{"href":4825,"rel":4826},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F497e8ac74cf3ba8b066982df0192bff238abd906",[904],[578,4828,4829],{},"497e8ac",[569,4831,4834],{"href":4832,"rel":4833},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F75a416bc14edcdd601918bf44fa291f391abcbcf",[904],[578,4835,4836],{},"75a416b",[927,4838,4839,4855],{},[930,4840,4841],{},[933,4842,4843,4846,4849,4852],{},[936,4844,4845],{},"CI variable",[936,4847,4848],{},"Reaches",[936,4850,4851],{},"As",[936,4853,4854],{},"Stored in",[948,4856,4857,4881,4903],{},[933,4858,4859,4864,4867,4872],{},[953,4860,4861],{},[578,4862,4863],{},"NUXT_PUBLIC_\u003CNAME>",[953,4865,4866],{},"The Nuxt pods",[953,4868,4869,4871],{},[578,4870,4863],{},", unchanged",[953,4873,4874,4875,769,4878,1602],{},"The ConfigMap ",[578,4876,4877],{},"\u003Cfullname>-pwa-env",[578,4879,4880],{},"siteEnv.pwa",[933,4882,4883,4889,4891,4895],{},[953,4884,4885,4886],{},"Any other ",[578,4887,4888],{},"NUXT_\u003CNAME>",[953,4890,4866],{},[953,4892,4893,4871],{},[578,4894,4888],{},[953,4896,4897,4898,769,4900,1602],{},"The Secret ",[578,4899,4877],{},[578,4901,4902],{},"siteEnv.pwaSecret",[933,4904,4905,4909,4912,4917],{},[953,4906,4907],{},[578,4908,3372],{},[953,4910,4911],{},"The php pod and the orphan scan CronJob",[953,4913,4914,4916],{},[578,4915,3376],{},", without the prefix",[953,4918,4897,4919,769,4922,1602],{},[578,4920,4921],{},"\u003Cfullname>-api-env",[578,4923,3427],{},[560,4925,4926,4927,4930,4931,4935,4936,4939,4940,802,4942,4944],{},"The chart adds them to the containers with ",[578,4928,4929],{},"envFrom"," (see ",[569,4932,4934],{"href":4933},"\u002Fdeployment\u002Fkubernetes#site-settings","Kubernetes → Site Settings","). The API's settings need the ",[578,4937,4938],{},"CWA_API_"," prefix because its own names, such as ",[578,4941,685],{},[578,4943,772],{},", are too generic to pass through whole.",[560,4946,4947,4952,4953,4956,4957,4960,4961,4963],{},[564,4948,3330,4949,4951],{},[578,4950,4938],{}," value is left out",", and the log says so (",[578,4954,4955],{},"api: \u003CNAME> left unset, CWA_API_\u003CNAME> is empty","). An empty variable would replace the Caddyfile's ",[578,4958,4959],{},"{$NAME:default}"," and Symfony's defaults, so leaving it out keeps them. An empty ",[578,4962,821],{}," value is still delivered, because Nuxt applies an empty value on purpose.",[1776,4965,4967],{"id":4966},"the-apis-optional-settings","The API's optional settings",[560,4969,4970,4971,4973,4974,4976,4977,573],{},"The template's own optional API settings go through the same passthrough. Set them as ",[578,4972,3372],{}," in CI; the local compose stack uses the bare ",[578,4975,3376],{},". Unset, the default in the Caddyfile or in Symfony applies (",[569,4978,4981],{"href":4979,"rel":4980},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002Fd64c278997770b07fb0c2d04ed2fa7383059ab03",[904],[578,4982,4983],{},"d64c278",[927,4985,4986,4995],{},[930,4987,4988],{},[933,4989,4990,4992],{},[936,4991,4845],{},[936,4993,4994],{},"What it sets",[948,4996,4997,5021,5042,5055,5071,5084],{},[933,4998,4999,5015],{},[953,5000,5001,604,5003,604,5005,604,5007,604,5009,604,5011,604,5013],{},[578,5002,3417],{},[578,5004,3453],{},[578,5006,3380],{},[578,5008,3495],{},[578,5010,3517],{},[578,5012,3543],{},[578,5014,3570],{},[953,5016,5017,5018],{},"Origin protection. See ",[569,5019,3352],{"href":5020},"#origin-protection",[933,5022,5023,5036],{},[953,5024,5025,604,5027,604,5030,604,5033],{},[578,5026,3792],{},[578,5028,5029],{},"_REQUESTS",[578,5031,5032],{},"_WINDOW",[578,5034,5035],{},"_BURST",[953,5037,5038,5039],{},"Cloudflare purge pacing. See ",[569,5040,3719],{"href":5041},"#cdn-purging",[933,5043,5044,5049],{},[953,5045,5046],{},[578,5047,5048],{},"CWA_API_CADDY_GLOBAL_CONFIG",[953,5050,5051,5052],{},"Extra lines for the Caddyfile's global options block, such as ",[578,5053,5054],{},"debug",[933,5056,5057,5062],{},[953,5058,5059],{},[578,5060,5061],{},"CWA_API_JWT_COOKIE_SAMESITE",[953,5063,1436,5064,5067,5068],{},[578,5065,5066],{},"SameSite"," attribute of the session, JWT and refresh token cookies. Default ",[578,5069,5070],{},"lax",[933,5072,5073,5078],{},[953,5074,5075],{},[578,5076,5077],{},"CWA_API_MERCURE_JWT_ALGORITHM",[953,5079,5080,5081],{},"The algorithm Symfony signs Mercure tokens with. Default ",[578,5082,5083],{},"hmac.sha256",[933,5085,5086,5091],{},[953,5087,5088],{},[578,5089,5090],{},"CWA_API_WEBSITE_NAME",[953,5092,5093,5094,5097,5098,5101,5102,5104,5105,769,5110,1602],{},"The site's name in emails (the bundle's ",[578,5095,5096],{},"website_name","). Default ",[578,5099,5100],{},"New Website",", from ",[578,5103,1666],{},". A multi-site project can export it per site from ",[569,5106,5107],{"href":1404},[578,5108,5109],{},"project.sh",[569,5111,5114],{"href":5112,"rel":5113},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002Fc1a3201e24378ef54e7bd64393110bb6b1a0cfce",[904],[578,5115,5116],{},"c1a3201",[560,5118,5119,5120,802,5122,5125],{},"Their bare CI names, such as ",[578,5121,3384],{},[578,5123,5124],{},"CLOUDFLARE_PURGE_PLAN",", are no longer read by the deploy.",[560,5127,5128],{},"Each side reads them as it reads any environment variable:",[627,5130,5131,5161],{},[632,5132,5133,5136,5137,5139,5140,2941,5143,3438,5146,5149,5150,5153,5154,5157,5158,663],{},[564,5134,5135],{},"Nuxt"," uses a ",[578,5138,821],{}," variable only for a key you declare in ",[578,5141,5142],{},"runtimeConfig",[578,5144,5145],{},"app\u002Fnuxt.config.ts",[578,5147,5148],{},"NUXT_PUBLIC_ANALYTICS_ID"," sets ",[578,5151,5152],{},"runtimeConfig.public.analyticsId",", and ",[578,5155,5156],{},"NUXT_MAPS_KEY"," sets the server-only ",[578,5159,5160],{},"runtimeConfig.mapsKey",[632,5162,5163,5166,5167,5170,5171,1008,5174,5177,5178,5180],{},[564,5164,5165],{},"Symfony"," reads ",[578,5168,5169],{},"CWA_API_MAPS_KEY"," as ",[578,5172,5173],{},"MAPS_KEY",[578,5175,5176],{},"'%env(MAPS_KEY)%'",". Give it a default in ",[578,5179,1666],{}," so the local stack still boots.",[1819,5182,5186],{"className":5183,"code":5184,"language":5185,"meta":1824,"style":1824},"language-ts shiki shiki-themes github-light github-dark material-theme-palenight","\u002F\u002F app\u002Fnuxt.config.ts\nexport default defineNuxtConfig({\n    runtimeConfig: {\n        mapsKey: '',          \u002F\u002F NUXT_MAPS_KEY, server only\n        public: {\n            analyticsId: '',  \u002F\u002F NUXT_PUBLIC_ANALYTICS_ID, sent to browsers\n        },\n    },\n})\n","ts",[578,5187,5188,5194,5215,5227,5245,5255,5270,5276,5282],{"__ignoreMap":1824},[1828,5189,5190],{"class":1830,"line":1831},[1828,5191,5193],{"class":5192},"sTBSN","\u002F\u002F app\u002Fnuxt.config.ts\n",[1828,5195,5197,5201,5204,5208,5211],{"class":1830,"line":5196},2,[1828,5198,5200],{"class":5199},"sm4w6","export",[1828,5202,5203],{"class":5199}," default",[1828,5205,5207],{"class":5206},"sKpYG"," defineNuxtConfig",[1828,5209,5210],{"class":1855},"(",[1828,5212,5214],{"class":5213},"sOvfz","{\n",[1828,5216,5218,5222,5224],{"class":1830,"line":5217},3,[1828,5219,5221],{"class":5220},"sDHlG","    runtimeConfig",[1828,5223,3886],{"class":5213},[1828,5225,5226],{"class":5213}," {\n",[1828,5228,5230,5233,5235,5239,5242],{"class":1830,"line":5229},4,[1828,5231,5232],{"class":5220},"        mapsKey",[1828,5234,3886],{"class":5213},[1828,5236,5238],{"class":5237},"seSrl"," ''",[1828,5240,5241],{"class":5213},",",[1828,5243,5244],{"class":5192},"          \u002F\u002F NUXT_MAPS_KEY, server only\n",[1828,5246,5248,5251,5253],{"class":1830,"line":5247},5,[1828,5249,5250],{"class":5220},"        public",[1828,5252,3886],{"class":5213},[1828,5254,5226],{"class":5213},[1828,5256,5258,5261,5263,5265,5267],{"class":1830,"line":5257},6,[1828,5259,5260],{"class":5220},"            analyticsId",[1828,5262,3886],{"class":5213},[1828,5264,5238],{"class":5237},[1828,5266,5241],{"class":5213},[1828,5268,5269],{"class":5192},"  \u002F\u002F NUXT_PUBLIC_ANALYTICS_ID, sent to browsers\n",[1828,5271,5273],{"class":1830,"line":5272},7,[1828,5274,5275],{"class":5213},"        },\n",[1828,5277,5279],{"class":1830,"line":5278},8,[1828,5280,5281],{"class":5213},"    },\n",[1828,5283,5285,5288],{"class":1830,"line":5284},9,[1828,5286,5287],{"class":5213},"}",[1828,5289,5290],{"class":1855},")\n",[880,5292,5293],{"icon":882,"color":883},[560,5294,5295,5301,5302,5304,5305,5308],{},[564,5296,5297,5300],{},[578,5298,5299],{},"NUXT_PUBLIC_*"," values aren't secret."," Nuxt sends public runtime config to every browser, in the page HTML. Put keys and tokens in a ",[578,5303,821],{}," name without ",[578,5306,5307],{},"PUBLIC_",", which stays on the server.",[880,5310,5311],{"icon":908},[560,5312,5313,5316,5317,614,5319,5322,5323,5326,5327,5329,5330,5333],{},[564,5314,5315],{},"Site settings are runtime only, never build args."," The values reach the containers when they start. The image builds get only what ",[578,5318,2457],{},[578,5320,5321],{},"build_app"," name, so one image serves every environment. Don't add a ",[578,5324,5325],{},"--build-arg"," or build secret for a site setting, and don't read one in a Dockerfile: read it at runtime, through ",[578,5328,5142],{}," in Nuxt or ",[578,5331,5332],{},"%env()%"," in Symfony.",[560,5335,5336,5337,5339,5340,5342,5343,5347],{},"On GitLab, every CI variable already reaches the deploy job. Remember that staging runs under ",[578,5338,917],{},", so a setting scoped to ",[578,5341,921],{}," reaches staging too. Use ",[569,5344,5346],{"href":5345},"#the-environment-name","the environment name"," where the two must behave differently.",[560,5349,5350,5351,5354,5355,802,5358,5361],{},"The deploy log prints ",[578,5352,5353],{},"Environment name: \u003Cname>"," and one line for each setting, such as ",[578,5356,5357],{},"pwa config NUXT_PUBLIC_ANALYTICS_ID (from NUXT_PUBLIC_ANALYTICS_ID)",[578,5359,5360],{},"api secret MAPS_KEY (from CWA_API_MAPS_KEY)",". It never prints a value.",[1776,5363,5365],{"id":5364},"reserved-names","Reserved names",[560,5367,5368,5369,5371,5372,5374,5375,5377,5378,5380],{},"The deploy fails, before it changes anything in the cluster, when a site setting uses a name the chart or its image already sets on that container. The chart's own ",[578,5370,3932],{}," entry would win over ",[578,5373,4929],{},", so the value would be ignored without a word. It also fails on a name that isn't usable, such as ",[578,5376,4938],{}," with nothing after it, or a ",[578,5379,3376],{}," that starts with a digit. The log lists every problem:",[1819,5382,5385],{"className":5383,"code":5384,"language":2889},[2887],"❌ Site environment variables (NUXT_*, CWA_API_*) that can't be passed through:\n  CWA_API_DATABASE_URL: DATABASE_URL is reserved on the api container: the chart or its image sets it (use its own CI variable, if it has one, or delete this one)\n",[578,5386,5384],{"__ignoreMap":1824},[560,5388,5389,5390,2941,5393,2764],{},"Reserved on the Nuxt container (",[578,5391,5392],{},"SITE_ENV_RESERVED_PWA",[578,5394,4816],{},[560,5396,5397,604,5400,604,5403,604,5406,614,5409,5412,5413,5415,5416,5419,5420,5423,5424,769,5426,604,5431,5438,5439,2463,5447,5449,5450,5452],{},[578,5398,5399],{},"NUXT_CWA_API_URL",[578,5401,5402],{},"NUXT_PUBLIC_CWA_API_URL",[578,5404,5405],{},"NUXT_PUBLIC_CWA_API_URL_BROWSER",[578,5407,5408],{},"NUXT_SITE_URL",[578,5410,5411],{},"NUXT_PUBLIC_CWA_ENVIRONMENT",". The deploy sets ",[578,5414,5408],{}," to ",[578,5417,5418],{},"https:\u002F\u002F$DOMAIN",", so cached pages' canonical URL and ",[578,5421,5422],{},"og:url"," use the site's own host, not whichever host rendered the page. A multi-site project gives each site its host through ",[578,5425,1011],{},[569,5427,5430],{"href":5428,"rel":5429},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fissues\u002F140",[904],"#140",[569,5432,5435],{"href":5433,"rel":5434},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fcommit\u002F45a379404852fc164b15f706d0aac3479c61b6ab",[904],[578,5436,5437],{},"45a3794","). The chart sets the API URLs itself (see ",[569,5440,5442,5443,5446],{"href":5441},"\u002Fdeployment\u002Fkubernetes#apiurl-should-stay-unset","Kubernetes → ",[578,5444,5445],{},"apiUrl"," should stay unset",[578,5448,5402],{}," is the deprecated name of ",[578,5451,5399],{},", and as public config it would put the internal API URL in every page.",[560,5454,5455,5456,5459,5460,2941,5462,3886],{},"Reserved on the php container (",[578,5457,5458],{},"SITE_ENV_RESERVED_API","), as the ",[578,5461,3376],{},[578,5463,3372],{},[560,5465,5466,604,5468,604,5470,604,5472,604,5475,604,5478,604,5480,604,5483,604,5486,604,5489,604,5491,604,5494,604,5496,604,5499,604,5501,604,5503,604,5505,604,5507,604,5509,604,5512,604,5514,604,5516,604,5518,604,5520,604,5522,604,5524,604,5526,604,5528,604,5530,604,5532,604,5534,604,5536,604,5539,604,5541,604,5544,604,5547,604,5550,604,5553,604,5556,604,5559,604,5562,604,5565,614,5567,663],{},[578,5467,1576],{},[578,5469,742],{},[578,5471,1535],{},[578,5473,5474],{},"APP_DEBUG",[578,5476,5477],{},"APP_ENV",[578,5479,772],{},[578,5481,5482],{},"APP_UPSTREAM",[578,5484,5485],{},"BROWSER_SERVER_NAME",[578,5487,5488],{},"CACHE_URL",[578,5490,861],{},[578,5492,5493],{},"CADDY_CACHE_EXTRA_CONFIG",[578,5495,607],{},[578,5497,5498],{},"CWA_ENVIRONMENT",[578,5500,1983],{},[578,5502,2000],{},[578,5504,2004],{},[578,5506,617],{},[578,5508,685],{},[578,5510,5511],{},"FRANKENPHP_CONFIG",[578,5513,2829],{},[578,5515,2803],{},[578,5517,708],{},[578,5519,705],{},[578,5521,712],{},[578,5523,2733],{},[578,5525,1321],{},[578,5527,1341],{},[578,5529,1304],{},[578,5531,727],{},[578,5533,730],{},[578,5535,613],{},[578,5537,5538],{},"MERCURE_EXTRA_DIRECTIVES",[578,5540,775],{},[578,5542,5543],{},"MERCURE_PUBLIC_URL",[578,5545,5546],{},"MERCURE_PUBLISHER_JWT_ALG",[578,5548,5549],{},"MERCURE_PUBLISHER_JWT_KEY",[578,5551,5552],{},"MERCURE_SUBSCRIBER_JWT_ALG",[578,5554,5555],{},"MERCURE_SUBSCRIBER_JWT_KEY",[578,5557,5558],{},"MERCURE_URL",[578,5560,5561],{},"RESET_DATABASE",[578,5563,5564],{},"SERVER_NAME",[578,5566,610],{},[578,5568,5569],{},"TRUSTED_PROXIES",[560,5571,5572,5573,802,5575,5577,5578,5580,5581,604,5583,5580,5585,604,5587,5580,5589,5153,5591,5580,5593,5595],{},"Most of these have their own CI variable, such as ",[578,5574,727],{},[578,5576,607],{}," (or ",[578,5579,2697],{}," for ",[578,5582,2733],{},[578,5584,2787],{},[578,5586,2803],{},[578,5588,2810],{},[578,5590,2829],{},[578,5592,4760],{},[578,5594,5561],{},"), so set that one instead.",[560,5597,5598,5599,5601,5602,3369,5604,802,5607,5610,5611,5615,5616,573],{},"If your own chart values add ",[578,5600,3932],{}," entries to a container, reserve their names too, without editing ",[578,5603,4816],{},[578,5605,5606],{},"SITE_ENV_RESERVED_API_EXTRA",[578,5608,5609],{},"SITE_ENV_RESERVED_PWA_EXTRA"," to a space-separated list in ",[569,5612,5613],{"href":1404},[578,5614,5109],{}," or as a CI variable (",[569,5617,5620],{"href":5618,"rel":5619},"https:\u002F\u002Fgithub.com\u002Fcomponents-web-app\u002Fcomponents-web-app\u002Fissues\u002F133",[904],"#133",[1819,5622,5627],{"className":5623,"code":5624,"filename":5625,"language":5626,"meta":1824,"style":1824},"language-sh shiki shiki-themes github-light github-dark material-theme-palenight","export SITE_ENV_RESERVED_API_EXTRA=\"MAPS_REGION\"\n","bin\u002Fdevops\u002Fproject.sh","sh",[578,5628,5629],{"__ignoreMap":1824},[1828,5630,5631,5634,5637,5640,5643,5646],{"class":1830,"line":1831},[1828,5632,5200],{"class":5633},"swB56",[1828,5635,5636],{"class":1855}," SITE_ENV_RESERVED_API_EXTRA",[1828,5638,5639],{"class":1848},"=",[1828,5641,5642],{"class":5237},"\"",[1828,5644,5645],{"class":1838},"MAPS_REGION",[1828,5647,5648],{"class":5237},"\"\n",[1776,5650,5652],{"id":5651},"on-github","On GitHub",[560,5654,5655,5656,802,5658,5660,5661,5664,5665,5668,5669,5672,5673,614,5675,5677,5678,663],{},"Variables named ",[578,5657,821],{},[578,5659,816],{}," reach the deploy through ",[578,5662,5663],{},"github-export-vars.sh",", like every other variable. GitHub can't list secrets, so each deploy job (review, staging, canary and production) also runs ",[578,5666,5667],{},"bin\u002Fdevops\u002Fgithub-export-secrets.sh",", in the step ",[3968,5670,5671],{},"Expose NUXT_* and CWA_API_* secrets to the deploy",". It receives every secret and exports only the ",[578,5674,821],{},[578,5676,816],{}," ones to the later steps, masked, including each line of a multi-line value. It runs after the kubectl and Helm setup actions, so those never see the values, and no build job runs it. So a site secret needs no workflow edit: add it under ",[564,5679,5680],{},"Settings → Secrets and variables → Actions → Secrets",[560,5682,5683],{},"The step adds a warning to the run in two cases:",[627,5685,5686,5695],{},[632,5687,5688,5691,5692,5694],{},[564,5689,5690],{},"Public value stored as a secret:"," a ",[578,5693,5299],{}," secret. It is still exported, but browsers receive it, so make it a variable.",[632,5696,5697,5700,5701,5703],{},[564,5698,5699],{},"Secret shadowed:"," a variable, or the job's ",[578,5702,3932],{},", already sets the name, so the secret isn't used. Delete one of the two.",[1776,5705,5707],{"id":5706},"the-environment-name","The environment name",[560,5709,5710,5711,5713,5714,5716,5717,2764],{},"The deploy names the environment it serves, from its track, and sets it as ",[578,5712,5498],{}," on the php pod and the orphan scan, and as ",[578,5715,5411],{}," on the Nuxt pods (Helm value ",[578,5718,5719],{},"cwaEnvironment",[927,5721,5722,5732],{},[930,5723,5724],{},[933,5725,5726,5729],{},[936,5727,5728],{},"Track",[936,5730,5731],{},"Name",[948,5733,5734,5743,5753,5763],{},[933,5735,5736,5739],{},[953,5737,5738],{},"Production",[953,5740,5741],{},[578,5742,921],{},[933,5744,5745,5748],{},[953,5746,5747],{},"Staging",[953,5749,5750],{},[578,5751,5752],{},"staging",[933,5754,5755,5758],{},[953,5756,5757],{},"Canary",[953,5759,5760],{},[578,5761,5762],{},"canary",[933,5764,5765,5768],{},[953,5766,5767],{},"Review apps",[953,5769,5770],{},[578,5771,5772],{},"review",[560,5774,5775,5776,588,5778,5780,5781,5783,5784,604,5786,614,5789,5791,5792],{},"So staging is told apart from production, although its GitLab job runs as ",[578,5777,917],{},[578,5779,5498],{}," CI variable replaces the name on every track it reaches; on GitLab, a value scoped to ",[578,5782,921],{}," reaches staging and canary too. It may hold only letters, digits, ",[578,5785,663],{},[578,5787,5788],{},"_",[578,5790,2149],{},", or the deploy fails with ",[578,5793,5794],{},"CWA_ENVIRONMENT must be letters, digits, '.', '_' or '-', not '\u003Cvalue>'.",[560,5796,5797,5798,5801,5802,5805,5806,5808],{},"Nuxt sees the name only once you declare it, as ",[578,5799,5800],{},"runtimeConfig.public.cwaEnvironment",". Symfony reads it with ",[578,5803,5804],{},"'%env(CWA_ENVIRONMENT)%'","; give it a default in ",[578,5807,1666],{}," for the local stack.",[5810,5811,5812],"style",{},"html pre.shiki code .sRCss, html code.shiki .sRCss{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#FFCB6B}html pre.shiki code .sLL54, html code.shiki .sLL54{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#C3E88D}html pre.shiki code .sVlFx, html code.shiki .sVlFx{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#89DDFF}html pre.shiki code .sPB8G, html code.shiki .sPB8G{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#BABED8}html pre.shiki code .szhYu, html code.shiki .szhYu{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#C3E88D}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 .sTBSN, html code.shiki .sTBSN{--shiki-light:#6A737D;--shiki-light-font-style:inherit;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sm4w6, html code.shiki .sm4w6{--shiki-light:#D73A49;--shiki-light-font-style:inherit;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sKpYG, html code.shiki .sKpYG{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#82AAFF}html pre.shiki code .sOvfz, html code.shiki .sOvfz{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#89DDFF}html pre.shiki code .sDHlG, html code.shiki .sDHlG{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#F07178}html pre.shiki code .seSrl, html code.shiki .seSrl{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#89DDFF}html pre.shiki code .swB56, html code.shiki .swB56{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#C792EA}",{"title":1824,"searchDepth":5196,"depth":5196,"links":5814},[5815,5816,5817,5818,5819,5820,5823,5826,5827,5832,5833,5834,5835,5836,5837,5838,5839],{"id":596,"depth":5196,"text":597},{"id":784,"depth":5196,"text":785},{"id":925,"depth":5196,"text":662},{"id":768,"depth":5196,"text":1264},{"id":1450,"depth":5196,"text":746},{"id":1679,"depth":5196,"text":716,"children":5821},[5822],{"id":1778,"depth":5217,"text":1779},{"id":1883,"depth":5196,"text":694,"children":5824},[5825],{"id":2271,"depth":5217,"text":2272},{"id":2411,"depth":5196,"text":2412},{"id":2478,"depth":5196,"text":2479,"children":5828},[5829,5830,5831],{"id":2492,"depth":5217,"text":2493},{"id":2748,"depth":5217,"text":2689},{"id":2979,"depth":5217,"text":2980},{"id":3207,"depth":5196,"text":3208},{"id":3351,"depth":5196,"text":3352},{"id":3718,"depth":5196,"text":3719},{"id":3953,"depth":5196,"text":3954},{"id":4151,"depth":5196,"text":4152},{"id":4280,"depth":5196,"text":4281},{"id":4557,"depth":5196,"text":4558},{"id":4810,"depth":5196,"text":826,"children":5840},[5841,5842,5843,5844],{"id":4966,"depth":5217,"text":4967},{"id":5364,"depth":5217,"text":5365},{"id":5651,"depth":5217,"text":5652},{"id":5706,"depth":5217,"text":5707},"Every CI\u002FCD variable the template's pipeline reads, grouped by concern, with its default and the Helm value it sets. Starts with the few a new site must set.","md",{},{"title":522,"description":5845},"afXclufTPx6XGocxrbiNYBHlNgxSPUGvLVjK4KB_TPM",[5851,5853],{"title":518,"path":519,"stem":520,"description":5852,"children":-1},"The template ships full CI\u002FCD for both GitLab CI and GitHub Actions — Docker Buildx builds, tests, per-branch review apps, and staged Kubernetes deployments via Helm.",{"title":526,"path":527,"stem":528,"description":5854,"children":-1},"Putting a CWA site behind Cloudflare. Proxy only is the recommended default. Edge caching has been tested end to end on the Free plan and is a supported choice, with an optional second rule for API responses.",1791855274342]