[{"data":1,"prerenderedAt":1602},["ShallowReactive",2],{"navigation":3,"\u002Fdeployment\u002Fmoving-to-kubernetes":600,"\u002Fdeployment\u002Fmoving-to-kubernetes-surround":1597},[4,24,67,93,232,529],{"title":5,"path":6,"stem":7,"children":8,"collapsed":23},"Getting Started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,11,15,19],{"title":10,"path":6,"stem":7},"Introduction",{"title":12,"path":13,"stem":14},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation",{"title":16,"path":17,"stem":18},"Key Ideas","\u002Fgetting-started\u002Fkey-ideas","1.getting-started\u002F3.key-ideas",{"title":20,"path":21,"stem":22},"Releases","\u002Fgetting-started\u002Freleases","1.getting-started\u002F4.releases",true,{"title":25,"path":26,"stem":27,"children":28,"collapsed":23},"Core Concepts","\u002Fcore-concepts","2.core-concepts\u002F0.index",[29,31,35,39,43,47,51,55,59,63],{"title":30,"path":26,"stem":27},"Overview",{"title":32,"path":33,"stem":34},"How It All Works","\u002Fcore-concepts\u002Farchitecture","2.core-concepts\u002F1.architecture",{"title":36,"path":37,"stem":38},"The Data Model","\u002Fcore-concepts\u002Fthe-data-model","2.core-concepts\u002F2.the-data-model",{"title":40,"path":41,"stem":42},"Layouts & Pages","\u002Fcore-concepts\u002Flayouts-and-pages","2.core-concepts\u002F3.layouts-and-pages",{"title":44,"path":45,"stem":46},"Dynamic Pages","\u002Fcore-concepts\u002Fdynamic-pages","2.core-concepts\u002F4.dynamic-pages",{"title":48,"path":49,"stem":50},"Components","\u002Fcore-concepts\u002Fcomponents","2.core-concepts\u002F5.components",{"title":52,"path":53,"stem":54},"Draft & Publish Workflow","\u002Fcore-concepts\u002Fdraft-and-publish","2.core-concepts\u002F6.draft-and-publish",{"title":56,"path":57,"stem":58},"The Admin Panel","\u002Fcore-concepts\u002Fadmin-panel","2.core-concepts\u002F7.admin-panel",{"title":60,"path":61,"stem":62},"Caching","\u002Fcore-concepts\u002Fcaching","2.core-concepts\u002F8.caching",{"title":64,"path":65,"stem":66},"Glossary","\u002Fcore-concepts\u002Fglossary","2.core-concepts\u002F9.glossary",{"title":68,"path":69,"stem":70,"children":71,"collapsed":23},"Guides","\u002Fguides","3.guides\u002F0.index",[72,73,77,81,85,89],{"title":30,"path":69,"stem":70},{"title":74,"path":75,"stem":76},"Your First Layout","\u002Fguides\u002Fyour-first-layout","3.guides\u002F1.your-first-layout",{"title":78,"path":79,"stem":80},"Your First Page Template","\u002Fguides\u002Fyour-first-page-template","3.guides\u002F2.your-first-page-template",{"title":82,"path":83,"stem":84},"Your First Component","\u002Fguides\u002Fyour-first-component","3.guides\u002F3.your-first-component",{"title":86,"path":87,"stem":88},"Alternative UI Variants","\u002Fguides\u002Falternative-ui-variants","3.guides\u002F4.alternative-ui-variants",{"title":90,"path":91,"stem":92},"Your First Dynamic Page","\u002Fguides\u002Fyour-first-dynamic-page","3.guides\u002F5.your-first-dynamic-page",{"title":94,"path":95,"stem":96,"children":97,"collapsed":23},"API Bundle (Back-End)","\u002Fapi","4.api\u002F0.index",[98,99,103,107,125,171,175,179,208,212,216,220,224,228],{"title":30,"path":95,"stem":96},{"title":100,"path":101,"stem":102},"Bundle Setup","\u002Fapi\u002Fbundle-setup","4.api\u002F02.bundle-setup",{"title":104,"path":105,"stem":106},"Configuration Reference","\u002Fapi\u002Fconfiguration","4.api\u002F03.configuration",{"title":108,"path":109,"stem":110,"children":111,"page":124},"Users","\u002Fapi\u002Fusers","4.api\u002F04.users",[112,116,120],{"title":113,"path":114,"stem":115},"Authentication","\u002Fapi\u002Fusers\u002Fauthentication","4.api\u002F04.users\u002F1.authentication",{"title":117,"path":118,"stem":119},"User Emails","\u002Fapi\u002Fusers\u002Fuser-emails","4.api\u002F04.users\u002F2.user-emails",{"title":121,"path":122,"stem":123},"Access Control","\u002Fapi\u002Fusers\u002Faccess-control","4.api\u002F04.users\u002F3.access-control",false,{"title":48,"path":126,"stem":127,"children":128,"page":124},"\u002Fapi\u002Fcomponents","4.api\u002F05.components",[129,133,154,167],{"title":130,"path":131,"stem":132},"Creating Components","\u002Fapi\u002Fcomponents\u002Fcreating-components","4.api\u002F05.components\u002F1.creating-components",{"title":134,"path":135,"stem":136,"children":137,"page":124},"Annotations","\u002Fapi\u002Fcomponents\u002Fannotations","4.api\u002F05.components\u002F2.annotations",[138,142,146,150],{"title":139,"path":140,"stem":141},"Publishable","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fpublishable","4.api\u002F05.components\u002F2.annotations\u002F1.publishable",{"title":143,"path":144,"stem":145},"Uploadable","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fuploadable","4.api\u002F05.components\u002F2.annotations\u002F2.uploadable",{"title":147,"path":148,"stem":149},"Timestamped","\u002Fapi\u002Fcomponents\u002Fannotations\u002Ftimestamped","4.api\u002F05.components\u002F2.annotations\u002F3.timestamped",{"title":151,"path":152,"stem":153},"Explicit Allow Only","\u002Fapi\u002Fcomponents\u002Fannotations\u002Fexplicit-allow-only","4.api\u002F05.components\u002F2.annotations\u002F4.explicit-allow-only",{"title":155,"path":156,"stem":157,"children":158,"page":124},"Built Ins","\u002Fapi\u002Fcomponents\u002Fbuilt-ins","4.api\u002F05.components\u002F3.built-ins",[159,163],{"title":160,"path":161,"stem":162},"Collection Component","\u002Fapi\u002Fcomponents\u002Fbuilt-ins\u002Fcollection-component","4.api\u002F05.components\u002F3.built-ins\u002F1.collection-component",{"title":164,"path":165,"stem":166},"Form Component","\u002Fapi\u002Fcomponents\u002Fbuilt-ins\u002Fform-component","4.api\u002F05.components\u002F3.built-ins\u002F2.form-component",{"title":168,"path":169,"stem":170},"Groups & Positions","\u002Fapi\u002Fcomponents\u002Fcomponent-groups","4.api\u002F05.components\u002F4.component-groups",{"title":172,"path":173,"stem":174},"Page Data & Nested Pages","\u002Fapi\u002Fdynamic-pages","4.api\u002F06.dynamic-pages",{"title":176,"path":177,"stem":178},"Routes","\u002Fapi\u002Froutes","4.api\u002F07.routes",{"title":180,"path":181,"stem":182,"children":183,"page":124},"Data Fixtures","\u002Fapi\u002Fdata-fixtures","4.api\u002F08.data-fixtures",[184,188,192,196,200,204],{"title":185,"path":186,"stem":187},"Getting Started with Fixtures","\u002Fapi\u002Fdata-fixtures\u002Fgetting-started","4.api\u002F08.data-fixtures\u002F1.getting-started",{"title":189,"path":190,"stem":191},"Writing a Scaffold","\u002Fapi\u002Fdata-fixtures\u002Fwriting-a-scaffold","4.api\u002F08.data-fixtures\u002F2.writing-a-scaffold",{"title":193,"path":194,"stem":195},"Builder Reference","\u002Fapi\u002Fdata-fixtures\u002Fbuilder-reference","4.api\u002F08.data-fixtures\u002F3.builder-reference",{"title":197,"path":198,"stem":199},"Seeding Content","\u002Fapi\u002Fdata-fixtures\u002Fseeding-content","4.api\u002F08.data-fixtures\u002F4.seeding-content",{"title":201,"path":202,"stem":203},"Appending & Multiple Scaffolds","\u002Fapi\u002Fdata-fixtures\u002Fappending","4.api\u002F08.data-fixtures\u002F5.appending",{"title":205,"path":206,"stem":207},"Generating Fixtures","\u002Fapi\u002Fdata-fixtures\u002Fgenerating-fixtures","4.api\u002F08.data-fixtures\u002F6.generating-fixtures",{"title":209,"path":210,"stem":211},"Real-Time (Mercure)","\u002Fapi\u002Freal-time","4.api\u002F09.real-time",{"title":213,"path":214,"stem":215},"Versions & Conflicts","\u002Fapi\u002Fversions-and-conflicts","4.api\u002F09.versions-and-conflicts",{"title":217,"path":218,"stem":219},"HTTP Caching","\u002Fapi\u002Fhttp-caching","4.api\u002F10.http-caching",{"title":221,"path":222,"stem":223},"Orphaned Data","\u002Fapi\u002Forphaned-data","4.api\u002F11.orphaned-data",{"title":225,"path":226,"stem":227},"Console Commands","\u002Fapi\u002Fconsole-commands","4.api\u002F12.console-commands",{"title":229,"path":230,"stem":231},"Debugging & Errors","\u002Fapi\u002Fdebugging","4.api\u002F13.debugging",{"title":233,"path":234,"stem":235,"children":236,"collapsed":23},"Nuxt Module (Front-End)","\u002Fnuxt-module","5.nuxt-module\u002F0.index",[237,238,242,255,275,304,325,456,490,493,521,525],{"title":30,"path":234,"stem":235},{"title":239,"path":240,"stem":241},"Module Setup","\u002Fnuxt-module\u002Fmodule-setup","5.nuxt-module\u002F01.module-setup",{"title":243,"path":244,"stem":245,"children":246,"page":124},"Configuration","\u002Fnuxt-module\u002Fconfiguration","5.nuxt-module\u002F02.configuration",[247,251],{"title":248,"path":249,"stem":250},"Nuxt Config","\u002Fnuxt-module\u002Fconfiguration\u002Fnuxt-config","5.nuxt-module\u002F02.configuration\u002F1.nuxt-config",{"title":252,"path":253,"stem":254},"Site Config & SEO","\u002Fnuxt-module\u002Fconfiguration\u002Fsite-config-and-seo","5.nuxt-module\u002F02.configuration\u002F2.site-config-and-seo",{"title":256,"path":257,"stem":258,"children":259,"page":124},"Building Your Ui","\u002Fnuxt-module\u002Fbuilding-your-ui","5.nuxt-module\u002F03.building-your-ui",[260,264,268,271],{"title":261,"path":262,"stem":263},"Layouts","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-layouts","5.nuxt-module\u002F03.building-your-ui\u002F1.creating-layouts",{"title":265,"path":266,"stem":267},"Page Templates","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-page-templates","5.nuxt-module\u002F03.building-your-ui\u002F2.creating-page-templates",{"title":48,"path":269,"stem":270},"\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcreating-components","5.nuxt-module\u002F03.building-your-ui\u002F3.creating-components",{"title":272,"path":273,"stem":274},"CLI Generator","\u002Fnuxt-module\u002Fbuilding-your-ui\u002Fcwa-cli","5.nuxt-module\u002F03.building-your-ui\u002F4.cwa-cli",{"title":276,"path":277,"stem":278,"children":279,"page":124},"Component Helpers","\u002Fnuxt-module\u002Fcomponent-helpers","5.nuxt-module\u002F04.component-helpers",[280,284,288,292,296,300],{"title":281,"path":282,"stem":283},"Images & Media","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fimages-and-uploads","5.nuxt-module\u002F04.component-helpers\u002F1.images-and-uploads",{"title":285,"path":286,"stem":287},"Collections & Pagination","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fcollections-and-pagination","5.nuxt-module\u002F04.component-helpers\u002F2.collections-and-pagination",{"title":289,"path":290,"stem":291},"HTML Content","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fhtml-content","5.nuxt-module\u002F04.component-helpers\u002F3.html-content",{"title":293,"path":294,"stem":295},"Rich-Text Editor","\u002Fnuxt-module\u002Fcomponent-helpers\u002Frich-text-editor","5.nuxt-module\u002F04.component-helpers\u002F4.rich-text-editor",{"title":297,"path":298,"stem":299},"Real-Time Updates","\u002Fnuxt-module\u002Fcomponent-helpers\u002Freal-time-updates","5.nuxt-module\u002F04.component-helpers\u002F5.real-time-updates",{"title":301,"path":302,"stem":303},"Forms","\u002Fnuxt-module\u002Fcomponent-helpers\u002Fforms","5.nuxt-module\u002F04.component-helpers\u002F6.forms",{"title":305,"path":306,"stem":307,"children":308,"page":124},"Cwa Components","\u002Fnuxt-module\u002Fcwa-components","5.nuxt-module\u002F05.cwa-components",[309,313,317,321],{"title":310,"path":311,"stem":312},"\u003CCwaComponentGroup \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-component-group","5.nuxt-module\u002F05.cwa-components\u002F1.cwa-component-group",{"title":314,"path":315,"stem":316},"\u003CCwaPage \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-page","5.nuxt-module\u002F05.cwa-components\u002F2.cwa-page",{"title":318,"path":319,"stem":320},"\u003CCwaLink \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-link","5.nuxt-module\u002F05.cwa-components\u002F3.cwa-link",{"title":322,"path":323,"stem":324},"\u003CCwaDefaultLayout \u002F>","\u002Fnuxt-module\u002Fcwa-components\u002Fcwa-default-layout","5.nuxt-module\u002F05.cwa-components\u002F5.cwa-default-layout",{"title":326,"path":327,"stem":328,"children":329,"page":124},"Composables","\u002Fnuxt-module\u002Fcomposables","5.nuxt-module\u002F06.composables",[330,333,341,366,386,411,431],{"title":30,"path":331,"stem":332},"\u002Fnuxt-module\u002Fcomposables\u002Foverview","5.nuxt-module\u002F06.composables\u002F0.overview",{"title":334,"path":335,"stem":336,"children":337,"page":124},"Layout","\u002Fnuxt-module\u002Fcomposables\u002Flayout","5.nuxt-module\u002F06.composables\u002F1.layout",[338],{"title":334,"path":339,"stem":340},"\u002Fnuxt-module\u002Fcomposables\u002Flayout\u002Fuse-cwa-layout","5.nuxt-module\u002F06.composables\u002F1.layout\u002F1.use-cwa-layout",{"title":342,"path":343,"stem":344,"children":345,"page":124},"Component","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent","5.nuxt-module\u002F06.composables\u002F2.component",[346,350,354,358,362],{"title":347,"path":348,"stem":349},"Component (recommended)","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-component","5.nuxt-module\u002F06.composables\u002F2.component\u002F1.use-cwa-component",{"title":351,"path":352,"stem":353},"Resource","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-resource","5.nuxt-module\u002F06.composables\u002F2.component\u002F2.use-cwa-resource",{"title":355,"path":356,"stem":357},"File Field","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-file-field","5.nuxt-module\u002F06.composables\u002F2.component\u002F3.use-cwa-file-field",{"title":359,"path":360,"stem":361},"Collection Pagination","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-collection-pagination","5.nuxt-module\u002F06.composables\u002F2.component\u002F4.use-cwa-collection-pagination",{"title":363,"path":364,"stem":365},"Collection Resource (deprecated)","\u002Fnuxt-module\u002Fcomposables\u002Fcomponent\u002Fuse-cwa-collection-resource","5.nuxt-module\u002F06.composables\u002F2.component\u002F9.use-cwa-collection-resource",{"title":367,"path":368,"stem":369,"children":370,"page":124},"Form","\u002Fnuxt-module\u002Fcomposables\u002Fform","5.nuxt-module\u002F06.composables\u002F3.form",[371,374,378,382],{"title":367,"path":372,"stem":373},"\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form","5.nuxt-module\u002F06.composables\u002F3.form\u002F1.use-cwa-form",{"title":375,"path":376,"stem":377},"Form Input","\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form-input","5.nuxt-module\u002F06.composables\u002F3.form\u002F2.use-cwa-form-input",{"title":379,"path":380,"stem":381},"Form Repeated","\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form-repeated","5.nuxt-module\u002F06.composables\u002F3.form\u002F3.use-cwa-form-repeated",{"title":383,"path":384,"stem":385},"Form Collection","\u002Fnuxt-module\u002Fcomposables\u002Fform\u002Fuse-cwa-form-collection","5.nuxt-module\u002F06.composables\u002F3.form\u002F4.use-cwa-form-collection",{"title":387,"path":388,"stem":389,"children":390,"page":124},"Admin Manager","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager","5.nuxt-module\u002F06.composables\u002F4.admin-manager",[391,395,399,403,407],{"title":392,"path":393,"stem":394},"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":396,"path":397,"stem":398},"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":400,"path":401,"stem":402},"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":404,"path":405,"stem":406},"Remote Changes","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fon-remote-change","5.nuxt-module\u002F06.composables\u002F4.admin-manager\u002F4.on-remote-change",{"title":408,"path":409,"stem":410},"Real-Time Events","\u002Fnuxt-module\u002Fcomposables\u002Fadmin-manager\u002Fuse-cwa-realtime","5.nuxt-module\u002F06.composables\u002F4.admin-manager\u002F5.use-cwa-realtime",{"title":412,"path":413,"stem":414,"children":415,"page":124},"Utilities","\u002Fnuxt-module\u002Fcomposables\u002Futilities","5.nuxt-module\u002F06.composables\u002F5.utilities",[416,420,424,428],{"title":417,"path":418,"stem":419},"Resource Endpoint","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-cwa-resource-endpoint","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F1.use-cwa-resource-endpoint",{"title":421,"path":422,"stem":423},"Query Model","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-query-bound-model","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F2.use-query-bound-model",{"title":425,"path":426,"stem":427},"Resource Route","\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-cwa-resource-route","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F3.use-cwa-resource-route",{"title":289,"path":429,"stem":430},"\u002Fnuxt-module\u002Fcomposables\u002Futilities\u002Fuse-html-content","5.nuxt-module\u002F06.composables\u002F5.utilities\u002F4.use-html-content",{"title":432,"path":433,"stem":434,"children":435,"page":124},"Auth","\u002Fnuxt-module\u002Fcomposables\u002Fauth","5.nuxt-module\u002F06.composables\u002F6.auth",[436,440,444,448,452],{"title":437,"path":438,"stem":439},"Login","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-login","5.nuxt-module\u002F06.composables\u002F6.auth\u002F1.use-login",{"title":441,"path":442,"stem":443},"Forgot Password","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-forgot-password","5.nuxt-module\u002F06.composables\u002F6.auth\u002F2.use-forgot-password",{"title":445,"path":446,"stem":447},"Reset Password","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-reset-password","5.nuxt-module\u002F06.composables\u002F6.auth\u002F3.use-reset-password",{"title":449,"path":450,"stem":451},"Verify Email","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-verify-email","5.nuxt-module\u002F06.composables\u002F6.auth\u002F4.use-verify-email",{"title":453,"path":454,"stem":455},"Resend Verify Email","\u002Fnuxt-module\u002Fcomposables\u002Fauth\u002Fuse-resend-verify-email","5.nuxt-module\u002F06.composables\u002F6.auth\u002F5.use-resend-verify-email",{"title":457,"path":458,"stem":459,"children":460,"page":124},"Cwa Api","\u002Fnuxt-module\u002Fcwa-api","5.nuxt-module\u002F07.cwa-api",[461,464,468,472,475,478,482,486],{"title":30,"path":462,"stem":463},"\u002Fnuxt-module\u002Fcwa-api\u002Foverview","5.nuxt-module\u002F07.cwa-api\u002F1.overview",{"title":465,"path":466,"stem":467},"Resources","\u002Fnuxt-module\u002Fcwa-api\u002Fresources","5.nuxt-module\u002F07.cwa-api\u002F2.resources",{"title":469,"path":470,"stem":471},"Resources Manager","\u002Fnuxt-module\u002Fcwa-api\u002Fresources-manager","5.nuxt-module\u002F07.cwa-api\u002F3.resources-manager",{"title":432,"path":473,"stem":474},"\u002Fnuxt-module\u002Fcwa-api\u002Fauth","5.nuxt-module\u002F07.cwa-api\u002F4.auth",{"title":301,"path":476,"stem":477},"\u002Fnuxt-module\u002Fcwa-api\u002Fforms","5.nuxt-module\u002F07.cwa-api\u002F5.forms",{"title":479,"path":480,"stem":481},"Admin","\u002Fnuxt-module\u002Fcwa-api\u002Fadmin","5.nuxt-module\u002F07.cwa-api\u002F6.admin",{"title":483,"path":484,"stem":485},"Site Config","\u002Fnuxt-module\u002Fcwa-api\u002Fsite-config","5.nuxt-module\u002F07.cwa-api\u002F7.site-config",{"title":487,"path":488,"stem":489},"Orphaned Resources","\u002Fnuxt-module\u002Fcwa-api\u002Forphaned-resources","5.nuxt-module\u002F07.cwa-api\u002F8.orphaned-resources",{"title":113,"path":491,"stem":492},"\u002Fnuxt-module\u002Fauthentication","5.nuxt-module\u002F08.authentication",{"title":494,"path":495,"stem":496,"children":497,"page":124},"Cwa Layer","\u002Fnuxt-module\u002Fcwa-layer","5.nuxt-module\u002F9.cwa-layer",[498,501,505,509,513,517],{"title":30,"path":499,"stem":500},"\u002Fnuxt-module\u002Fcwa-layer\u002Foverview","5.nuxt-module\u002F9.cwa-layer\u002F1.overview",{"title":502,"path":503,"stem":504},"Auth Pages","\u002Fnuxt-module\u002Fcwa-layer\u002Fauth-pages","5.nuxt-module\u002F9.cwa-layer\u002F2.auth-pages",{"title":506,"path":507,"stem":508},"Admin Panel","\u002Fnuxt-module\u002Fcwa-layer\u002Fadmin-panel","5.nuxt-module\u002F9.cwa-layer\u002F3.admin-panel",{"title":510,"path":511,"stem":512},"Styling","\u002Fnuxt-module\u002Fcwa-layer\u002Fstyling","5.nuxt-module\u002F9.cwa-layer\u002F4.styling",{"title":514,"path":515,"stem":516},"Date Pickers","\u002Fnuxt-module\u002Fcwa-layer\u002Fdate-pickers","5.nuxt-module\u002F9.cwa-layer\u002F5.date-pickers",{"title":518,"path":519,"stem":520},"Maintenance","\u002Fnuxt-module\u002Fcwa-layer\u002Fmaintenance","5.nuxt-module\u002F9.cwa-layer\u002F6.maintenance",{"title":522,"path":523,"stem":524},"PWA & Offline","\u002Fnuxt-module\u002Fpwa","5.nuxt-module\u002F91.pwa",{"title":526,"path":527,"stem":528},"Page Caching","\u002Fnuxt-module\u002Fpage-caching","5.nuxt-module\u002F92.page-caching",{"title":530,"path":531,"stem":532,"children":533,"collapsed":23},"Deployment & Operations","\u002Fdeployment","6.deployment\u002F0.index",[534,536,540,544,548,552,556,560,564,568,572,576,580,584,588,592,596],{"title":535,"path":531,"stem":532},"Deploying a CWA Site",{"title":537,"path":538,"stem":539},"Docker","\u002Fdeployment\u002Fdocker","6.deployment\u002F1.docker",{"title":541,"path":542,"stem":543},"Deploy to One Server","\u002Fdeployment\u002Fone-server","6.deployment\u002F1.one-server",{"title":545,"path":546,"stem":547},"Kubernetes & Helm","\u002Fdeployment\u002Fkubernetes","6.deployment\u002F2.kubernetes",{"title":549,"path":550,"stem":551},"Moving From One Server to Kubernetes","\u002Fdeployment\u002Fmoving-to-kubernetes","6.deployment\u002F2.moving-to-kubernetes",{"title":553,"path":554,"stem":555},"Scaled Mode","\u002Fdeployment\u002Fscaled-mode","6.deployment\u002F2.scaled-mode",{"title":557,"path":558,"stem":559},"CI\u002FCD","\u002Fdeployment\u002Fci-cd","6.deployment\u002F3.ci-cd",{"title":561,"path":562,"stem":563},"Migrations and Backups","\u002Fdeployment\u002Fmigrations-and-backups","6.deployment\u002F3.migrations-and-backups",{"title":565,"path":566,"stem":567},"CI\u002FCD Variables","\u002Fdeployment\u002Fvariables","6.deployment\u002F4.variables",{"title":569,"path":570,"stem":571},"Cloudflare","\u002Fdeployment\u002Fcloudflare","6.deployment\u002F5.cloudflare",{"title":573,"path":574,"stem":575},"Origin Protection","\u002Fdeployment\u002Forigin-protection","6.deployment\u002F5.origin-protection",{"title":577,"path":578,"stem":579},"Load Testing","\u002Fdeployment\u002Fload-testing","6.deployment\u002F6.load-testing",{"title":581,"path":582,"stem":583},"Performance Audit","\u002Fdeployment\u002Fperformance-audit","6.deployment\u002F6.performance-audit",{"title":585,"path":586,"stem":587},"Reading Lighthouse Scores","\u002Fdeployment\u002Freading-lighthouse-scores","6.deployment\u002F6.reading-lighthouse-scores",{"title":589,"path":590,"stem":591},"Browser Tests","\u002Fdeployment\u002Fbrowser-tests","6.deployment\u002F7.browser-tests",{"title":593,"path":594,"stem":595},"Faster Functional Tests","\u002Fdeployment\u002Ffaster-functional-tests","6.deployment\u002F7.faster-functional-tests",{"title":597,"path":598,"stem":599},"Troubleshooting","\u002Fdeployment\u002Ftroubleshooting","6.deployment\u002F7.troubleshooting",{"id":601,"title":549,"badge":602,"body":603,"description":1592,"extension":1593,"links":602,"meta":1594,"navigation":23,"path":550,"seo":1595,"stem":551,"__hash__":1596},"docs\u002F6.deployment\u002F2.moving-to-kubernetes.md",null,{"type":604,"value":605,"toc":1581},"minimark",[606,615,690,701,704,709,715,758,761,768,774,914,928,936,940,992,1000,1009,1012,1029,1041,1231,1244,1254,1258,1261,1501,1506,1509,1516,1557,1574,1577],[607,608,609,610,614],"p",{},"A site that started on ",[611,612,613],"a",{"href":542},"one server"," can move to Kubernetes with the template's pipeline. The same project deploys both ways, so nothing in the code changes. What moves is the data and the settings:",[616,617,618,631],"table",{},[619,620,621],"thead",{},[622,623,624,628],"tr",{},[625,626,627],"th",{},"On one server",[625,629,630],{},"On Kubernetes",[632,633,634,652,671,682],"tbody",{},[622,635,636,645],{},[637,638,639,640,644],"td",{},"The database in the ",[641,642,643],"code",{},"database"," container",[637,646,647,648,651],{},"An external PostgreSQL database (",[641,649,650],{},"DATABASE_URL",")",[622,653,654,661],{},[637,655,656,657,660],{},"Media in the ",[641,658,659],{},"media_data"," volume",[637,662,663,664,667,668],{},"A Cloud Storage bucket (",[641,665,666],{},"GCLOUD_BUCKET","), under ",[641,669,670],{},"GCLOUD_PATH_PREFIX",[622,672,673,679],{},[637,674,675,676],{},"Secrets and hosts in ",[641,677,678],{},".env.prod",[637,680,681],{},"CI variables",[622,683,684,687],{},[637,685,686],{},"Caddy's own Let's Encrypt certificates",[637,688,689],{},"cert-manager, through the ingress",[691,692,694],"callout",{"icon":693},"i-heroicons-information-circle",[607,695,696,700],{},[697,698,699],"strong",{},"These steps haven't been run on a real site yet."," They're put together from the template's scripts and pipeline, like the single-server scripts themselves (a preview). Try the move on staging first, as below, and keep the old server until the new site has run for a few days.",[607,702,703],{},"The move has two parts. Prepare the cluster while the old server still serves the site. Then, at a quiet time, close the old site, copy the last of its data and point DNS at the cluster.",[705,706,708],"h2",{"id":707},"prepare-the-cluster","Prepare the cluster",[607,710,711,712,714],{},"Follow ",[611,713,535],{"href":531}," up to, but not including, the production deploy: prepare the cluster, create the namespaces and set the CI variables. Two of them come from the server:",[716,717,718,737],"ul",{},[719,720,721,724,725,727,728,731,732,736],"li",{},[697,722,723],{},"The database:"," an external PostgreSQL database, with ",[641,726,650],{}," and ",[641,729,730],{},"CWA_CI_POSTGRES_ENABLED=\"false\""," (see ",[611,733,735],{"href":734},"\u002Fdeployment\u002Fvariables#database","CI\u002FCD Variables › Database","). It must be PostgreSQL, like the server's.",[719,738,739,742,743,746,747,749,750,752,753,757],{},[697,740,741],{},"The media bucket:"," ",[641,744,745],{},"GCLOUD_JSON",", ",[641,748,666],{},", and ",[641,751,670],{}," if other apps share the bucket (see ",[611,754,756],{"href":755},"\u002Fdeployment\u002Fvariables#media-storage","CI\u002FCD Variables › Media storage","). A Kubernetes deploy always stores media in the bucket.",[607,759,760],{},"Deploy the same commit the server runs, so both have the same migrations.",[762,763,765,766],"h3",{"id":764},"the-secrets-from-envprod","The secrets from ",[641,767,678],{},[607,769,770,771,773],{},"Copy the values from the server's ",[641,772,678],{}," into CI variables, so the new site keeps them:",[616,775,776,790],{},[619,777,778],{},[622,779,780,784,787],{},[625,781,782],{},[641,783,678],{},[625,785,786],{},"CI variable",[625,788,789],{},"Why keep it",[632,791,792,808,822,837,851,865,881,896],{},[622,793,794,802,805],{},[637,795,796,746,799],{},[641,797,798],{},"JWT_SECRET_KEY",[641,800,801],{},"JWT_PASSPHRASE",[637,803,804],{},"The same names",[637,806,807],{},"Users stay signed in. A new key pair signs everyone out",[622,809,810,815,819],{},[637,811,812],{},[641,813,814],{},"APP_SECRET",[637,816,817],{},[641,818,814],{},[637,820,821],{},"What Symfony signed with it, such as signed URLs, stays valid",[622,823,824,829,834],{},[637,825,826],{},[641,827,828],{},"CADDY_MERCURE_JWT_SECRET",[637,830,831],{},[641,832,833],{},"MERCURE_JWT_SECRET",[637,835,836],{},"The real-time cookies browsers hold stay valid",[622,838,839,844,848],{},[637,840,841],{},[641,842,843],{},"NUXT_APP_SECRET",[637,845,846],{},[641,847,843],{},[637,849,850],{},"OG image URLs already shared keep their signature",[622,852,853,858,862],{},[637,854,855],{},[641,856,857],{},"ADMIN_PASSWORD",[637,859,860],{},[641,861,857],{},[637,863,864],{},"The next fixture load would reset the admin to it",[622,866,867,876,878],{},[637,868,869,749,872,875],{},[641,870,871],{},"MAILER_DSN",[641,873,874],{},"MAILER_EMAIL"," if you set it",[637,877,804],{},[637,879,880],{},"The site's mail relay",[622,882,883,888,893],{},[637,884,885],{},[641,886,887],{},"SITE_HOST",[637,889,890],{},[641,891,892],{},"KUBE_INGRESS_BASE_DOMAIN",[637,894,895],{},"Production is served at it",[622,897,898,903,908],{},[637,899,900],{},[641,901,902],{},"SITE_ALIASES",[637,904,905],{},[641,906,907],{},"KUBE_INGRESS_ALIAS_DOMAINS",[637,909,910,913],{},[697,911,912],{},"Comma-separated"," here, not space-separated",[607,915,916,917,920,921,923,924,927],{},"Leave ",[641,918,919],{},"JWT_PUBLIC_KEY"," out: the deploy derives it from the key and its passphrase, and fails if they don't belong together. Copy the key without the double quotes ",[641,922,678],{}," puts around it. ",[641,925,926],{},"POSTGRES_PASSWORD"," isn't needed: the external database has its own.",[607,929,930,931,935],{},"For each secret that isn't a CI variable, the deploy keeps the release's live value, or generates a new one for a new release. So the first deploy of a site without these variables generates new ones. How to set them, protected and on GitHub as secrets, is in ",[611,932,934],{"href":933},"\u002Fdeployment\u002Fvariables#secrets","CI\u002FCD Variables › Secrets",".",[762,937,939],{"id":938},"try-it-on-staging","Try it on staging",[941,942,943,966,978,981],"ol",{},[719,944,945,946,949,950,953,954,957,958,965],{},"Point ",[641,947,948],{},"staging.\u003Cdomain>"," at the cluster's ingress, and push ",[641,951,952],{},"main",". Staging gets a certificate from ",[641,955,956],{},"letsencrypt-staging",", which browsers don't trust: that's on purpose (see ",[611,959,961,962,964],{"href":960},"\u002Fdeployment#push-main-and-check-staging","Deploying a CWA Site › Push ",[641,963,952],{}," and check staging",").",[719,967,968,969,973,974,965],{},"Load a copy of the site's data into the new database, as in ",[611,970,972],{"href":971},"#copy-the-database","Copy the database",", from a recent backup. Copy the media too (",[611,975,977],{"href":976},"#copy-the-media","Copy the media",[719,979,980],{},"Check staging serves the site, with its images, and that you can sign in.",[719,982,983,984,731,987,991],{},"Once staging's certificate was issued, set ",[641,985,986],{},"CWA_CI_CLUSTER_ISSUER=letsencrypt-prod",[611,988,990],{"href":989},"\u002Fdeployment#switch-to-trusted-certificates","Deploying a CWA Site › Switch to trusted certificates","). The production deploy then gets a trusted certificate.",[607,993,994,995,999],{},"On GitLab, staging gets production's variables, so it uses the same external database (see ",[611,996,998],{"href":997},"\u002Fdeployment\u002Fci-cd#staging","CI\u002FCD › Staging","). The final copy below replaces whatever staging wrote there.",[691,1001,1003],{"icon":1002},"i-heroicons-light-bulb",[607,1004,1005,1008],{},[697,1006,1007],{},"Lower the DNS records' TTL a day before the move",", for example to 300 seconds, so visitors reach the cluster soon after you change them.",[705,1010,977],{"id":1011},"copy-the-media",[607,1013,1014,1015,1018,1019,1021,1022,1025,1026,1028],{},"Each backup's ",[641,1016,1017],{},"\u003Ctime>-media.tar.gz"," holds the ",[641,1020,659],{}," volume: uploads, and their image variants under ",[641,1023,1024],{},"cache\u002F",". The bucket needs the same files at the same paths, under the site's ",[641,1027,670],{},". The database stores each file's path, not its URL, so the paths must match.",[607,1030,1031,1032,1035,1036,1040],{},"Unpack an archive and copy it with the Google Cloud CLI. Uploaded originals never change at a path, so they get a year's ",[641,1033,1034],{},"Cache-Control","; variants get 30 days, as the API sets them when it stores a file itself (see ",[611,1037,1039],{"href":1038},"\u002Fapi\u002Fcomponents\u002Fannotations\u002Fuploadable#cache-control-on-stored-files","Uploadable › Cache-Control on Stored Files","):",[1042,1043,1048],"pre",{"className":1044,"code":1045,"language":1046,"meta":1047,"style":1047},"language-bash shiki shiki-themes github-light github-dark material-theme-palenight","mkdir media && tar -xzf backups\u002F\u003Ctime>-media.tar.gz -C media\n\ngcloud storage rsync media gs:\u002F\u002F\u003Cbucket>\u002F\u003Cprefix> --recursive \\\n  --exclude='^cache\u002F' --cache-control='public, max-age=31536000, immutable'\ngcloud storage rsync media\u002Fcache gs:\u002F\u002F\u003Cbucket>\u002F\u003Cprefix>cache --recursive \\\n  --cache-control='public, max-age=2592000'\n","bash","",[641,1049,1050,1100,1106,1153,1179,1218],{"__ignoreMap":1047},[1051,1052,1055,1059,1063,1067,1070,1074,1077,1081,1084,1088,1091,1094,1097],"span",{"class":1053,"line":1054},"line",1,[1051,1056,1058],{"class":1057},"sRCss","mkdir",[1051,1060,1062],{"class":1061},"sLL54"," media",[1051,1064,1066],{"class":1065},"sOvfz"," &&",[1051,1068,1069],{"class":1057}," tar",[1051,1071,1073],{"class":1072},"szhYu"," -xzf",[1051,1075,1076],{"class":1061}," backups\u002F",[1051,1078,1080],{"class":1079},"sVlFx","\u003C",[1051,1082,1083],{"class":1061},"tim",[1051,1085,1087],{"class":1086},"sPB8G","e",[1051,1089,1090],{"class":1079},">",[1051,1092,1093],{"class":1061},"-media.tar.gz",[1051,1095,1096],{"class":1072}," -C",[1051,1098,1099],{"class":1061}," media\n",[1051,1101,1103],{"class":1053,"line":1102},2,[1051,1104,1105],{"emptyLinePlaceholder":23},"\n",[1051,1107,1109,1112,1115,1118,1120,1123,1125,1128,1131,1133,1136,1138,1141,1144,1146,1149],{"class":1053,"line":1108},3,[1051,1110,1111],{"class":1057},"gcloud",[1051,1113,1114],{"class":1061}," storage",[1051,1116,1117],{"class":1061}," rsync",[1051,1119,1062],{"class":1061},[1051,1121,1122],{"class":1061}," gs:\u002F\u002F",[1051,1124,1080],{"class":1079},[1051,1126,1127],{"class":1061},"bucke",[1051,1129,1130],{"class":1086},"t",[1051,1132,1090],{"class":1079},[1051,1134,1135],{"class":1061},"\u002F",[1051,1137,1080],{"class":1079},[1051,1139,1140],{"class":1061},"prefi",[1051,1142,1143],{"class":1086},"x",[1051,1145,1090],{"class":1079},[1051,1147,1148],{"class":1072}," --recursive",[1051,1150,1152],{"class":1151},"sc2zw"," \\\n",[1051,1154,1156,1159,1163,1166,1168,1171,1173,1176],{"class":1053,"line":1155},4,[1051,1157,1158],{"class":1072},"  --exclude=",[1051,1160,1162],{"class":1161},"seSrl","'",[1051,1164,1165],{"class":1061},"^cache\u002F",[1051,1167,1162],{"class":1161},[1051,1169,1170],{"class":1072}," --cache-control=",[1051,1172,1162],{"class":1161},[1051,1174,1175],{"class":1061},"public, max-age=31536000, immutable",[1051,1177,1178],{"class":1161},"'\n",[1051,1180,1182,1184,1186,1188,1191,1193,1195,1197,1199,1201,1203,1205,1207,1209,1211,1214,1216],{"class":1053,"line":1181},5,[1051,1183,1111],{"class":1057},[1051,1185,1114],{"class":1061},[1051,1187,1117],{"class":1061},[1051,1189,1190],{"class":1061}," media\u002Fcache",[1051,1192,1122],{"class":1061},[1051,1194,1080],{"class":1079},[1051,1196,1127],{"class":1061},[1051,1198,1130],{"class":1086},[1051,1200,1090],{"class":1079},[1051,1202,1135],{"class":1061},[1051,1204,1080],{"class":1079},[1051,1206,1140],{"class":1061},[1051,1208,1143],{"class":1086},[1051,1210,1090],{"class":1079},[1051,1212,1213],{"class":1061},"cache",[1051,1215,1148],{"class":1072},[1051,1217,1152],{"class":1151},[1051,1219,1221,1224,1226,1229],{"class":1053,"line":1220},6,[1051,1222,1223],{"class":1072},"  --cache-control=",[1051,1225,1162],{"class":1161},[1051,1227,1228],{"class":1061},"public, max-age=2592000",[1051,1230,1178],{"class":1161},[607,1232,916,1233,1236,1237,1239,1240,1243],{},[641,1234,1235],{},"\u003Cprefix>"," empty when ",[641,1238,670],{}," is empty, and otherwise write it with its trailing slash, such as ",[641,1241,1242],{},"site-a\u002F",". If the server stored media under a prefix of its own, copy from that folder of the archive.",[607,1245,1246,1249,1250,1253],{},[641,1247,1248],{},"rsync"," only copies what changed, so copy once while preparing, and again from the final backup. Don't add ",[641,1251,1252],{},"--delete-unmatched-destination-objects",": it would delete what the new site's staging stored.",[705,1255,1257],{"id":1256},"move-the-site","Move the site",[607,1259,1260],{},"At a quiet time, ask editors to stop editing, then:",[941,1262,1263,1307,1317,1325,1332,1346,1360,1491],{},[719,1264,1265,1268,1269,1303,1306],{},[697,1266,1267],{},"Close the old site."," On the server, turn maintenance mode on, so visitors see the maintenance page and nothing changes while you copy:",[1042,1270,1272],{"className":1044,"code":1271,"language":1046,"meta":1047,"style":1047},"docker compose --env-file .env.prod exec php bin\u002Fconsole silverback:api-components:maintenance on\n",[641,1273,1274],{"__ignoreMap":1047},[1051,1275,1276,1279,1282,1285,1288,1291,1294,1297,1300],{"class":1053,"line":1054},[1051,1277,1278],{"class":1057},"docker",[1051,1280,1281],{"class":1061}," compose",[1051,1283,1284],{"class":1072}," --env-file",[1051,1286,1287],{"class":1061}," .env.prod",[1051,1289,1290],{"class":1061}," exec",[1051,1292,1293],{"class":1061}," php",[1051,1295,1296],{"class":1061}," bin\u002Fconsole",[1051,1298,1299],{"class":1061}," silverback:api-components:maintenance",[1051,1301,1302],{"class":1061}," on\n",[1304,1305],"br",{},"Admins bypass maintenance mode, so make sure nobody signed in keeps editing.",[719,1308,1309,1312,1313,1316],{},[697,1310,1311],{},"Take a final backup"," on the server with ",[641,1314,1315],{},"bin\u002Fserver\u002Fbackup.sh",", and copy its files off the server.",[719,1318,1319,1322,1323,935],{},[697,1320,1321],{},"Copy the media again"," from the final backup's archive, as in ",[611,1324,977],{"href":976},[719,1326,1327,1329,1330,935],{},[697,1328,972],{}," from the final backup's dump, as in ",[611,1331,972],{"href":971},[719,1333,1334,1337,1338,1341,1342,965],{},[697,1335,1336],{},"Point DNS at the cluster's ingress",": the site's host and every alias. With Cloudflare in front, leave them ",[697,1339,1340],{},"DNS only"," until the certificate is issued (see ",[611,1343,1345],{"href":1344},"\u002Fdeployment\u002Fcloudflare#each-deploy-track","Cloudflare › Each deploy track",[719,1347,1348,731,1351,1355,1356,1359],{},[697,1349,1350],{},"Deploy production",[611,1352,1354],{"href":1353},"\u002Fdeployment#deploy-production","Deploying a CWA Site › Deploy production","). ",[697,1357,1358],{},"Don't load the fixtures:"," the admin and the pages came with the database. cert-manager gets the certificate once DNS reaches the ingress. Until then, the ingress serves a temporary one.",[719,1361,1362,1365,1366,1369,1370],{},[697,1363,1364],{},"Open the site."," The dump was taken with maintenance mode on, and the setting is stored in the database, so the new site starts in maintenance mode too. In a ready API pod, flush the cache and turn it off, as ",[641,1367,1368],{},"restore.sh"," does on a server:",[1042,1371,1373],{"className":1044,"code":1372,"language":1046,"meta":1047,"style":1047},"kubectl get pods -n \u003Cnamespace> -l app.kubernetes.io\u002Fname=cwa,app.kubernetes.io\u002Finstance=\u003Crelease>\nkubectl exec -n \u003Cnamespace> \u003Cready pod> -- php bin\u002Fconsole silverback:api-components:purge-http-cache\nkubectl exec -n \u003Cnamespace> \u003Cready pod> -- php bin\u002Fconsole silverback:api-components:maintenance off\n",[641,1374,1375,1415,1454],{"__ignoreMap":1047},[1051,1376,1377,1380,1383,1386,1389,1392,1395,1397,1399,1402,1405,1407,1410,1412],{"class":1053,"line":1054},[1051,1378,1379],{"class":1057},"kubectl",[1051,1381,1382],{"class":1061}," get",[1051,1384,1385],{"class":1061}," pods",[1051,1387,1388],{"class":1072}," -n",[1051,1390,1391],{"class":1079}," \u003C",[1051,1393,1394],{"class":1061},"namespac",[1051,1396,1087],{"class":1086},[1051,1398,1090],{"class":1079},[1051,1400,1401],{"class":1072}," -l",[1051,1403,1404],{"class":1061}," app.kubernetes.io\u002Fname=cwa,app.kubernetes.io\u002Finstance=",[1051,1406,1080],{"class":1079},[1051,1408,1409],{"class":1061},"releas",[1051,1411,1087],{"class":1086},[1051,1413,1414],{"class":1079},">\n",[1051,1416,1417,1419,1421,1423,1425,1427,1429,1431,1433,1436,1439,1442,1444,1447,1449,1451],{"class":1053,"line":1102},[1051,1418,1379],{"class":1057},[1051,1420,1290],{"class":1061},[1051,1422,1388],{"class":1072},[1051,1424,1391],{"class":1079},[1051,1426,1394],{"class":1061},[1051,1428,1087],{"class":1086},[1051,1430,1090],{"class":1079},[1051,1432,1391],{"class":1079},[1051,1434,1435],{"class":1061},"ready",[1051,1437,1438],{"class":1061}," po",[1051,1440,1441],{"class":1086},"d",[1051,1443,1090],{"class":1079},[1051,1445,1446],{"class":1072}," --",[1051,1448,1293],{"class":1061},[1051,1450,1296],{"class":1061},[1051,1452,1453],{"class":1061}," silverback:api-components:purge-http-cache\n",[1051,1455,1456,1458,1460,1462,1464,1466,1468,1470,1472,1474,1476,1478,1480,1482,1484,1486,1488],{"class":1053,"line":1108},[1051,1457,1379],{"class":1057},[1051,1459,1290],{"class":1061},[1051,1461,1388],{"class":1072},[1051,1463,1391],{"class":1079},[1051,1465,1394],{"class":1061},[1051,1467,1087],{"class":1086},[1051,1469,1090],{"class":1079},[1051,1471,1391],{"class":1079},[1051,1473,1435],{"class":1061},[1051,1475,1438],{"class":1061},[1051,1477,1441],{"class":1086},[1051,1479,1090],{"class":1079},[1051,1481,1446],{"class":1072},[1051,1483,1293],{"class":1061},[1051,1485,1296],{"class":1061},[1051,1487,1299],{"class":1061},[1051,1489,1490],{"class":1061}," off\n",[719,1492,1493,1496,1497,965],{},[697,1494,1495],{},"Check the site:"," pages come from the cache, images load from the bucket, and you can sign in with your existing password (see ",[611,1498,1500],{"href":1499},"\u002Fdeployment#check-the-site","Deploying a CWA Site › Check the site",[607,1502,1503,1504,935],{},"Keep the old server running, still in maintenance mode, until DNS has moved everywhere: a visitor whose resolver still has the old address gets the maintenance page rather than an error. Then stop it, and keep its last backup and ",[641,1505,678],{},[762,1507,972],{"id":1508},"copy-the-database",[607,1510,1511,1512,1515],{},"Restore the dump into the external database with ",[641,1513,1514],{},"pg_restore",", from anywhere that can reach it:",[1042,1517,1519],{"className":1044,"code":1518,"language":1046,"meta":1047,"style":1047},"pg_restore --clean --if-exists --no-owner --dbname='postgresql:\u002F\u002F\u003Cuser>:\u003Cpassword>@\u003Chost>\u002F\u003Cdatabase>' backups\u002F\u003Ctime>-database.dump\n",[641,1520,1521],{"__ignoreMap":1047},[1051,1522,1523,1525,1528,1531,1534,1537,1539,1542,1544,1546,1548,1550,1552,1554],{"class":1053,"line":1054},[1051,1524,1514],{"class":1057},[1051,1526,1527],{"class":1072}," --clean",[1051,1529,1530],{"class":1072}," --if-exists",[1051,1532,1533],{"class":1072}," --no-owner",[1051,1535,1536],{"class":1072}," --dbname=",[1051,1538,1162],{"class":1161},[1051,1540,1541],{"class":1061},"postgresql:\u002F\u002F\u003Cuser>:\u003Cpassword>@\u003Chost>\u002F\u003Cdatabase>",[1051,1543,1162],{"class":1161},[1051,1545,1076],{"class":1061},[1051,1547,1080],{"class":1079},[1051,1549,1083],{"class":1061},[1051,1551,1087],{"class":1086},[1051,1553,1090],{"class":1079},[1051,1555,1556],{"class":1061},"-database.dump\n",[607,1558,1559,1562,1563,1566,1567,1569,1570,935],{},[641,1560,1561],{},"--clean --if-exists"," drops each table before restoring it, so it replaces a copy you loaded earlier. ",[641,1564,1565],{},"--no-owner"," makes the external database's user own everything. Use a ",[641,1568,1514],{}," of the server's PostgreSQL major version or newer (the template's is 16). This is the same command as ",[611,1571,1573],{"href":1572},"\u002Fdeployment\u002Fmigrations-and-backups#restoring-a-backup","Migrations and Backups › Restoring a backup",[607,1575,1576],{},"The first production deploy finds the database already migrated to the commit you deployed, so it has nothing left to migrate.",[1578,1579,1580],"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 .sOvfz, html code.shiki .sOvfz{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#89DDFF}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 .sPB8G, html code.shiki .sPB8G{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#BABED8}html pre.shiki code .sc2zw, html code.shiki .sc2zw{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#BABED8}html pre.shiki code .seSrl, html code.shiki .seSrl{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#89DDFF}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);}",{"title":1047,"searchDepth":1102,"depth":1102,"links":1582},[1583,1588,1589],{"id":707,"depth":1102,"text":708,"children":1584},[1585,1587],{"id":764,"depth":1108,"text":1586},"The secrets from .env.prod",{"id":938,"depth":1108,"text":939},{"id":1011,"depth":1102,"text":977},{"id":1256,"depth":1102,"text":1257,"children":1590},[1591],{"id":1508,"depth":1108,"text":972},"Move a CWA site from one server with Docker Compose to Kubernetes with the template's pipeline - the media into a Cloud Storage bucket, the database from a final dump taken under maintenance mode, the secrets from .env.prod as CI variables, and the certificates from the staging issuer to letsencrypt-prod.","md",{},{"title":549,"description":1592},"IqxyhPMgVAAwHfIUqon78RzSuKyVy6hAWUvb9MY0Cdo",[1598,1600],{"title":545,"path":546,"stem":547,"description":1599,"children":-1},"Deploying the CWA stack to Kubernetes using Helm — values configuration, secrets management, migration Jobs, and rolling updates.",{"title":553,"path":554,"stem":555,"description":1601,"children":-1},"The template's opt-in scaled mode runs the API on two or more php pods, sharing one Redis cache and one Mercure hub, so losing a pod is not an outage. When you need it, how to turn it on, what it costs and what can go wrong.",1792796839395]