[{"data":1,"prerenderedAt":1890},["ShallowReactive",2],{"navigation":3,"\u002Fdeployment\u002Fone-server":600,"\u002Fdeployment\u002Fone-server-surround":1885},[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":541,"badge":602,"body":603,"description":1880,"extension":1881,"links":602,"meta":1882,"navigation":23,"path":542,"seo":1883,"stem":543,"__hash__":1884},"docs\u002F6.deployment\u002F1.one-server.md",null,{"type":604,"value":605,"toc":1865},"minimark",[606,628,645,726,731,779,783,872,875,897,904,913,916,978,981,1017,1027,1069,1081,1102,1126,1145,1149,1162,1170,1184,1197,1201,1207,1313,1320,1325,1330,1347,1354,1358,1369,1523,1563,1566,1569,1578,1584,1639,1652,1666,1672,1675,1697,1707,1727,1733,1764,1768,1779,1838,1852,1856,1861],[607,608,611],"callout",{"icon":609,"color":610},"i-heroicons-exclamation-triangle","warning",[612,613,614,618,619,623,624,627],"p",{},[615,616,617],"strong",{},"One server is a preview."," The template's ",[620,621,622],"code",{},"bin\u002Fserver\u002F"," scripts set up, deploy, back up and restore a site, but they haven't run a production site yet. Kubernetes is the supported way to run a site (see ",[625,626,535],"a",{"href":531},").",[612,629,630,631,634,635,637,638,644],{},"Every project made with ",[620,632,633],{},"create-cwa"," has the scripts in ",[620,636,622],{},". They run ",[625,639,641],{"href":640},"\u002Fdeployment\u002Fdocker#production-docker-compose",[620,642,643],{},"compose.prod.yaml"," on one server, and do for it what the Kubernetes pipeline does for a cluster: build, migrate, purge, generate image variants and warm the cache. They also back the site up and restore it.",[646,647,648,661],"table",{},[649,650,651],"thead",{},[652,653,654,658],"tr",{},[655,656,657],"th",{},"Script",[655,659,660],{},"What it does",[662,663,664,679,693,706,716],"tbody",{},[652,665,666,672],{},[667,668,669],"td",{},[620,670,671],{},"bin\u002Fserver\u002Finit",[667,673,674,675,678],{},"Writes the site's settings and secrets to ",[620,676,677],{},".env.prod",", once",[652,680,681,686],{},[667,682,683],{},[620,684,685],{},"bin\u002Fserver\u002Fdeploy.sh",[667,687,688,689,692],{},"Builds the images and deploys the checkout. ",[620,690,691],{},"--fixtures"," loads the fixtures too",[652,694,695,700],{},[667,696,697],{},[620,698,699],{},"bin\u002Fserver\u002Fbackup.sh",[667,701,702,703],{},"Backs up the database, the media and the TLS certificates to ",[620,704,705],{},"backups\u002F",[652,707,708,713],{},[667,709,710],{},[620,711,712],{},"bin\u002Fserver\u002Frestore.sh",[667,714,715],{},"Puts a backup back",[652,717,718,723],{},[667,719,720],{},[620,721,722],{},"bin\u002Fserver\u002Fcron.example",[667,724,725],{},"The scheduled jobs to copy into the server's crontab",[727,728,730],"h2",{"id":729},"what-you-need","What you need",[732,733,734,741,767,773],"ul",{},[735,736,737,740],"li",{},[615,738,739],{},"A Linux server",", such as Debian or Ubuntu, from any provider. The deploy builds the images on the server, so it needs room to build as well as to serve.",[735,742,743,746,747,750,751,754,755,758,759,762,763,766],{},[615,744,745],{},"Docker Engine with the Compose plugin"," (",[620,748,749],{},"docker compose","), plus ",[620,752,753],{},"git",", ",[620,756,757],{},"curl"," and ",[620,760,761],{},"openssl",". Run the scripts as a user that can run ",[620,764,765],{},"docker",".",[735,768,769,772],{},[615,770,771],{},"A domain"," whose DNS you can change.",[735,774,775,778],{},[615,776,777],{},"A mail relay"," that takes SMTP, for the emails the site sends (password resets, the orphan scan).",[727,780,782],{"id":781},"prepare-the-server","Prepare the server",[784,785,786,800,818],"ol",{},[735,787,788,791,792,795,796,799],{},[615,789,790],{},"Point the domain at the server."," Create an ",[620,793,794],{},"A"," (and ",[620,797,798],{},"AAAA",") record for the site's host, and for each other host it should answer to. Caddy, inside the php container, gets a Let's Encrypt certificate for each of them on its own, so the records must reach this server before the first deploy.",[735,801,802,805,806,809,810,813,814,817],{},[615,803,804],{},"Open the ports."," ",[620,807,808],{},"80\u002Ftcp"," (Let's Encrypt's challenge and the redirect to HTTPS), ",[620,811,812],{},"443\u002Ftcp",", and ",[620,815,816],{},"443\u002Fudp"," for HTTP\u002F3.",[735,819,820,823,824,827,828],{},[615,821,822],{},"Clone your project"," on the server, for example to ",[620,825,826],{},"\u002Fsrv\u002Fmy-site",":",[829,830,835],"pre",{"className":831,"code":832,"language":833,"meta":834,"style":834},"language-bash shiki shiki-themes github-light github-dark material-theme-palenight","git clone \u003Cyour project's repository> \u002Fsrv\u002Fmy-site\ncd \u002Fsrv\u002Fmy-site\n","bash","",[620,836,837,866],{"__ignoreMap":834},[838,839,842,845,849,853,856,859,863],"span",{"class":840,"line":841},"line",1,[838,843,753],{"class":844},"sRCss",[838,846,848],{"class":847},"sLL54"," clone",[838,850,852],{"class":851},"sVlFx"," \u003C",[838,854,855],{"class":847},"your",[838,857,858],{"class":847}," project",[838,860,862],{"class":861},"seSrl","'",[838,864,865],{"class":847},"s repository> \u002Fsrv\u002Fmy-site\n",[838,867,869],{"class":840,"line":868},2,[838,870,871],{"class":847},"cd \u002Fsrv\u002Fmy-site\n",[612,873,874],{},"The scripts can be run from anywhere: each one moves to the checkout's root first.",[607,876,878],{"icon":877},"i-heroicons-light-bulb",[612,879,880,883,884,887,888,891,892,896],{},[615,881,882],{},"Cloudflare in front:"," leave the records ",[615,885,886],{},"DNS only"," until the first deploy has its certificate, then proxy them with ",[615,889,890],{},"Full (strict)",", as in ",[625,893,895],{"href":894},"\u002Fdeployment\u002Fcloudflare#option-a-proxy-only","Cloudflare › Option A",". Don't turn on edge caching (Option B): a single-server deploy never purges Cloudflare, so the edge would keep old pages.",[727,898,900,901],{"id":899},"write-the-settings-with-init","Write the settings with ",[620,902,903],{},"init",[829,905,907],{"className":831,"code":906,"language":833,"meta":834,"style":834},"bin\u002Fserver\u002Finit\n",[620,908,909],{"__ignoreMap":834},[838,910,911],{"class":840,"line":841},[838,912,906],{"class":844},[612,914,915],{},"It asks three questions, unless the variable is already set in the environment:",[646,917,918,931],{},[649,919,920],{},[652,921,922,925,928],{},[655,923,924],{},"Setting",[655,926,927],{},"Question",[655,929,930],{},"Example",[662,932,933,948,963],{},[652,934,935,940,943],{},[667,936,937],{},[620,938,939],{},"SITE_HOST",[667,941,942],{},"The site's domain",[667,944,945],{},[620,946,947],{},"www.example.com",[652,949,950,955,958],{},[667,951,952],{},[620,953,954],{},"SITE_ALIASES",[667,956,957],{},"Other domains served the same, separated by spaces. Optional",[667,959,960],{},[620,961,962],{},"example.com",[652,964,965,970,973],{},[667,966,967],{},[620,968,969],{},"MAILER_DSN",[667,971,972],{},"Your mail relay as a Symfony Mailer DSN",[667,974,975],{},[620,976,977],{},"smtp:\u002F\u002Fuser:pass@smtp.example.com:587",[612,979,980],{},"A host must be a bare name: no scheme, port or path. No answer may contain a single quote. To run it unattended, set all three first:",[829,982,984],{"className":831,"code":983,"language":833,"meta":834,"style":834},"SITE_HOST=www.example.com SITE_ALIASES=example.com MAILER_DSN='smtp:\u002F\u002Fuser:pass@smtp.example.com:587' bin\u002Fserver\u002Finit\n",[620,985,986],{"__ignoreMap":834},[838,987,988,991,994,996,999,1001,1003,1006,1008,1010,1012,1014],{"class":840,"line":841},[838,989,939],{"class":990},"sPB8G",[838,992,993],{"class":851},"=",[838,995,947],{"class":847},[838,997,998],{"class":990}," SITE_ALIASES",[838,1000,993],{"class":851},[838,1002,962],{"class":847},[838,1004,1005],{"class":990}," MAILER_DSN",[838,1007,993],{"class":851},[838,1009,862],{"class":861},[838,1011,977],{"class":847},[838,1013,862],{"class":861},[838,1015,1016],{"class":844}," bin\u002Fserver\u002Finit\n",[612,1018,1019,1020,1022,1023,1026],{},"It writes ",[620,1021,677],{}," at the root of the checkout, readable only by you (mode ",[620,1024,1025],{},"600","):",[732,1028,1029,1032,1038,1057],{},[735,1030,1031],{},"your three answers;",[735,1033,1034,1037],{},[620,1035,1036],{},"COMPOSE_FILE=compose.yaml:compose.prod.yaml",", so Compose uses the production file and never the dev override;",[735,1039,1040,1041,754,1044,754,1047,754,1050,758,1053,1056],{},"new random values for ",[620,1042,1043],{},"APP_SECRET",[620,1045,1046],{},"NUXT_APP_SECRET",[620,1048,1049],{},"CADDY_MERCURE_JWT_SECRET",[620,1051,1052],{},"POSTGRES_PASSWORD",[620,1054,1055],{},"ADMIN_PASSWORD",";",[735,1058,1059,1060,754,1063,758,1066,766],{},"a new 4096-bit JWT key pair: ",[620,1061,1062],{},"JWT_SECRET_KEY",[620,1064,1065],{},"JWT_PUBLIC_KEY",[620,1067,1068],{},"JWT_PASSPHRASE",[612,1070,1071,1072,1075,1076,1078,1079,766],{},"It ends by printing the admin login, ",[620,1073,1074],{},"admin \u002F \u003Cpassword>",". The password is also ",[620,1077,1055],{}," in ",[620,1080,677],{},[607,1082,1083],{"icon":609,"color":610},[612,1084,1085,1090,1091,1094,1095,1097,1098,1101],{},[615,1086,1087,1089],{},[620,1088,677],{}," holds the site's secrets."," Keep a copy somewhere safe, off the server, and never commit it (the template's ",[620,1092,1093],{},".gitignore"," leaves it out). ",[620,1096,903],{}," never overwrites it: run again, it stops with ",[620,1099,1100],{},".env.prod already exists: delete it first to start again (its secrets are the site's)."," A new JWT key pair signs every user out.",[612,1103,1104,1105,1107,1108,1110,1111,1114,1115,1118,1119,1122,1123,627],{},"Add any other setting ",[620,1106,643],{}," passes to php to ",[620,1109,677],{}," yourself, such as ",[620,1112,1113],{},"MAILER_EMAIL"," (the address the site sends from, and where the orphan scan reports), ",[620,1116,1117],{},"ADMIN_EMAIL"," or ",[620,1120,1121],{},"ERROR_MAIL_TO"," (see ",[625,1124,1125],{"href":640},"Docker › Production Docker Compose",[612,1127,1128,1129,1132,1133,1136,1137,1140,1141,1122,1143,627],{},"Uploaded media is stored on the server, in the ",[620,1130,1131],{},"media_data"," volume, and served at ",[620,1134,1135],{},"https:\u002F\u002F\u003CSITE_HOST>\u002Fuploads\u002F",". To store it in a Cloud Storage bucket instead, set ",[620,1138,1139],{},"MEDIA_STORAGE=gcloud"," and the bucket's settings in ",[620,1142,677],{},[625,1144,1125],{"href":640},[727,1146,1148],{"id":1147},"the-first-deploy","The first deploy",[829,1150,1152],{"className":831,"code":1151,"language":833,"meta":834,"style":834},"bin\u002Fserver\u002Fdeploy.sh --fixtures\n",[620,1153,1154],{"__ignoreMap":834},[838,1155,1156,1158],{"class":840,"line":841},[838,1157,685],{"class":844},[838,1159,1161],{"class":1160},"szhYu"," --fixtures\n",[612,1163,1164,1166,1167,1169],{},[620,1165,691],{}," loads the fixtures, which create the admin user and any pages your scaffold seeds. Use it on the first deploy. The load appends: it creates what's missing and keeps what exists. Every load also resets the admin's password to ",[620,1168,1055],{},", so leave it off later deploys.",[612,1171,1172,1173,1176,1177,1180,1181,1183],{},"When it's done, the log ends with ",[620,1174,1175],{},"✅ Deployed https:\u002F\u002F\u003CSITE_HOST>",". Sign in at ",[620,1178,1179],{},"\u002Flogin"," with the login ",[620,1182,903],{}," printed.",[607,1185,1187],{"icon":1186},"i-heroicons-photo",[612,1188,1189,1192,1193,1196],{},[615,1190,1191],{},"Image placeholder:"," a first ",[620,1194,1195],{},"deploy.sh --fixtures"," run in a terminal: the build, the fixture load, the image variants and the page warm, ending with \"Deployed\".",[727,1198,1200],{"id":1199},"what-a-deploy-does","What a deploy does",[612,1202,1203,1206],{},[620,1204,1205],{},"deploy.sh"," can be run again at any time. In order, it:",[784,1208,1209,1218,1223,1236,1246,1255,1263,1280,1303],{},[735,1210,1211,746,1214,1217],{},[615,1212,1213],{},"Builds the images",[620,1215,1216],{},"docker compose build --pull","), on the server, from the checkout.",[735,1219,1220],{},[615,1221,1222],{},"Starts the database.",[735,1224,1225,1228,1229,1232,1233],{},[615,1226,1227],{},"Checks for pending migrations",", when the site is already deployed. It runs the template's migrations plan with the new php image, in a one-off container with a read-only database session, and prints ",[620,1230,1231],{},"Pending migrations: \u003Cn>",". A failed plan stops the deploy before anything restarts: ",[620,1234,1235],{},"❌ The migrations plan failed: nothing was deployed.",[735,1237,1238,1241,1242,1245],{},[615,1239,1240],{},"Backs up first"," when migrations are pending, with ",[620,1243,1244],{},"backup.sh",". A failed backup stops the deploy too.",[735,1247,1248,1251,1252],{},[615,1249,1250],{},"Starts the front end, then the API."," The app container starts first, so no old front end renders into the new API's cache. php migrates the database as it starts. Each container whose image changed is recreated, so ",[615,1253,1254],{},"the site is down for the few seconds php takes to restart.",[735,1256,1257,1260,1261,766],{},[615,1258,1259],{},"Loads the fixtures",", with ",[620,1262,691],{},[735,1264,1265,1268,1269,1272,1273,1122,1276,627],{},[615,1266,1267],{},"Purges the rendered pages",", which point at the old build's ",[620,1270,1271],{},"\u002F_nuxt"," files, and warms ",[620,1274,1275],{},"\u002F_api",[625,1277,1279],{"href":1278},"\u002Fdeployment\u002Fci-cd#why-every-deploy-clears-the-page-cache","CI\u002FCD › Why every deploy clears the page cache",[735,1281,1282,1285,1286,746,1289,1292,1293,754,1296,1299,1300,1302],{},[615,1283,1284],{},"Generates missing image variants"," in one-off php containers, never in the php that serves the site. Each is capped at ",[620,1287,1288],{},"CWA_SERVER_IMAGE_VARIANTS_MEMORY",[620,1290,1291],{},"1536m","). A container that stops at its own memory limit (",[620,1294,1295],{},"CWA_SERVER_IMAGE_VARIANTS_MAX_MEMORY",[620,1297,1298],{},"1G",") hands over to another, up to 50. When it made any, the deploy flushes the HTTP cache and warms ",[620,1301,1275],{}," again, so cached responses list the new variants.",[735,1304,1305,1308,1309,1312],{},[615,1306,1307],{},"Warms the page cache"," with every page in the sitemap. The requests go to the server itself (",[620,1310,1311],{},"127.0.0.1","), whatever DNS says. A failed warm only warns: pages render on their first visit instead.",[612,1314,1315,1316,766],{},"The settings that change these steps are in ",[625,1317,1319],{"href":1318},"\u002Fdeployment\u002Fvariables#single-server-scripts","CI\u002FCD Variables › Single-server scripts",[1321,1322,1324],"h3",{"id":1323},"later-deploys","Later deploys",[612,1326,1327,1328,827],{},"Pull the new code, then deploy without ",[620,1329,691],{},[829,1331,1333],{"className":831,"code":1332,"language":833,"meta":834,"style":834},"git pull\nbin\u002Fserver\u002Fdeploy.sh\n",[620,1334,1335,1342],{"__ignoreMap":834},[838,1336,1337,1339],{"class":840,"line":841},[838,1338,753],{"class":844},[838,1340,1341],{"class":847}," pull\n",[838,1343,1344],{"class":840,"line":868},[838,1345,1346],{"class":844},"bin\u002Fserver\u002Fdeploy.sh\n",[612,1348,1349,1350,627],{},"A deploy only migrates forward. To go back to an older commit after a migration ran, restore the backup taken before it (see ",[625,1351,1353],{"href":1352},"#restore-a-backup","Restore a backup",[727,1355,1357],{"id":1356},"schedule-the-jobs","Schedule the jobs",[612,1359,1360,1362,1363,1366,1367,827],{},[620,1361,722],{}," has the site's scheduled jobs. Copy them into ",[620,1364,1365],{},"crontab -e"," for the user that runs Docker, with the path to your checkout in place of ",[620,1368,826],{},[829,1370,1372],{"className":831,"code":1371,"language":833,"meta":834,"style":834},"# Daily backup at 02:30\n30 2 * * * cd \u002Fsrv\u002Fmy-site && sh bin\u002Fserver\u002Fbackup.sh >> backups\u002Fbackup.log 2>&1\n# Then copy backups\u002F off the server\n0 3 * * * rsync -a --delete \u002Fsrv\u002Fmy-site\u002Fbackups\u002F backup-host:\u002Fbackups\u002Fmy-site\u002F\n\n# Daily orphan scan at 03:00\n0 3 * * * cd \u002Fsrv\u002Fmy-site && docker compose --env-file .env.prod exec -T php bin\u002Fconsole silverback:api-components:scan-orphaned >> backups\u002Forphan-scan.log 2>&1\n",[620,1373,1374,1380,1422,1428,1458,1464,1470],{"__ignoreMap":834},[838,1375,1376],{"class":840,"line":841},[838,1377,1379],{"class":1378},"sTBSN","# Daily backup at 02:30\n",[838,1381,1382,1385,1389,1393,1395,1397,1400,1403,1407,1410,1413,1416,1419],{"class":840,"line":868},[838,1383,1384],{"class":844},"30",[838,1386,1388],{"class":1387},"scSvc"," 2",[838,1390,1392],{"class":1391},"sc2zw"," *",[838,1394,1392],{"class":1391},[838,1396,1392],{"class":1391},[838,1398,1399],{"class":847}," cd",[838,1401,1402],{"class":847}," \u002Fsrv\u002Fmy-site",[838,1404,1406],{"class":1405},"sOvfz"," &&",[838,1408,1409],{"class":844}," sh",[838,1411,1412],{"class":847}," bin\u002Fserver\u002Fbackup.sh",[838,1414,1415],{"class":851}," >>",[838,1417,1418],{"class":847}," backups\u002Fbackup.log",[838,1420,1421],{"class":851}," 2>&1\n",[838,1423,1425],{"class":840,"line":1424},3,[838,1426,1427],{"class":1378},"# Then copy backups\u002F off the server\n",[838,1429,1431,1434,1437,1439,1441,1443,1446,1449,1452,1455],{"class":840,"line":1430},4,[838,1432,1433],{"class":844},"0",[838,1435,1436],{"class":1387}," 3",[838,1438,1392],{"class":1391},[838,1440,1392],{"class":1391},[838,1442,1392],{"class":1391},[838,1444,1445],{"class":847}," rsync",[838,1447,1448],{"class":1160}," -a",[838,1450,1451],{"class":1160}," --delete",[838,1453,1454],{"class":847}," \u002Fsrv\u002Fmy-site\u002Fbackups\u002F",[838,1456,1457],{"class":847}," backup-host:\u002Fbackups\u002Fmy-site\u002F\n",[838,1459,1461],{"class":840,"line":1460},5,[838,1462,1463],{"emptyLinePlaceholder":23},"\n",[838,1465,1467],{"class":840,"line":1466},6,[838,1468,1469],{"class":1378},"# Daily orphan scan at 03:00\n",[838,1471,1473,1475,1477,1479,1481,1483,1485,1487,1489,1492,1495,1498,1501,1504,1507,1510,1513,1516,1518,1521],{"class":840,"line":1472},7,[838,1474,1433],{"class":844},[838,1476,1436],{"class":1387},[838,1478,1392],{"class":1391},[838,1480,1392],{"class":1391},[838,1482,1392],{"class":1391},[838,1484,1399],{"class":847},[838,1486,1402],{"class":847},[838,1488,1406],{"class":1405},[838,1490,1491],{"class":844}," docker",[838,1493,1494],{"class":847}," compose",[838,1496,1497],{"class":1160}," --env-file",[838,1499,1500],{"class":847}," .env.prod",[838,1502,1503],{"class":847}," exec",[838,1505,1506],{"class":1160}," -T",[838,1508,1509],{"class":847}," php",[838,1511,1512],{"class":847}," bin\u002Fconsole",[838,1514,1515],{"class":847}," silverback:api-components:scan-orphaned",[838,1517,1415],{"class":851},[838,1519,1520],{"class":847}," backups\u002Forphan-scan.log",[838,1522,1421],{"class":851},[732,1524,1525,1537,1550],{},[735,1526,1527,1530,1531,1122,1533,627],{},[615,1528,1529],{},"The backup"," keeps the newest five in ",[620,1532,705],{},[625,1534,1536],{"href":1535},"#back-up","Back up",[735,1538,1539,1542,1543,754,1546,1549],{},[615,1540,1541],{},"The copy off the server"," is commented out in the file, because only you know where it goes. Use ",[620,1544,1545],{},"rsync",[620,1547,1548],{},"rclone"," or your provider's tool. Don't leave it out: a backup on the same disk as the site is lost with it.",[735,1551,1552,1555,1556,1558,1559,627],{},[615,1553,1554],{},"The orphan scan"," refreshes the admin's Orphaned Resources report, and emails ",[620,1557,1113],{}," when the orphans have changed. It never deletes anything (see ",[625,1560,1562],{"href":1561},"\u002Fapi\u002Fconsole-commands#scan-orphaned","Console Commands › scan-orphaned",[612,1564,1565],{},"The times are the server's local time.",[727,1567,1536],{"id":1568},"back-up",[829,1570,1572],{"className":831,"code":1571,"language":833,"meta":834,"style":834},"bin\u002Fserver\u002Fbackup.sh\n",[620,1573,1574],{"__ignoreMap":834},[838,1575,1576],{"class":840,"line":841},[838,1577,1571],{"class":844},[612,1579,1580,1581,1583],{},"Each backup is three files in ",[620,1582,705],{},", named after the time it started, in UTC:",[646,1585,1586,1596],{},[649,1587,1588],{},[652,1589,1590,1593],{},[655,1591,1592],{},"File",[655,1594,1595],{},"What it holds",[662,1597,1598,1616,1629],{},[652,1599,1600,1605],{},[667,1601,1602],{},[620,1603,1604],{},"\u003Ctime>-database.dump",[667,1606,1607,1608,1611,1612,1615],{},"The database, from ",[620,1609,1610],{},"pg_dump --format=custom",". It's kept only if ",[620,1613,1614],{},"pg_restore --list"," reads it back",[652,1617,1618,1623],{},[667,1619,1620],{},[620,1621,1622],{},"\u003Ctime>-media.tar.gz",[667,1624,1625,1626,1628],{},"The ",[620,1627,1131],{}," volume: uploads and their image variants",[652,1630,1631,1636],{},[667,1632,1633],{},[620,1634,1635],{},"\u003Ctime>-caddy-data.tar.gz",[667,1637,1638],{},"Caddy's data, which holds the site's TLS certificates",[612,1640,1641,1644,1645,1648,1649,1651],{},[620,1642,1643],{},"\u003Ctime>"," looks like ",[620,1646,1647],{},"20261024T031500Z",". It keeps the newest five backups and deletes older ones, counting the ones ",[620,1650,1205],{}," takes before migrations too. Only files named in this form are ever deleted. The folder is readable only by you.",[607,1653,1654],{"icon":609,"color":610},[612,1655,1656,1659,1660,1662,1663,1665],{},[615,1657,1658],{},"A backup on the same disk isn't a backup."," If the server's disk fails, or the server is deleted, ",[620,1661,705],{}," goes with it. Copy it somewhere else every day, and keep that copy private: the dump holds every user's email address and password hash, and Caddy's data holds the certificates' private keys. Keep ",[620,1664,677],{}," with it, because a backup doesn't include it.",[612,1667,1668,1669,1671],{},"With ",[620,1670,1139],{},", uploads are in the bucket, not on the server, so the media archive is empty and the bucket isn't backed up by these scripts.",[727,1673,1353],{"id":1674},"restore-a-backup",[829,1676,1678],{"className":831,"code":1677,"language":833,"meta":834,"style":834},"bin\u002Fserver\u002Frestore.sh                     # the newest backup\nbin\u002Fserver\u002Frestore.sh 20261024T031500Z    # a given one\n",[620,1679,1680,1687],{"__ignoreMap":834},[838,1681,1682,1684],{"class":840,"line":841},[838,1683,712],{"class":844},[838,1685,1686],{"class":1378},"                     # the newest backup\n",[838,1688,1689,1691,1694],{"class":840,"line":868},[838,1690,712],{"class":844},[838,1692,1693],{"class":847}," 20261024T031500Z",[838,1695,1696],{"class":1378},"    # a given one\n",[612,1698,1699,1702,1703,1706],{},[615,1700,1701],{},"Restoring replaces the database and the media."," Everything since the backup is lost. So it first asks you to type the site's host; ",[620,1704,1705],{},"--yes"," skips the question. Then it:",[784,1708,1709,1712,1715,1718,1721,1724],{},[735,1710,1711],{},"Turns maintenance mode on, so visitors see the maintenance page.",[735,1713,1714],{},"Restores the database.",[735,1716,1717],{},"Turns maintenance mode on again, because the setting is stored in the database and the backup brought back its own.",[735,1719,1720],{},"Replaces the media with the backup's.",[735,1722,1723],{},"Restarts php, which migrates a database restored from an older version of the site, then purges the HTTP cache.",[735,1725,1726],{},"Turns maintenance mode off.",[612,1728,1729,1732],{},[620,1730,1731],{},"--certificates"," also restores Caddy's data. Use it when a new server takes over the site, so it serves the old certificates straight away:",[784,1734,1735,1743,1751,1756,1761],{},[735,1736,1737,1738,1740,1741,766],{},"On the new server, clone the project and copy ",[620,1739,677],{}," from the old one, rather than running ",[620,1742,903],{},[735,1744,1745,1746,1748,1749,766],{},"Run ",[620,1747,685],{},", without ",[620,1750,691],{},[735,1752,1753,1754,766],{},"Copy the backup's three files into ",[620,1755,705],{},[735,1757,1745,1758,766],{},[620,1759,1760],{},"bin\u002Fserver\u002Frestore.sh \u003Ctime> --certificates",[735,1762,1763],{},"Point the DNS at the new server.",[727,1765,1767],{"id":1766},"running-commands-by-hand","Running commands by hand",[612,1769,1770,1771,1774,1775,1778],{},"Run Compose with the site's settings: ",[620,1772,1773],{},"--env-file .env.prod",", from the checkout's root. ",[620,1776,1777],{},"COMPOSE_FILE"," in that file picks the production files.",[829,1780,1782],{"className":831,"code":1781,"language":833,"meta":834,"style":834},"docker compose --env-file .env.prod ps\ndocker compose --env-file .env.prod logs -f php\ndocker compose --env-file .env.prod exec php bin\u002Fconsole silverback:api-components:maintenance on\n",[620,1783,1784,1797,1816],{"__ignoreMap":834},[838,1785,1786,1788,1790,1792,1794],{"class":840,"line":841},[838,1787,765],{"class":844},[838,1789,1494],{"class":847},[838,1791,1497],{"class":1160},[838,1793,1500],{"class":847},[838,1795,1796],{"class":847}," ps\n",[838,1798,1799,1801,1803,1805,1807,1810,1813],{"class":840,"line":868},[838,1800,765],{"class":844},[838,1802,1494],{"class":847},[838,1804,1497],{"class":1160},[838,1806,1500],{"class":847},[838,1808,1809],{"class":847}," logs",[838,1811,1812],{"class":1160}," -f",[838,1814,1815],{"class":847}," php\n",[838,1817,1818,1820,1822,1824,1826,1828,1830,1832,1835],{"class":840,"line":1424},[838,1819,765],{"class":844},[838,1821,1494],{"class":847},[838,1823,1497],{"class":1160},[838,1825,1500],{"class":847},[838,1827,1503],{"class":847},[838,1829,1509],{"class":847},[838,1831,1512],{"class":847},[838,1833,1834],{"class":847}," silverback:api-components:maintenance",[838,1836,1837],{"class":847}," on\n",[607,1839,1840],{"icon":609,"color":610},[612,1841,1842,805,1848,1851],{},[615,1843,1844,1845,766],{},"Never run ",[620,1846,1847],{},"docker compose down -v",[620,1849,1850],{},"-v"," deletes the volumes: the database, the media and the certificates.",[727,1853,1855],{"id":1854},"moving-to-kubernetes","Moving to Kubernetes",[612,1857,1858,1859,766],{},"When one server isn't enough, see ",[625,1860,549],{"href":550},[1862,1863,1864],"style",{},"html pre.shiki code .sRCss, html code.shiki .sRCss{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#FFCB6B}html pre.shiki code .sLL54, html code.shiki .sLL54{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#C3E88D}html pre.shiki code .sVlFx, html code.shiki .sVlFx{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#89DDFF}html pre.shiki code .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);}html pre.shiki code .sPB8G, html code.shiki .sPB8G{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#BABED8}html pre.shiki code .szhYu, html code.shiki .szhYu{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#C3E88D}html pre.shiki code .sTBSN, html code.shiki .sTBSN{--shiki-light:#6A737D;--shiki-light-font-style:inherit;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .scSvc, html code.shiki .scSvc{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#F78C6C}html pre.shiki code .sc2zw, html code.shiki .sc2zw{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#BABED8}html pre.shiki code .sOvfz, html code.shiki .sOvfz{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#89DDFF}",{"title":834,"searchDepth":868,"depth":868,"links":1866},[1867,1868,1869,1871,1872,1875,1876,1877,1878,1879],{"id":729,"depth":868,"text":730},{"id":781,"depth":868,"text":782},{"id":899,"depth":868,"text":1870},"Write the settings with init",{"id":1147,"depth":868,"text":1148},{"id":1199,"depth":868,"text":1200,"children":1873},[1874],{"id":1323,"depth":1424,"text":1324},{"id":1356,"depth":868,"text":1357},{"id":1568,"depth":868,"text":1536},{"id":1674,"depth":868,"text":1353},{"id":1766,"depth":868,"text":1767},{"id":1854,"depth":868,"text":1855},"Run a CWA site on one Linux server with Docker Compose and the template's bin\u002Fserver scripts - set up the server, write the site's settings with init, deploy with deploy.sh, schedule backups, restore one, and run commands by hand. A preview.","md",{},{"title":541,"description":1880},"OUKkZlLvSLM-sJpQgoL-fyM4MVK34UcE3Co_WPZ_GtE",[1886,1888],{"title":537,"path":538,"stem":539,"description":1887,"children":-1},"The Docker Compose stack for local development and single-server production (a preview) — services, volumes, environment variables, the images and their build stages, and how Caddy caches pages.",{"title":545,"path":546,"stem":547,"description":1889,"children":-1},"Deploying the CWA stack to Kubernetes using Helm — values configuration, secrets management, migration Jobs, and rolling updates.",1792796839259]