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