[{"data":1,"prerenderedAt":1486},["ShallowReactive",2],{"navigation":3,"\u002Fdeployment":553,"\u002Fdeployment-surround":1481},[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":504,"badge":555,"body":556,"description":1475,"extension":1476,"links":555,"meta":1477,"navigation":1478,"path":505,"seo":1479,"stem":506,"__hash__":1480},"docs\u002F6.deployment\u002F0.index.md",null,{"type":557,"value":558,"toc":1462},"minimark",[559,568,583,588,591,686,699,702,976,980,983,1008,1020,1024,1035,1055,1059,1062,1087,1098,1105,1122,1128,1132,1143,1147,1161,1164,1187,1194,1198,1210,1214,1316,1326,1330,1458],[560,561,562,563,567],"p",{},"A project made with ",[564,565,566],"code",{},"create-cwa"," already has a complete pipeline. It builds the images, runs the tests, and deploys to Kubernetes with Helm. This page takes a new site to production in order. Each step links to the page that explains it in full.",[569,570,572],"callout",{"icon":571},"i-heroicons-photo",[560,573,574,578,579,582],{},[575,576,577],"strong",{},"Image placeholder:"," a diagram of the first deploy: a cluster with ingress-nginx and cert-manager, a push to ",[564,580,581],{},"main"," deploying staging, DNS pointing at the ingress, then the manual production job.",[584,585,587],"h2",{"id":586},"prepare-the-cluster","Prepare the cluster",[560,589,590],{},"The pipeline deploys into a cluster you already run. It doesn't install anything cluster-wide, so set these up once:",[592,593,594,609,635,663],"ul",{},[595,596,597,600,601,604,605,608],"li",{},[575,598,599],{},"A Kubernetes cluster."," The deploy jobs install kubectl ",[564,602,603],{},"1.31.0"," and Helm ",[564,606,607],{},"3.19.0"," by default. The daily orphan scan's time zone needs Kubernetes 1.27 or later.",[595,610,611,621,622,625,626,629,630,634],{},[575,612,613,620],{},[614,615,619],"a",{"href":616,"rel":617},"https:\u002F\u002Fkubernetes.github.io\u002Fingress-nginx\u002Fdeploy\u002F",[618],"nofollow","ingress-nginx","."," The pipeline writes ingress-nginx annotations, and its rate limit and ",[564,623,624],{},"www"," redirect settings are ingress-nginx's. Give the controller's Service ",[564,627,628],{},"externalTrafficPolicy: Local",", ideally with 2 replicas and a PodDisruptionBudget. Otherwise every visitor looks internal and Caddy's per-visitor rate limit never applies (see ",[614,631,633],{"href":632},"\u002Fdeployment\u002Forigin-protection#ingress-rate-limits","Origin Protection → Ingress rate limits",").",[595,636,637,652,653,656,657,660,661,620],{},[575,638,639,644,645,648,649,620],{},[614,640,643],{"href":641,"rel":642},"https:\u002F\u002Fcert-manager.io\u002Fdocs\u002Finstallation\u002F",[618],"cert-manager"," with two ClusterIssuers, named exactly ",[564,646,647],{},"letsencrypt-staging"," and ",[564,650,651],{},"letsencrypt-prod"," The pipeline names the issuer in the ingress's ",[564,654,655],{},"cert-manager.io\u002Fcluster-issuer"," annotation, from ",[564,658,659],{},"CWA_CI_CLUSTER_ISSUER",", but never creates it. It defaults to ",[564,662,647],{},[595,664,665,668,669,674,675,678,679,682,683,685],{},[575,666,667],{},"A way for CI to reach the cluster."," On GitLab, a ",[614,670,673],{"href":671,"rel":672},"https:\u002F\u002Fdocs.gitlab.com\u002Fuser\u002Fclusters\u002Fagent\u002F",[618],"GitLab agent"," whose context you set as ",[564,676,677],{},"KUBE_CONTEXT",". On GitHub, a kubeconfig in the ",[564,680,681],{},"KUBECONFIG"," secret, and its context in the ",[564,684,677],{}," secret.",[569,687,690],{"icon":688,"color":689},"i-heroicons-exclamation-triangle","warning",[560,691,692,695,696,698],{},[575,693,694],{},"The ClusterIssuer names must match."," A ClusterIssuer with any other name is never used, and no certificate is issued. If yours have other names, set ",[564,697,659],{}," to the name on every deploy.",[560,700,701],{},"The issuers use Let's Encrypt's HTTP-01 challenge through ingress-nginx. A minimal pair, following cert-manager's own examples (use your own email address):",[703,704,709],"pre",{"className":705,"code":706,"language":707,"meta":708,"style":708},"language-yaml shiki shiki-themes github-light github-dark material-theme-palenight","apiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt-staging\nspec:\n  acme:\n    server: https:\u002F\u002Facme-staging-v02.api.letsencrypt.org\u002Fdirectory\n    email: ops@example.com\n    privateKeySecretRef:\n      name: letsencrypt-staging\n    solvers:\n      - http01:\n          ingress:\n            ingressClassName: nginx\n---\napiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt-prod\nspec:\n  acme:\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n    email: ops@example.com\n    privateKeySecretRef:\n      name: letsencrypt-prod\n    solvers:\n      - http01:\n          ingress:\n            ingressClassName: nginx\n","yaml","",[564,710,711,728,739,748,759,767,775,786,797,805,815,823,834,842,853,860,869,878,885,895,902,909,919,928,935,944,951,960,967],{"__ignoreMap":708},[712,713,716,720,724],"span",{"class":714,"line":715},"line",1,[712,717,719],{"class":718},"s-h7I","apiVersion",[712,721,723],{"class":722},"sOvfz",":",[712,725,727],{"class":726},"sLL54"," cert-manager.io\u002Fv1\n",[712,729,731,734,736],{"class":714,"line":730},2,[712,732,733],{"class":718},"kind",[712,735,723],{"class":722},[712,737,738],{"class":726}," ClusterIssuer\n",[712,740,742,745],{"class":714,"line":741},3,[712,743,744],{"class":718},"metadata",[712,746,747],{"class":722},":\n",[712,749,751,754,756],{"class":714,"line":750},4,[712,752,753],{"class":718},"  name",[712,755,723],{"class":722},[712,757,758],{"class":726}," letsencrypt-staging\n",[712,760,762,765],{"class":714,"line":761},5,[712,763,764],{"class":718},"spec",[712,766,747],{"class":722},[712,768,770,773],{"class":714,"line":769},6,[712,771,772],{"class":718},"  acme",[712,774,747],{"class":722},[712,776,778,781,783],{"class":714,"line":777},7,[712,779,780],{"class":718},"    server",[712,782,723],{"class":722},[712,784,785],{"class":726}," https:\u002F\u002Facme-staging-v02.api.letsencrypt.org\u002Fdirectory\n",[712,787,789,792,794],{"class":714,"line":788},8,[712,790,791],{"class":718},"    email",[712,793,723],{"class":722},[712,795,796],{"class":726}," ops@example.com\n",[712,798,800,803],{"class":714,"line":799},9,[712,801,802],{"class":718},"    privateKeySecretRef",[712,804,747],{"class":722},[712,806,808,811,813],{"class":714,"line":807},10,[712,809,810],{"class":718},"      name",[712,812,723],{"class":722},[712,814,758],{"class":726},[712,816,818,821],{"class":714,"line":817},11,[712,819,820],{"class":718},"    solvers",[712,822,747],{"class":722},[712,824,826,829,832],{"class":714,"line":825},12,[712,827,828],{"class":722},"      -",[712,830,831],{"class":718}," http01",[712,833,747],{"class":722},[712,835,837,840],{"class":714,"line":836},13,[712,838,839],{"class":718},"          ingress",[712,841,747],{"class":722},[712,843,845,848,850],{"class":714,"line":844},14,[712,846,847],{"class":718},"            ingressClassName",[712,849,723],{"class":722},[712,851,852],{"class":726}," nginx\n",[712,854,856],{"class":714,"line":855},15,[712,857,859],{"class":858},"sRCss","---\n",[712,861,863,865,867],{"class":714,"line":862},16,[712,864,719],{"class":718},[712,866,723],{"class":722},[712,868,727],{"class":726},[712,870,872,874,876],{"class":714,"line":871},17,[712,873,733],{"class":718},[712,875,723],{"class":722},[712,877,738],{"class":726},[712,879,881,883],{"class":714,"line":880},18,[712,882,744],{"class":718},[712,884,747],{"class":722},[712,886,888,890,892],{"class":714,"line":887},19,[712,889,753],{"class":718},[712,891,723],{"class":722},[712,893,894],{"class":726}," letsencrypt-prod\n",[712,896,898,900],{"class":714,"line":897},20,[712,899,764],{"class":718},[712,901,747],{"class":722},[712,903,905,907],{"class":714,"line":904},21,[712,906,772],{"class":718},[712,908,747],{"class":722},[712,910,912,914,916],{"class":714,"line":911},22,[712,913,780],{"class":718},[712,915,723],{"class":722},[712,917,918],{"class":726}," https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n",[712,920,922,924,926],{"class":714,"line":921},23,[712,923,791],{"class":718},[712,925,723],{"class":722},[712,927,796],{"class":726},[712,929,931,933],{"class":714,"line":930},24,[712,932,802],{"class":718},[712,934,747],{"class":722},[712,936,938,940,942],{"class":714,"line":937},25,[712,939,810],{"class":718},[712,941,723],{"class":722},[712,943,894],{"class":726},[712,945,947,949],{"class":714,"line":946},26,[712,948,820],{"class":718},[712,950,747],{"class":722},[712,952,954,956,958],{"class":714,"line":953},27,[712,955,828],{"class":722},[712,957,831],{"class":718},[712,959,747],{"class":722},[712,961,963,965],{"class":714,"line":962},28,[712,964,839],{"class":718},[712,966,747],{"class":722},[712,968,970,972,974],{"class":714,"line":969},29,[712,971,847],{"class":718},[712,973,723],{"class":722},[712,975,852],{"class":726},[584,977,979],{"id":978},"create-the-namespaces","Create the namespaces",[560,981,982],{},"The pipeline never creates namespaces. It only deploys into ones that exist, with role bindings for the identity CI deploys with:",[592,984,985,1002],{},[595,986,987,990,991,994,995,998,999,685],{},[575,988,989],{},"Production's namespace",", which staging and canary share. On GitLab it's ",[564,992,993],{},"\u003Cproject name>-production"," unless you set ",[564,996,997],{},"KUBE_NAMESPACE",". On GitHub, set its name in the ",[564,1000,1001],{},"KUBE_NAMESPACE_PRODUCTION",[595,1003,1004,1007],{},[575,1005,1006],{},"One namespace per branch"," that should get a review app. A branch without one is skipped, and its pipeline shows orange.",[560,1009,1010,1011,1015,1016,634],{},"How to create them, and the role binding to add, is in ",[614,1012,1014],{"href":1013},"\u002Fdeployment\u002Fci-cd#creating-a-namespace-for-a-branch","CI\u002FCD → Creating a namespace for a branch",". The CI identity also needs access to cert-manager's Certificates in production's namespace, so changing hostnames later causes no downtime (see ",[614,1017,1019],{"href":1018},"\u002Fdeployment\u002Fci-cd#permissions","CI\u002FCD → Permissions",[584,1021,1023],{"id":1022},"set-the-ci-variables","Set the CI variables",[560,1025,1026,1027,1030,1031,620],{},"The CLI sets up GitLab CI or GitHub Actions when you create the project. Set the few variables a new site needs: the cluster connection, the domain (",[564,1028,1029],{},"KUBE_INGRESS_BASE_DOMAIN","), an external database, the media bucket, the mailer and the first admin's password. The checklist is ",[614,1032,1034],{"href":1033},"\u002Fdeployment\u002Fvariables#minimum-for-a-new-site","CI\u002FCD Variables → Minimum for a new site",[569,1036,1038],{"icon":1037},"i-heroicons-light-bulb",[560,1039,1040,1043,1044,648,1047,1050,1051,634],{},[575,1041,1042],{},"Use an external database for a real site."," The in-cluster PostgreSQL keeps no data by default, so it loses everything when its pod is replaced. Set ",[564,1045,1046],{},"DATABASE_URL",[564,1048,1049],{},"CWA_CI_POSTGRES_ENABLED=\"false\""," (see ",[614,1052,1054],{"href":1053},"\u002Fdeployment\u002Fvariables#database","CI\u002FCD Variables → Database",[584,1056,1058],{"id":1057},"point-dns-at-the-ingress","Point DNS at the ingress",[560,1060,1061],{},"Let's Encrypt's HTTP-01 challenge must reach the cluster's ingress, so point these hostnames at the ingress controller's address:",[592,1063,1064,1074,1080],{},[595,1065,1066,1067,1069,1070,1073],{},"production: ",[564,1068,1029],{},", and any ",[564,1071,1072],{},"KUBE_INGRESS_ALIAS_DOMAINS",";",[595,1075,1076,1077,1073],{},"staging: ",[564,1078,1079],{},"staging.\u003CKUBE_INGRESS_BASE_DOMAIN>",[595,1081,1082,1083,1086],{},"review apps: ",[564,1084,1085],{},"\u003Cbranch>-review.\u003CKUBE_INGRESS_BASE_DOMAIN>",", usually through a wildcard record.",[560,1088,1089,1090,1093,1094,634],{},"If Cloudflare is in front, leave each record ",[575,1091,1092],{},"DNS only"," until its first certificate is issued (see ",[614,1095,1097],{"href":1096},"\u002Fdeployment\u002Fcloudflare#each-deploy-track","Cloudflare → Each deploy track",[584,1099,1101,1102,1104],{"id":1100},"push-main-and-check-staging","Push ",[564,1103,581],{}," and check staging",[560,1106,1107,1108,1110,1111,1114,1115,1050,1118,634],{},"A push to ",[564,1109,581],{}," builds the images, runs the tests and deploys staging. On GitHub, staging runs only once you set the ",[564,1112,1113],{},"CWA_CI_STAGING_ENABLED"," variable to ",[564,1116,1117],{},"true",[614,1119,1121],{"href":1120},"\u002Fdeployment\u002Fvariables#pipeline-flags","Pipeline flags",[560,1123,1124,1125,1127],{},"Staging gets a certificate from ",[564,1126,647],{},", which browsers don't trust, so expect a certificate warning. That's on purpose: if a hostname or the DNS is wrong, the challenge fails against the staging issuer's generous limits instead of Let's Encrypt's production limits. Check that staging serves the site, warning aside.",[584,1129,1131],{"id":1130},"switch-to-trusted-certificates","Switch to trusted certificates",[560,1133,1134,1135,1138,1139,1142],{},"Once the domain points at the cluster and staging's certificate was issued, set ",[564,1136,1137],{},"CWA_CI_CLUSTER_ISSUER=letsencrypt-prod",", and keep it on every production site. On GitLab, a variable scoped to ",[564,1140,1141],{},"production"," reaches staging and canary too.",[584,1144,1146],{"id":1145},"deploy-production","Deploy production",[560,1148,1149,1150,1152,1153,1156,1157,634],{},"Production is a manual job: ",[575,1151,1141],{}," in GitLab's pipeline, or the ",[575,1154,1155],{},"Production"," workflow on GitHub, run from the Actions tab. On GitLab it waits for the builds and tests. Both remove staging and canary once production is deployed (see ",[614,1158,1160],{"href":1159},"\u002Fdeployment\u002Fci-cd#production","CI\u002FCD → Production",[560,1162,1163],{},"For the first deploy only, load the fixtures, which create the scaffold pages and the first admin user:",[1165,1166,1167,1173,1184],"ol",{},[595,1168,1169,1170,620],{},"Set ",[564,1171,1172],{},"CWA_CI_FIXTURES_ENABLED=\"true\"",[595,1174,1175,1176,1179,1180,1183],{},"Deploy production. On GitLab, then start the manual ",[575,1177,1178],{},"load fixtures production"," job. On GitHub, the ",[575,1181,1182],{},"Load fixtures"," step runs after the deploy by itself.",[595,1185,1186],{},"Remove the variable. On GitHub the step runs after every deploy while it's set.",[560,1188,1189,1190,620],{},"Fixture loads append and never delete content. See ",[614,1191,1193],{"href":1192},"\u002Fdeployment\u002Fci-cd#fixtures-on-a-deploy","CI\u002FCD → Fixtures on a deploy",[584,1195,1197],{"id":1196},"optional-put-cloudflare-in-front","Optional: put Cloudflare in front",[560,1199,1200,1201,1205,1206,620],{},"Proxy only, with no edge caching, is the recommended start: ",[614,1202,1204],{"href":1203},"\u002Fdeployment\u002Fcloudflare#option-a-proxy-only","Cloudflare → Option A",". Turn on edge caching later, if you want it, with ",[614,1207,1209],{"href":1208},"\u002Fdeployment\u002Fcloudflare#option-b-proxy-with-edge-caching","Option B",[584,1211,1213],{"id":1212},"check-the-site","Check the site",[592,1215,1216,1233,1292,1309],{},[595,1217,1218,1221,1222,1225,1226,1050,1229,634],{},[575,1219,1220],{},"The cache warm job"," after the production deploy lists every sitemap page with its status. Any page that isn't ",[564,1223,1224],{},"200"," is listed under ",[564,1227,1228],{},"CACHE WARM FAILED",[614,1230,1232],{"href":1231},"\u002Fdeployment\u002Fci-cd#warming-the-cache-after-a-deploy","CI\u002FCD → Warming the cache after a deploy",[595,1234,1235,1238,1239,1242,1243,1246,1247,620,1250],{},[575,1236,1237],{},"Pages come from the cache."," Request a page twice: the second response's ",[564,1240,1241],{},"cache-status"," header shows a Souin hit. Behind Cloudflare's Option A, ",[564,1244,1245],{},"cf-cache-status"," is ",[564,1248,1249],{},"DYNAMIC",[703,1251,1255],{"className":1252,"code":1253,"language":1254,"meta":708,"style":708},"language-sh shiki shiki-themes github-light github-dark material-theme-palenight","curl -sI https:\u002F\u002Fwww.example.com\u002F | grep -i -E 'cf-cache-status|cache-status'\n","sh",[564,1256,1257],{"__ignoreMap":708},[712,1258,1259,1262,1266,1269,1273,1276,1279,1282,1286,1289],{"class":714,"line":715},[712,1260,1261],{"class":858},"curl",[712,1263,1265],{"class":1264},"szhYu"," -sI",[712,1267,1268],{"class":726}," https:\u002F\u002Fwww.example.com\u002F",[712,1270,1272],{"class":1271},"sVlFx"," |",[712,1274,1275],{"class":858}," grep",[712,1277,1278],{"class":1264}," -i",[712,1280,1281],{"class":1264}," -E",[712,1283,1285],{"class":1284},"seSrl"," '",[712,1287,1288],{"class":726},"cf-cache-status|cache-status",[712,1290,1291],{"class":1284},"'\n",[595,1293,1294,1297,1298,1301,1302,648,1305,1308],{},[575,1295,1296],{},"You can sign in."," Sign in at ",[564,1299,1300],{},"\u002Flogin"," with ",[564,1303,1304],{},"ADMIN_USERNAME",[564,1306,1307],{},"ADMIN_PASSWORD",", and change the password if you left the default.",[595,1310,1311,1312,1315],{},"Optional: run the ",[614,1313,1314],{"href":539},"performance audit"," to see what a visitor experiences.",[569,1317,1318],{"icon":571},[560,1319,1320,1322,1323,1325],{},[575,1321,577],{}," a cache warm job's log after a first production deploy, every page ",[564,1324,1224],{},", with its time to first byte.",[584,1327,1329],{"id":1328},"which-page-do-i-need","Which page do I need?",[1331,1332,1333,1346],"table",{},[1334,1335,1336],"thead",{},[1337,1338,1339,1343],"tr",{},[1340,1341,1342],"th",{},"I want to…",[1340,1344,1345],{},"Page",[1347,1348,1349,1359,1368,1377,1386,1395,1404,1413,1422,1431,1440,1449],"tbody",{},[1337,1350,1351,1355],{},[1352,1353,1354],"td",{},"Run the stack locally, or on one server with compose",[1352,1356,1357],{},[614,1358,510],{"href":511},[1337,1360,1361,1364],{},[1352,1362,1363],{},"Understand what the Helm chart deploys and how it scales",[1352,1365,1366],{},[614,1367,514],{"href":515},[1337,1369,1370,1373],{},[1352,1371,1372],{},"Understand the pipeline's stages, scripts, fixtures and hostname changes",[1352,1374,1375],{},[614,1376,518],{"href":519},[1337,1378,1379,1382],{},[1352,1380,1381],{},"Look up a CI variable, its default, or the Helm value it sets",[1352,1383,1384],{},[614,1385,522],{"href":523},[1337,1387,1388,1391],{},[1352,1389,1390],{},"Put Cloudflare in front, with or without edge caching",[1352,1392,1393],{},[614,1394,526],{"href":527},[1337,1396,1397,1400],{},[1352,1398,1399],{},"Keep made-up URLs and request floods from getting past the cache",[1352,1401,1402],{},[614,1403,530],{"href":531},[1337,1405,1406,1409],{},[1352,1407,1408],{},"See every cache layer and what clears it",[1352,1410,1411],{},[614,1412,57],{"href":58},[1337,1414,1415,1418],{},[1352,1416,1417],{},"Find out how much traffic the site can take",[1352,1419,1420],{},[614,1421,534],{"href":535},[1337,1423,1424,1427],{},[1352,1425,1426],{},"Measure what one visitor experiences",[1352,1428,1429],{},[614,1430,538],{"href":539},[1337,1432,1433,1436],{},[1352,1434,1435],{},"Speed up a slow functional test suite",[1352,1437,1438],{},[614,1439,546],{"href":547},[1337,1441,1442,1445],{},[1352,1443,1444],{},"Fix an error or a symptom",[1352,1446,1447],{},[614,1448,550],{"href":551},[1337,1450,1451,1454],{},[1352,1452,1453],{},"Bring a project up to a newer template release",[1352,1455,1456],{},[614,1457,19],{"href":20},[1459,1460,1461],"style",{},"html pre.shiki code .s-h7I, html code.shiki .s-h7I{--shiki-light:#22863A;--shiki-default:#85E89D;--shiki-dark:#F07178}html pre.shiki code .sOvfz, html code.shiki .sOvfz{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#89DDFF}html pre.shiki code .sLL54, html code.shiki .sLL54{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#C3E88D}html pre.shiki code .sRCss, html code.shiki .sRCss{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#FFCB6B}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 .szhYu, html code.shiki .szhYu{--shiki-light:#005CC5;--shiki-default:#79B8FF;--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 .seSrl, html code.shiki .seSrl{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#89DDFF}",{"title":708,"searchDepth":730,"depth":730,"links":1463},[1464,1465,1466,1467,1468,1470,1471,1472,1473,1474],{"id":586,"depth":730,"text":587},{"id":978,"depth":730,"text":979},{"id":1022,"depth":730,"text":1023},{"id":1057,"depth":730,"text":1058},{"id":1100,"depth":730,"text":1469},"Push main and check staging",{"id":1130,"depth":730,"text":1131},{"id":1145,"depth":730,"text":1146},{"id":1196,"depth":730,"text":1197},{"id":1212,"depth":730,"text":1213},{"id":1328,"depth":730,"text":1329},"Take a new CWA site from a generated project to production on Kubernetes, step by step, with the template's GitLab or GitHub pipeline. Starts with what the cluster needs.","md",{},true,{"title":504,"description":1475},"0I8H-3jIbygdTtT6K0PcpSY0wHtfbAaFBjD4DBNaQgk",[1482,1484],{"title":500,"path":501,"stem":502,"description":1483,"children":-1},"How the module lets a shared cache store rendered page HTML, tags it for purging, and works out how long each page can be kept.",{"title":510,"path":511,"stem":512,"description":1485,"children":-1},"The Docker Compose stack for local development and single-server production — services, volumes, environment variables, the images and their build stages, and how Caddy caches pages.",1791855266283]