[{"data":1,"prerenderedAt":4082},["ShallowReactive",2],{"navigation":3,"\u002Fdeployment\u002Ftroubleshooting":604,"\u002Fdeployment\u002Ftroubleshooting-surround":4079},[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":601,"badge":606,"body":607,"description":4074,"extension":4075,"links":606,"meta":4076,"navigation":23,"path":602,"seo":4077,"stem":603,"__hash__":4078},"docs\u002F6.deployment\u002F7.troubleshooting.md",null,{"type":608,"value":609,"toc":3935},"minimark",[610,614,623,628,637,659,692,743,753,773,779,796,802,819,829,904,911,917,956,962,968,992,995,1019,1094,1167,1172,1178,1184,1196,1216,1238,1254,1399,1416,1421,1462,1479,1486,1490,1501,1537,1551,1580,1591,1631,1653,1662,1670,1685,1717,1725,1743,1782,1803,1807,1825,1902,1909,1913,1924,1928,1938,1972,1979,2006,2023,2031,2046,2053,2081,2089,2096,2103,2127,2136,2139,2172,2180,2189,2196,2203,2208,2238,2248,2252,2287,2294,2302,2326,2352,2356,2384,2391,2412,2446,2450,2479,2490,2494,2518,2525,2551,2557,2602,2606,2616,2629,2640,2643,2650,2658,2679,2726,2732,2738,2763,2767,2813,2823,2827,2834,2855,2858,2862,2869,2873,2884,2919,2926,2933,2937,2940,3087,3100,3149,3156,3181,3195,3202,3208,3226,3233,3256,3260,3284,3295,3324,3335,3349,3372,3378,3400,3404,3423,3430,3440,3444,3465,3469,3476,3503,3514,3521,3532,3539,3554,3560,3569,3573,3592,3596,3603,3626,3632,3646,3652,3664,3677,3692,3698,3709,3775,3786,3790,3799,3802,3810,3821,3825,3833,3837,3845,3853,3863,3867,3874,3882,3889,3892,3931],[611,612,613],"p",{},"Each problem below has its own heading, named after what you see: the symptom or the error text. Search the page for your error message. Each entry links back to the reference section that explains the part involved.",[611,615,616,617,622],{},"Troubleshooting for other parts of CWA is listed at the end, in ",[618,619,621],"a",{"href":620},"#elsewhere-in-the-docs","Elsewhere in the docs",".",[624,625,627],"h2",{"id":626},"local-development","Local development",[629,630,632,636],"h3",{"id":631},"create-cwa-says-a-port-is-in-use",[633,634,635],"code",{},"create-cwa"," says a port is in use",[611,638,639,640,643,644,647,648,651,652,655,656,658],{},"The stack publishes ports 80 and 443 (Caddy in ",[633,641,642],{},"php","), 3001 (the Nuxt dev server in ",[633,645,646],{},"app","), 5432 (PostgreSQL in ",[633,649,650],{},"database",") and 8025 (Mailpit's web UI in ",[633,653,654],{},"mailpit","). ",[633,657,635],{}," checks them before it downloads anything, and names what holds each one it finds in use:",[660,661,662,678],"ul",{},[663,664,665,669,670,673,674,677],"li",{},[666,667,668],"strong",{},"A container",", usually another CWA project's stack. The message gives the command, ",[633,671,672],{},"docker compose -p \u003Cproject> stop",", or ",[633,675,676],{},"docker stop \u003Ccontainer>"," for a container outside Compose.",[663,679,680,683,684,687,688,691],{},[666,681,682],{},"A process on your machine."," For 80 and 443 that's often Laravel Valet (",[633,685,686],{},"valet stop","), Herd, nginx or Apache. For 5432 it's a local PostgreSQL (Postgres.app, or ",[633,689,690],{},"brew services stop postgresql","). For 8025 it's a local Mailpit or MailHog.",[611,693,694,695,698,699,702,703,698,706,673,709,698,712,715,716,719,720,723,724,726,727,730,731,734,735,738,739,622],{},"For 3001, 5432 and 8025 you can move the stack instead: change ",[633,696,697],{},"published: 3001"," under ",[633,700,701],{},"app:",", ",[633,704,705],{},"published: 5432",[633,707,708],{},"database:",[633,710,711],{},"published: 8025",[633,713,714],{},"mailpit:",", in ",[633,717,718],{},"compose.override.yaml",". ",[633,721,722],{},"bin\u002Fup"," prints Mailpit's address from the published port, so it follows the change. The CLI creates the project without starting the stack, so run ",[633,725,722],{}," in it once the port is free. A ",[633,728,729],{},"docker compose up"," that fails with ",[633,732,733],{},"port is already allocated"," or ",[633,736,737],{},"address already in use"," has the same cause. See ",[618,740,742],{"href":741},"\u002Fgetting-started\u002Finstallation#_1-scaffold-your-project","Installation › Scaffold your project",[629,744,746,748,749,752],{"id":745},"create-cwa-says-docker-info-failed",[633,747,635],{}," says ",[633,750,751],{},"docker info"," failed",[611,754,755,756,758,759,761,762,764,765,768,769,772],{},"Docker is installed but its daemon isn't running. Start Docker Desktop, or the Docker service on Linux, wait until ",[633,757,751],{}," answers, then run ",[633,760,722],{}," in the project the CLI created. ",[633,763,722],{}," checks the same thing first, and stops with ",[633,766,767],{},"bin\u002Fup: Docker isn't running: start Docker Desktop (or the Docker daemon) and run bin\u002Fup again."," When ",[633,770,771],{},"docker"," isn't installed at all, the CLI doesn't offer to start the stack.",[629,774,776,778],{"id":775},"create-cwa-warns-that-docker-has-too-little-memory",[633,777,635],{}," warns that Docker has too little memory",[611,780,781,782,784,785,788,789,792,793,795],{},"Under about 3.5 GiB of memory for Docker, the CLI warns that the stack needs about 4 GiB, and still starts it. With too little, a container can run out of memory, for example while the ",[633,783,646],{}," container runs ",[633,786,787],{},"pnpm install"," or a build runs. In Docker Desktop, raise the memory in ",[666,790,791],{},"Settings › Resources",", apply, and run ",[633,794,722],{}," again.",[629,797,799,801],{"id":798},"binup-fails-at-starting-the-stack",[633,800,722],{}," fails at \"Starting the stack\"",[611,803,804,806,807,810,811,818],{},[633,805,722],{}," runs ",[633,808,809],{},"docker compose up -d --wait",", and stops if a container doesn't turn healthy within its time budget (see ",[618,812,814,815,817],{"href":813},"\u002Fgetting-started\u002Finstallation#_2-let-binup-start-the-site","Installation › Let ",[633,816,722],{}," start the site","). It names the services that aren't healthy and prints the last 40 log lines of each:",[820,821,827],"pre",{"className":822,"code":824,"language":825,"meta":826},[823],"language-text","bin\u002Fup failed at \"Starting the stack (docker compose up -d --wait)\" (after 912s): not healthy: php\n--- last 40 log lines of php ---\n…\n    more: docker compose logs -f php\nFix it and run bin\u002Fup again; finished phases are skipped quickly.\n","text","",[633,828,824],{"__ignoreMap":826},[660,830,831,843,855,868,886],{},[663,832,833,836,837,839,840,622],{},[666,834,835],{},"The first build is slow."," The first run builds the images, installs the Composer and pnpm dependencies and migrates, which can take longer than the 15 minutes ",[633,838,722],{}," allows on a slow connection. If the log shows it still working, give it longer, in seconds: ",[633,841,842],{},"CWA_UP_TIMEOUT=1800 bin\u002Fup",[663,844,845,850,851,622],{},[666,846,847,849],{},[633,848,642],{}," isn't healthy:"," see ",[618,852,854],{"href":853},"#php-stays-unhealthy","php stays unhealthy",[663,856,857,850,861,622],{},[666,858,859,849],{},[633,860,650],{},[618,862,864,865,867],{"href":863},"#the-database-container-exits-because-its-data-is-from-an-older-postgresql","The ",[633,866,650],{}," container exits because its data is from an older PostgreSQL",[663,869,870,874,875,877,878,885],{},[666,871,872,849],{},[633,873,646],{}," its log shows why the Nuxt dev server didn't start, often a crashed ",[633,876,787],{}," (see ",[618,879,881,882,884],{"href":880},"#every-page-returns-502-or-503-after-you-run-pnpm-install-on-the-host","Every page returns 502 or 503 after you run ",[633,883,787],{}," on the host",").",[663,887,888,893,894,734,896,898,899,622],{},[666,889,890,892],{},[633,891,654],{}," (or another service) didn't start, with an empty log:"," a port it publishes is taken. Look above the failure for ",[633,895,733],{},[633,897,737],{},", and see ",[618,900,902,636],{"href":901},"#create-cwa-says-a-port-is-in-use",[633,903,635],{},[611,905,906,907,910],{},"When no service is named, the message ends ",[633,908,909],{},"the images didn't build or the containers didn't start (see the output above)",": the error is in the build output above it.",[629,912,914,916],{"id":913},"binup-fails-at-loading-fixtures",[633,915,722],{}," fails at \"Loading fixtures\"",[611,918,919,921,922,925,926,929,930,933,934,936,937,939,940,942,943,946,947,950,951,955],{},[633,920,722],{}," loads the fixtures with ",[633,923,924],{},"doctrine:fixtures:load --append"," and stops with ",[633,927,928],{},"timed out after \u003Cn>s"," when the load uses up what's left of the time budget, or with ",[633,931,932],{},"doctrine:fixtures:load exited \u003Ccode>"," when it fails. Either way it prints the last 40 log lines of ",[633,935,642],{}," and ",[633,938,650],{},", and the load's own error is just above them. Fix it and run ",[633,941,722],{}," again: ",[633,944,945],{},"--append"," only creates what's missing, so a second run doesn't duplicate anything. To start the stack without loading them, run ",[633,948,949],{},"bin\u002Fup --skip-fixtures",". See ",[618,952,954],{"href":953},"\u002Fapi\u002Fdata-fixtures\u002Fappending#appending-to-an-existing-site","Appending to an Existing Site"," for what a load keeps.",[629,957,959,961],{"id":958},"binup-warns-that-the-first-visit-may-be-slow",[633,960,722],{}," warns that the first visit may be slow",[820,963,966],{"className":964,"code":965,"language":825,"meta":826},[823],"    warning: \u002F still answered 503 after 120s; the first visit may be slow (docker compose logs app)\n",[633,967,965],{"__ignoreMap":826},[611,969,970,971,973,974,936,977,980,981,983,984,987,988,991],{},"After the fixtures, ",[633,972,722],{}," requests ",[633,975,976],{},"\u002F",[633,978,979],{},"\u002Flogin"," from inside ",[633,982,642],{}," so the Nuxt dev server compiles them, and gives each up to 120 seconds. This is only a warning: the site still starts, and the summary is still printed. Your first visit to the page compiles it instead, which can take a while. If the page doesn't load at all, read the dev server's log with ",[633,985,986],{},"docker compose logs app",". A status of ",[633,989,990],{},"000"," means the request got no answer.",[629,993,854],{"id":994},"php-stays-unhealthy",[611,996,997,1000,1001,1003,1004,673,1007,1009,1010,1013,1014,1018],{},[633,998,999],{},"docker compose ps"," shows ",[633,1002,642],{}," as ",[633,1005,1006],{},"unhealthy",[633,1008,809],{}," fails on it. Its health check requests ",[633,1011,1012],{},"\u002F_api\u002F_\u002Fhealth"," on a loopback-only site at port 2080 inside the container (see ",[618,1015,1017],{"href":1016},"\u002Fdeployment\u002Fdocker#waiting-for-the-stack","Docker › Waiting for the stack","). Read the last checks, and request the route yourself:",[820,1020,1024],{"className":1021,"code":1022,"language":1023,"meta":826,"style":826},"language-bash shiki shiki-themes github-light github-dark material-theme-palenight","docker inspect -f '{{json .State.Health}}' $(docker compose ps -q php)\ndocker compose exec php curl -sS http:\u002F\u002F127.0.0.1:2080\u002F_api\u002F_\u002Fhealth\n","bash",[633,1025,1026,1073],{"__ignoreMap":826},[1027,1028,1031,1034,1038,1042,1046,1049,1052,1056,1058,1061,1064,1067,1070],"span",{"class":1029,"line":1030},"line",1,[1027,1032,771],{"class":1033},"sRCss",[1027,1035,1037],{"class":1036},"sLL54"," inspect",[1027,1039,1041],{"class":1040},"szhYu"," -f",[1027,1043,1045],{"class":1044},"seSrl"," '",[1027,1047,1048],{"class":1036},"{{json .State.Health}}",[1027,1050,1051],{"class":1044},"'",[1027,1053,1055],{"class":1054},"sOvfz"," $(",[1027,1057,771],{"class":1033},[1027,1059,1060],{"class":1036}," compose",[1027,1062,1063],{"class":1036}," ps",[1027,1065,1066],{"class":1040}," -q",[1027,1068,1069],{"class":1036}," php",[1027,1071,1072],{"class":1054},")\n",[1027,1074,1076,1078,1080,1083,1085,1088,1091],{"class":1029,"line":1075},2,[1027,1077,771],{"class":1033},[1027,1079,1060],{"class":1036},[1027,1081,1082],{"class":1036}," exec",[1027,1084,1069],{"class":1036},[1027,1086,1087],{"class":1036}," curl",[1027,1089,1090],{"class":1040}," -sS",[1027,1092,1093],{"class":1036}," http:\u002F\u002F127.0.0.1:2080\u002F_api\u002F_\u002Fhealth\n",[660,1095,1096,1113,1144],{},[663,1097,1098,1108,1109,1112],{},[666,1099,1100,1103,1104,1107],{},[633,1101,1102],{},"503"," with ",[633,1105,1106],{},"\"reason\":\"database\"",":"," the database is down or unreachable. Check ",[633,1110,1111],{},"docker compose ps database"," and its logs.",[663,1114,1115,1118,1119,1122,1123,1126,1127,1129,1130,1132,1133,1136,1137,1140,1141,622],{},[666,1116,1117],{},"The connection is refused:"," Caddy isn't running yet, because the entrypoint is still running ",[633,1120,1121],{},"composer install"," or the migrations (",[633,1124,1125],{},"docker compose logs php","). The check allows 300 seconds for that; a first ",[633,1128,1121],{}," on a slow connection can take longer, and ",[633,1131,642],{}," turns healthy once it answers. If the start finished and it's still refused, the image's ",[633,1134,1135],{},"api\u002FDockerfile"," is newer than its Caddyfile, which has no port 2080 site: copy both, with ",[633,1138,1139],{},"app\u002FDockerfile",", and run ",[633,1142,1143],{},"docker compose build php app",[663,1145,1146,1151,1152,1155,1156,1159,1160,1163,1164,622],{},[666,1147,1148,1107],{},[633,1149,1150],{},"400"," Symfony refused the ",[633,1153,1154],{},"Host",". The check sends ",[633,1157,1158],{},"BROWSER_SERVER_NAME"," (default ",[633,1161,1162],{},"localhost","), so it must match ",[633,1165,1166],{},"TRUSTED_HOSTS",[629,1168,864,1170,867],{"id":1169},"the-database-container-exits-because-its-data-is-from-an-older-postgresql",[633,1171,650],{},[611,1173,1174,1177],{},[633,1175,1176],{},"docker compose logs database"," shows:",[820,1179,1182],{"className":1180,"code":1181,"language":825,"meta":826},[823],"Error: in 18+, these Docker images are configured to store database data in a\n       format which is compatible with \"pg_ctlcluster\" …\n       Counter to that, there appears to be PostgreSQL data in:\n         \u002Fvar\u002Flib\u002Fpostgresql\n",[633,1183,1181],{"__ignoreMap":826},[611,1185,864,1186,1188,1189,1192,1193,1195],{},[633,1187,650],{}," container runs PostgreSQL 18, and its ",[633,1190,1191],{},"database_data"," volume holds data from an older PostgreSQL, such as 16. PostgreSQL 18 won't start on another major version's data directory, so ",[633,1194,642],{}," never turns healthy either.",[611,1197,1198,1199,1202,1203,1206,1207,1210,1211,1215],{},"If you don't need the local content, remove the volumes and start again. ",[666,1200,1201],{},"This deletes the local database: every page, component and user in it."," ",[633,1204,1205],{},"-v"," also removes the ",[633,1208,1209],{},"caddy_data"," volume, so the browser warns about the certificate again (see ",[618,1212,1214],{"href":1213},"\u002Fdeployment\u002Fdocker#volumes","Docker › Volumes","):",[820,1217,1219],{"className":1021,"code":1218,"language":1023,"meta":826,"style":826},"docker compose down -v\nbin\u002Fup\n",[633,1220,1221,1233],{"__ignoreMap":826},[1027,1222,1223,1225,1227,1230],{"class":1029,"line":1030},[1027,1224,771],{"class":1033},[1027,1226,1060],{"class":1036},[1027,1228,1229],{"class":1036}," down",[1027,1231,1232],{"class":1040}," -v\n",[1027,1234,1235],{"class":1029,"line":1075},[1027,1236,1237],{"class":1033},"bin\u002Fup\n",[611,1239,1240,1242,1243,1246,1247,1250,1251,1253],{},[633,1241,722],{}," creates an empty PostgreSQL 18 database and loads the fixtures. To keep the content, dump it with the old version first. A one-off PostgreSQL 16 container can read the volume (",[633,1244,1245],{},"\u003Cproject>"," is the Compose project name, by default the folder's name), and ",[633,1248,1249],{},"--clean"," lets the dump replace the tables ",[633,1252,722],{}," migrates:",[820,1255,1257],{"className":1021,"code":1256,"language":1023,"meta":826,"style":826},"docker compose down\ndocker run -d --name old-pg -v \u003Cproject>_database_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata postgres:16-alpine\ndocker exec old-pg pg_dump -U app --clean --if-exists app > local.sql\ndocker rm -f old-pg\ndocker compose down -v\nbin\u002Fup --skip-fixtures\ndocker compose exec -T database psql -U app app \u003C local.sql\n",[633,1258,1259,1268,1307,1339,1352,1363,1371],{"__ignoreMap":826},[1027,1260,1261,1263,1265],{"class":1029,"line":1030},[1027,1262,771],{"class":1033},[1027,1264,1060],{"class":1036},[1027,1266,1267],{"class":1036}," down\n",[1027,1269,1270,1272,1275,1278,1281,1284,1287,1291,1294,1298,1301,1304],{"class":1029,"line":1075},[1027,1271,771],{"class":1033},[1027,1273,1274],{"class":1036}," run",[1027,1276,1277],{"class":1040}," -d",[1027,1279,1280],{"class":1040}," --name",[1027,1282,1283],{"class":1036}," old-pg",[1027,1285,1286],{"class":1040}," -v",[1027,1288,1290],{"class":1289},"sVlFx"," \u003C",[1027,1292,1293],{"class":1036},"projec",[1027,1295,1297],{"class":1296},"sPB8G","t",[1027,1299,1300],{"class":1289},">",[1027,1302,1303],{"class":1036},"_database_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata",[1027,1305,1306],{"class":1036}," postgres:16-alpine\n",[1027,1308,1310,1312,1314,1316,1319,1322,1325,1328,1331,1333,1336],{"class":1029,"line":1309},3,[1027,1311,771],{"class":1033},[1027,1313,1082],{"class":1036},[1027,1315,1283],{"class":1036},[1027,1317,1318],{"class":1036}," pg_dump",[1027,1320,1321],{"class":1040}," -U",[1027,1323,1324],{"class":1036}," app",[1027,1326,1327],{"class":1040}," --clean",[1027,1329,1330],{"class":1040}," --if-exists",[1027,1332,1324],{"class":1036},[1027,1334,1335],{"class":1289}," >",[1027,1337,1338],{"class":1036}," local.sql\n",[1027,1340,1342,1344,1347,1349],{"class":1029,"line":1341},4,[1027,1343,771],{"class":1033},[1027,1345,1346],{"class":1036}," rm",[1027,1348,1041],{"class":1040},[1027,1350,1351],{"class":1036}," old-pg\n",[1027,1353,1355,1357,1359,1361],{"class":1029,"line":1354},5,[1027,1356,771],{"class":1033},[1027,1358,1060],{"class":1036},[1027,1360,1229],{"class":1036},[1027,1362,1232],{"class":1040},[1027,1364,1366,1368],{"class":1029,"line":1365},6,[1027,1367,722],{"class":1033},[1027,1369,1370],{"class":1040}," --skip-fixtures\n",[1027,1372,1374,1376,1378,1380,1383,1386,1389,1391,1393,1395,1397],{"class":1029,"line":1373},7,[1027,1375,771],{"class":1033},[1027,1377,1060],{"class":1036},[1027,1379,1082],{"class":1036},[1027,1381,1382],{"class":1040}," -T",[1027,1384,1385],{"class":1036}," database",[1027,1387,1388],{"class":1036}," psql",[1027,1390,1321],{"class":1040},[1027,1392,1324],{"class":1036},[1027,1394,1324],{"class":1036},[1027,1396,1290],{"class":1289},[1027,1398,1338],{"class":1036},[611,1400,1401,1402,936,1405,1408,1409,1411,1412,1415],{},"Use your own ",[633,1403,1404],{},"POSTGRES_USER",[633,1406,1407],{},"POSTGRES_DB"," in place of ",[633,1410,646],{}," if you set them. On a single server the volume holds the live site, so never use ",[633,1413,1414],{},"down -v"," there without a dump.",[629,1417,881,1419,884],{"id":1418},"every-page-returns-502-or-503-after-you-run-pnpm-install-on-the-host",[633,1420,787],{},[611,1422,1423,1424,1426,1427,936,1430,1433,1434,1436,1437,1439,1440,1443,1444,1446,1447,1450,1451,1454,1455,734,1458,1461],{},"In development, ",[633,1425,718],{}," bind-mounts ",[633,1428,1429],{},"app\u002F",[633,1431,1432],{},"app\u002Fnode_modules"," into the ",[633,1435,646],{}," container, and that container runs ",[633,1438,787],{}," and then ",[633,1441,1442],{},"pnpm dev",". A ",[633,1445,787],{}," on your machine, such as for a dependency bump, rewrites the shared ",[633,1448,1449],{},"node_modules"," while the container's dev server is still running from it. The dev server then crash-loops with errors like ",[633,1452,1453],{},"Cannot find module 'typescript'",", a missing ",[633,1456,1457],{},"@nuxt\u002Fcli\u002Fdist\u002Fdev\u002Findex.mjs",[633,1459,1460],{},"Cannot resolve module \"@nuxt\u002Fkit\"",", and Caddy returns 502 or 503 for every page. Restart the container so it reinstalls from inside:",[820,1463,1465],{"className":1021,"code":1464,"language":1023,"meta":826,"style":826},"docker compose restart app\n",[633,1466,1467],{"__ignoreMap":826},[1027,1468,1469,1471,1473,1476],{"class":1029,"line":1030},[1027,1470,771],{"class":1033},[1027,1472,1060],{"class":1036},[1027,1474,1475],{"class":1036}," restart",[1027,1477,1478],{"class":1036}," app\n",[611,1480,1481,1482,622],{},"When you check its logs afterwards, only look at lines after the restart. The crash from before it is still in the log and looks like a new failure. See ",[618,1483,1485],{"href":1484},"\u002Fdeployment\u002Fdocker#development-workflow","Docker › Development Workflow",[629,1487,1489],{"id":1488},"every-dev-page-takes-about-30-seconds","Every dev page takes about 30 seconds",[611,1491,1492,1493,1496,1497,1500],{},"This is almost always ",[666,1494,1495],{},"missing PostgreSQL statistics",", not slow I\u002FO. CWA's components use Doctrine ",[633,1498,1499],{},"JOINED"," inheritance, so loading a page emits a wide multi-table join. The component subclass tables hold only a handful of rows each — permanently below the threshold that triggers PostgreSQL's autoanalyze — so those tables are never analysed, the planner has no statistics, and it wildly over-estimates the join and throws parallel workers at a query that returns almost nothing. Fix it by analysing the database once:",[820,1502,1504],{"className":1021,"code":1503,"language":1023,"meta":826,"style":826},"docker compose exec database psql -U app -d app -c 'ANALYZE;'\n",[633,1505,1506],{"__ignoreMap":826},[1027,1507,1508,1510,1512,1514,1516,1518,1520,1522,1524,1526,1529,1531,1534],{"class":1029,"line":1030},[1027,1509,771],{"class":1033},[1027,1511,1060],{"class":1036},[1027,1513,1082],{"class":1036},[1027,1515,1385],{"class":1036},[1027,1517,1388],{"class":1036},[1027,1519,1321],{"class":1040},[1027,1521,1324],{"class":1036},[1027,1523,1277],{"class":1040},[1027,1525,1324],{"class":1036},[1027,1527,1528],{"class":1040}," -c",[1027,1530,1045],{"class":1044},[1027,1532,1533],{"class":1036},"ANALYZE;",[1027,1535,1536],{"class":1044},"'\n",[611,1538,1539,1540,1542,1543,1546,1547,622],{},"The template's ",[633,1541,642],{}," entrypoint does this on every ",[633,1544,1545],{},"compose up",", but a new small fixture table can still trigger it until the next restart. See ",[618,1548,1550],{"href":1549},"\u002Fdeployment\u002Fdocker#starting-the-stack","Docker › Starting the Stack",[1552,1553,1556],"callout",{"color":1554,"icon":1555},"warning","i-heroicons-exclamation-triangle",[611,1557,1558,1559,1562,1563,1567,1568,1571,1572,1575,1576,1579],{},"This is easy to misdiagnose because it's a pure query-planner problem with ",[666,1560,1561],{},"no I\u002FO",". A near-idle database, tiny tables, and a 99% cache-hit ratio are all true ",[1564,1565,1566],"em",{},"and completely consistent with it"," — none of them rule the database out. If dev is mysteriously slow, go straight to ",[633,1569,1570],{},"EXPLAIN (ANALYZE, BUFFERS)"," and check ",[633,1573,1574],{},"SELECT relname, reltuples FROM pg_class"," for tables reporting ",[633,1577,1578],{},"-1"," (never analysed).",[629,1581,1583,1586,1587,1590],{"id":1582},"composer-update-fails-with-a-rawgithubusercontentcom-404",[633,1584,1585],{},"composer update"," fails with a ",[633,1588,1589],{},"raw.githubusercontent.com"," 404",[611,1592,1593,1594,1597,1598,1601,1602,1605,1606,1608,1609,1612,1613,1616,1617,1620,1621,1623,1624,976,1627,1630],{},"If Composer dies downloading a Symfony Flex recipes index with an HTTP 404, the cause is usually a ",[666,1595,1596],{},"stale GitHub token"," in the container's ",[633,1599,1600],{},"\u002Fconfig\u002Fcomposer\u002Fauth.json",", not a network or GitHub outage. GitHub returns ",[666,1603,1604],{},"404, not 401",", for a bad token on ",[633,1607,1589],{}," — and a plain ",[633,1610,1611],{},"curl"," of the same URL returns 200 because it sends no token, which misleadingly \"proves\" the network is fine. The ",[633,1614,1615],{},"\u002Fconfig"," volume (",[633,1618,1619],{},"caddy_config",", see ",[618,1622,1214],{"href":1213},") persists across ",[633,1625,1626],{},"compose down",[633,1628,1629],{},"up",", so the bad token survives restarts. Clear it:",[820,1632,1634],{"className":1021,"code":1633,"language":1023,"meta":826,"style":826},"docker compose exec php rm -f \u002Fconfig\u002Fcomposer\u002Fauth.json\n",[633,1635,1636],{"__ignoreMap":826},[1027,1637,1638,1640,1642,1644,1646,1648,1650],{"class":1029,"line":1030},[1027,1639,771],{"class":1033},[1027,1641,1060],{"class":1036},[1027,1643,1082],{"class":1036},[1027,1645,1069],{"class":1036},[1027,1647,1346],{"class":1036},[1027,1649,1041],{"class":1040},[1027,1651,1652],{"class":1036}," \u002Fconfig\u002Fcomposer\u002Fauth.json\n",[611,1654,1655,1656,1658,1659,885],{},"Then re-run ",[633,1657,1585],{}," (or set a valid ",[633,1660,1661],{},"GITHUB_TOKEN",[629,1663,1665,1666,1669],{"id":1664},"composer-or-php-reports-a-missing-ffi-extension","Composer or php reports a missing ",[633,1667,1668],{},"ffi"," extension",[611,1671,1672,1673,1676,1677,1681,1682,1684],{},"The project's Composer dependencies require ",[633,1674,1675],{},"ext-ffi"," for the libvips imagine driver (see ",[618,1678,1680],{"href":1679},"\u002Fdeployment\u002Fdocker#image-processing","Docker › Image processing","). A php image built without them has no libvips or ",[633,1683,1668],{},". Rebuild the dev php image:",[820,1686,1688],{"className":1021,"code":1687,"language":1023,"meta":826,"style":826},"docker compose build php && docker compose up -d php\n",[633,1689,1690],{"__ignoreMap":826},[1027,1691,1692,1694,1696,1699,1701,1704,1707,1709,1712,1714],{"class":1029,"line":1030},[1027,1693,771],{"class":1033},[1027,1695,1060],{"class":1036},[1027,1697,1698],{"class":1036}," build",[1027,1700,1069],{"class":1036},[1027,1702,1703],{"class":1054}," &&",[1027,1705,1706],{"class":1033}," docker",[1027,1708,1060],{"class":1036},[1027,1710,1711],{"class":1036}," up",[1027,1713,1277],{"class":1040},[1027,1715,1716],{"class":1036}," php\n",[629,1718,1720,1721,1724],{"id":1719},"caddy-reports-unexpected-eof-after-you-edit-the-caddyfile","Caddy reports ",[633,1722,1723],{},"unexpected EOF"," after you edit the Caddyfile",[611,1726,1727,1728,1730,1731,1734,1735,1738,1739,1742],{},"If Caddy fails with ",[633,1729,1723],{}," while ",[633,1732,1733],{},"api\u002Ffrankenphp\u002FCaddyfile"," looks valid on disk, it's a stale-config issue, not a syntax error. On macOS\u002FWindows (Docker Desktop), a ",[666,1736,1737],{},"single-file"," bind mount caches the file's size, so an edit that makes the file ",[1564,1740,1741],{},"longer"," — what most editors' atomic save does — is served to the container truncated. Edits that shrink or keep the length work, which makes it look random. Confirm by comparing byte counts:",[820,1744,1746],{"className":1021,"code":1745,"language":1023,"meta":826,"style":826},"wc -c \u003C api\u002Ffrankenphp\u002FCaddyfile\ndocker compose exec php sh -c 'wc -c \u003C \u002Fapp\u002Ffrankenphp\u002FCaddyfile'\n",[633,1747,1748,1760],{"__ignoreMap":826},[1027,1749,1750,1753,1755,1757],{"class":1029,"line":1030},[1027,1751,1752],{"class":1033},"wc",[1027,1754,1528],{"class":1040},[1027,1756,1290],{"class":1289},[1027,1758,1759],{"class":1036}," api\u002Ffrankenphp\u002FCaddyfile\n",[1027,1761,1762,1764,1766,1768,1770,1773,1775,1777,1780],{"class":1029,"line":1075},[1027,1763,771],{"class":1033},[1027,1765,1060],{"class":1036},[1027,1767,1082],{"class":1036},[1027,1769,1069],{"class":1036},[1027,1771,1772],{"class":1036}," sh",[1027,1774,1528],{"class":1040},[1027,1776,1045],{"class":1044},[1027,1778,1779],{"class":1036},"wc -c \u003C \u002Fapp\u002Ffrankenphp\u002FCaddyfile",[1027,1781,1536],{"class":1044},[611,1783,1784,1785,1788,1789,1792,1793,1202,1796,1799,1800,1802],{},"If they differ, recreate the container (",[633,1786,1787],{},"restart"," isn't enough): ",[633,1790,1791],{},"docker compose up -d --force-recreate php",". The CWA template avoids this by reading dev config through the ",[633,1794,1795],{},".\u002Fapi",[666,1797,1798],{},"directory"," mount rather than mounting single files — you'll only hit it if you reintroduce a single-file mount in ",[633,1801,718],{},". Production is unaffected: the image bakes the Caddyfile in.",[629,1804,1806],{"id":1805},"the-dev-server-loads-but-nothing-on-the-page-responds","The dev server loads, but nothing on the page responds",[611,1808,1809,1810,1813,1814,1817,1818,1821,1822,1107],{},"On Nuxt 4.5, if pages render but never hydrate, so buttons do nothing and the login form reloads the page, check the browser console for a ",[633,1811,1812],{},"SyntaxError"," about ",[633,1815,1816],{},"__unhead_devtoolsPlugin"," being declared twice. ",[633,1819,1820],{},"@unhead\u002Fbundler"," 3.4.1 registers its DevTools runtime import once for each of Nuxt's client and server Vite servers. Turn off unhead's own DevTools panel in ",[633,1823,1824],{},"nuxt.config.ts",[820,1826,1830],{"className":1827,"code":1828,"language":1829,"meta":826,"style":826},"language-ts shiki shiki-themes github-light github-dark material-theme-palenight","export default defineNuxtConfig({\n  unhead: {\n    vite: {\n      devtools: false,\n    },\n  },\n})\n","ts",[633,1831,1832,1851,1862,1871,1885,1890,1895],{"__ignoreMap":826},[1027,1833,1834,1838,1841,1845,1848],{"class":1029,"line":1030},[1027,1835,1837],{"class":1836},"sm4w6","export",[1027,1839,1840],{"class":1836}," default",[1027,1842,1844],{"class":1843},"sKpYG"," defineNuxtConfig",[1027,1846,1847],{"class":1296},"(",[1027,1849,1850],{"class":1054},"{\n",[1027,1852,1853,1857,1859],{"class":1029,"line":1075},[1027,1854,1856],{"class":1855},"sDHlG","  unhead",[1027,1858,1107],{"class":1054},[1027,1860,1861],{"class":1054}," {\n",[1027,1863,1864,1867,1869],{"class":1029,"line":1309},[1027,1865,1866],{"class":1855},"    vite",[1027,1868,1107],{"class":1054},[1027,1870,1861],{"class":1054},[1027,1872,1873,1876,1878,1882],{"class":1029,"line":1341},[1027,1874,1875],{"class":1855},"      devtools",[1027,1877,1107],{"class":1054},[1027,1879,1881],{"class":1880},"swWMF"," false",[1027,1883,1884],{"class":1054},",\n",[1027,1886,1887],{"class":1029,"line":1354},[1027,1888,1889],{"class":1054},"    },\n",[1027,1891,1892],{"class":1029,"line":1365},[1027,1893,1894],{"class":1054},"  },\n",[1027,1896,1897,1900],{"class":1029,"line":1373},[1027,1898,1899],{"class":1054},"}",[1027,1901,1072],{"class":1296},[611,1903,1904,1905,1908],{},"This only affects development. Nuxt DevTools and ",[633,1906,1907],{},"useSeoMeta"," still work, and the production build is unchanged. The template sets this. Remove it once an unhead release fixes the duplicate.",[629,1910,1912],{"id":1911},"the-rate-limit-never-triggers-on-the-dev-stack","The rate limit never triggers on the dev stack",[611,1914,1915,1916,1919,1920,622],{},"The rate limit never applies to private addresses, so your own browser may never trigger it locally. To try it, send an ",[633,1917,1918],{},"X-Forwarded-For"," header with a public address: the request comes from a trusted private address, so Caddy believes the header. See ",[618,1921,1923],{"href":1922},"\u002Fdeployment\u002Fdocker#origin-protection-settings","Docker › Origin protection settings",[624,1925,1927],{"id":1926},"single-server-production","Single-server production",[629,1929,1931,1934,1935],{"id":1930},"docker-compose-stops-with-required-variable-is-missing-a-value",[633,1932,1933],{},"docker compose"," stops with ",[633,1936,1937],{},"required variable … is missing a value",[611,1939,1940,1943,1944,1947,1948,1951,1952,1955,1956,1959,1960,877,1963,1967,1968,622],{},[633,1941,1942],{},"compose.prod.yaml"," requires every secret and site value, and names the one that's missing, for example ",[633,1945,1946],{},"required variable SITE_HOST is missing a value: set SITE_HOST to the site's domain",". Set it in the env file you pass with ",[633,1949,1950],{},"--env-file",": ",[633,1953,1954],{},".env.prod",", if ",[633,1957,1958],{},"bin\u002Fserver\u002Finit"," wrote it. Run Compose by hand as ",[633,1961,1962],{},"docker compose --env-file .env.prod …",[618,1964,1966],{"href":1965},"\u002Fdeployment\u002Fone-server#running-commands-by-hand","Deploy to One Server › Running commands by hand","). See ",[618,1969,1971],{"href":1970},"\u002Fdeployment\u002Fdocker#production-docker-compose","Docker › Production Docker Compose",[629,1973,1975,1976],{"id":1974},"php-wont-start-site_host-and-site_aliases-must-be-host-names-without-a-scheme-or-port","php won't start: ",[633,1977,1978],{},"SITE_HOST and SITE_ALIASES must be host names without a scheme or port",[611,1980,1981,1982,1984,1985,936,1988,1991,1992,734,1995,1998,1999,2002,2003,2005],{},"php's entrypoint builds ",[633,1983,1166],{}," and the CORS origins from ",[633,1986,1987],{},"SITE_HOST",[633,1989,1990],{},"SITE_ALIASES",", and stops on a value that isn't a bare host, such as ",[633,1993,1994],{},"https:\u002F\u002Fexample.com",[633,1996,1997],{},"example.com:443",". Set ",[633,2000,2001],{},"SITE_HOST=example.com",", and separate ",[633,2004,1990],{}," with spaces.",[611,2007,2008,2009,2012,2013,2015,2016,2019,2020,622],{},"A comma between aliases stops it with ",[633,2010,2011],{},"SITE_ALIASES is space-separated, not comma-separated: '\u003Cvalue>'.",", because Compose puts ",[633,2014,1990],{}," into Caddy's ",[633,2017,2018],{},"SERVER_NAME",", which refuses ",[633,2021,2022],{},"a,b",[629,2024,2026,748,2028],{"id":2025},"binserverinit-says-envprod-already-exists",[633,2027,1958],{},[633,2029,2030],{},".env.prod already exists",[611,2032,2033,2036,2037,2039,2040,622],{},[633,2034,2035],{},"init"," never overwrites the site's settings, because they hold its secrets: a new JWT key pair signs every user out. Edit ",[633,2038,1954],{}," instead. To start again on a new site, delete it first. See ",[618,2041,2043,2044],{"href":2042},"\u002Fdeployment\u002Fone-server#write-the-settings-with-init","Deploy to One Server › Write the settings with ",[633,2045,2035],{},[629,2047,2049,2050],{"id":2048},"a-script-says-no-envprod-run-binserverinit-first","A script says ",[633,2051,2052],{},"No .env.prod: run bin\u002Fserver\u002Finit first.",[611,2054,2055,702,2058,936,2061,2064,2065,2067,2068,2070,2071,719,2073,2076,2077,885],{},[633,2056,2057],{},"deploy.sh",[633,2059,2060],{},"backup.sh",[633,2062,2063],{},"restore.sh"," read the site's settings from ",[633,2066,1954],{}," at the root of the checkout. Run ",[633,2069,1958],{},", or, on a server taking over a site, copy the old server's ",[633,2072,1954],{},[633,2074,2075],{},"CWA_SERVER_ENV_FILE"," names a different file (see ",[618,2078,2080],{"href":2079},"\u002Fdeployment\u002Fvariables#single-server-scripts","CI\u002FCD Variables › Single-server scripts",[629,2082,2084,1934,2086],{"id":2083},"deploysh-stops-with-the-migrations-plan-failed-nothing-was-deployed",[633,2085,2057],{},[633,2087,2088],{},"❌ The migrations plan failed: nothing was deployed.",[611,2090,2091,2092,622],{},"The new php image couldn't plan the pending migrations against the live database, and the log above shows why. The images are built, but nothing restarted, so the site still runs the previous deploy. Fix the migration, then deploy again. See ",[618,2093,2095],{"href":2094},"\u002Fdeployment\u002Fmigrations-and-backups#on-one-server","Migrations and Backups › On one server",[629,2097,2099,2100],{"id":2098},"a-backup-fails-with-the-dump-doesnt-read-back-pg_restore-list-no-backup-written","A backup fails with ",[633,2101,2102],{},"❌ The dump doesn't read back (pg_restore --list): no backup written.",[611,2104,2105,2108,2109,2112,2113,2115,2116,2119,2120,2123,2124,2126],{},[633,2106,2107],{},"pg_dump"," wrote a file that ",[633,2110,2111],{},"pg_restore"," can't read, for example because the disk filled up, so ",[633,2114,2060],{}," deleted it. Free some space, or move ",[633,2117,2118],{},"backups\u002F"," elsewhere with ",[633,2121,2122],{},"CWA_SERVER_BACKUP_DIR",". When ",[633,2125,2057],{}," took the backup before migrating, the deploy stopped there and nothing restarted.",[629,2128,2130,2132,2133],{"id":2129},"deploysh-ends-with-deployed-but-image-variants-failed",[633,2131,2057],{}," ends with ",[633,2134,2135],{},"❌ Deployed, but image variants failed",[611,2137,2138],{},"The site is deployed: only the image variants failed, and they run in their own containers. The line before it says why:",[660,2140,2141,2164],{},[663,2142,2143,2148,2149,2152,2153,2156,2157,2152,2160,2163],{},[666,2144,2145,1107],{},[633,2146,2147],{},"Image variants ran out of memory (exit 137)"," a container went over ",[633,2150,2151],{},"CWA_SERVER_IMAGE_VARIANTS_MEMORY"," (",[633,2154,2155],{},"1536m","). Raise it, or lower ",[633,2158,2159],{},"CWA_SERVER_IMAGE_VARIANTS_MAX_MEMORY",[633,2161,2162],{},"1G",") so each container stops sooner and the next carries on.",[663,2165,2166,2171],{},[666,2167,2168,1107],{},[633,2169,2170],{},"imagine:generate exited 1"," a variant failed; the log above lists which.",[611,2173,2174,2177,2178,622],{},[633,2175,2176],{},"WARNING: image variants still missing after \u003Cn> runs"," isn't a failure: the next deploy carries on. See ",[618,2179,2080],{"href":2079},[629,2181,2183,2185,2186],{"id":2182},"deploysh-warns-the-page-warm-failed",[633,2184,2057],{}," warns ",[633,2187,2188],{},"the page warm failed",[611,2190,2191,2192,2195],{},"The deploy finished, but some sitemap pages didn't return ",[633,2193,2194],{},"200"," when it requested them from the server itself, and the log above lists them. They render on their first visit instead. Open one to see its error. A sitemap with no pages fails the warm too.",[629,2197,2199,2200,2202],{"id":2198},"the-site-stays-in-maintenance-mode-after-restoresh-fails","The site stays in maintenance mode after ",[633,2201,2063],{}," fails",[611,2204,2205,2207],{},[633,2206,2063],{}," turns maintenance mode on before it replaces anything, and turns it off only at the end, so a restore that stops halfway leaves visitors on the maintenance page. Fix the error and run it again, or turn maintenance off by hand:",[820,2209,2211],{"className":1021,"code":2210,"language":1023,"meta":826,"style":826},"docker compose --env-file .env.prod exec php bin\u002Fconsole silverback:api-components:maintenance off\n",[633,2212,2213],{"__ignoreMap":826},[1027,2214,2215,2217,2219,2222,2225,2227,2229,2232,2235],{"class":1029,"line":1030},[1027,2216,771],{"class":1033},[1027,2218,1060],{"class":1036},[1027,2220,2221],{"class":1040}," --env-file",[1027,2223,2224],{"class":1036}," .env.prod",[1027,2226,1082],{"class":1036},[1027,2228,1069],{"class":1036},[1027,2230,2231],{"class":1036}," bin\u002Fconsole",[1027,2233,2234],{"class":1036}," silverback:api-components:maintenance",[1027,2236,2237],{"class":1036}," off\n",[611,2239,2240,2243,2244,622],{},[633,2241,2242],{},"No complete backup in backups\u002F."," means it found no database dump and media archive with the same time. See ",[618,2245,2247],{"href":2246},"\u002Fdeployment\u002Fone-server#restore-a-backup","Deploy to One Server › Restore a backup",[624,2249,2251],{"id":2250},"caddy-and-souin","Caddy and Souin",[611,2253,2254,1202,2260,2263,2264,2267,2268,2270,2271,2274,2275,2278,2279,2282,2283,2286],{},[666,2255,2256,2257,622],{},"Check every Caddyfile change with ",[633,2258,2259],{},"bin\u002Ftest\u002Fcaddy-validate.sh",[633,2261,2262],{},"frankenphp adapt"," only parses the Caddyfile, so errors raised while Caddy loads its modules, like several below, pass it. The script runs ",[633,2265,2266],{},"frankenphp validate",", which loads every module, for both ",[633,2269,2018],{}," shapes the template uses: Helm's ",[633,2272,2273],{},":80"," and compose's several ports. It also checks each with php forwarding Mercure to a hub of its own (scaled mode), and the hub's own ",[633,2276,2277],{},"mercure-hub.Caddyfile",". Run it locally with ",[633,2280,2281],{},"docker compose exec -T php sh -s \u003C bin\u002Ftest\u002Fcaddy-validate.sh",". It checks the Caddyfile at ",[633,2284,2285],{},"\u002Fetc\u002Fcaddy\u002FCaddyfile",", which is the copy built into the image, so rebuild php after editing. The unit-tests job runs it in CI too.",[611,2288,2289,2290,622],{},"How the template's Caddyfile caches is in ",[618,2291,2293],{"href":2292},"\u002Fdeployment\u002Fdocker#the-http-cache-in-caddy","Docker › The HTTP Cache in Caddy",[629,2295,2297,2298,2301],{"id":2296},"php-wont-start-with-unrecognized-directive-after-you-copy-the-templates-caddyfile","php won't start with ",[633,2299,2300],{},"unrecognized directive"," after you copy the template's Caddyfile",[611,2303,2304,2305,2308,2309,2313,2314,2317,2318,2321,2322,2325],{},"The Caddyfile uses two Caddy modules that a php image built without them doesn't have: ",[633,2306,2307],{},"query_allowlist",", the template's own handler for the query allowlist (see ",[618,2310,2312],{"href":2311},"\u002Fdeployment\u002Forigin-protection#query-parameters-on-pages","Origin Protection › Query Parameters on Pages","), and ",[633,2315,2316],{},"rate_limit",", from ",[633,2319,2320],{},"mholt\u002Fcaddy-ratelimit",". Such an image refuses the file. In dev, the ",[633,2323,2324],{},"--watch"," reload keeps the old config running, and the next restart crash-loops. Rebuild the image, or let CI build it:",[820,2327,2328],{"className":1021,"code":1687,"language":1023,"meta":826,"style":826},[633,2329,2330],{"__ignoreMap":826},[1027,2331,2332,2334,2336,2338,2340,2342,2344,2346,2348,2350],{"class":1029,"line":1030},[1027,2333,771],{"class":1033},[1027,2335,1060],{"class":1036},[1027,2337,1698],{"class":1036},[1027,2339,1069],{"class":1036},[1027,2341,1703],{"class":1054},[1027,2343,1706],{"class":1033},[1027,2345,1060],{"class":1036},[1027,2347,1711],{"class":1036},[1027,2349,1277],{"class":1040},[1027,2351,1716],{"class":1036},[629,2353,2355],{"id":2354},"php-wont-start-after-you-copy-the-templates-caddyfile-because-an-import-isnt-found","php won't start after you copy the template's Caddyfile, because an import isn't found",[611,2357,2358,2359,2362,2363,2366,2367,936,2370,2372,2373,2375,2376,2379,2380,622],{},"The Caddyfile's ",[633,2360,2361],{},"mercure"," block imports the hub's settings from ",[633,2364,2365],{},"mercure-hub.caddy",", which sits beside it. Copy ",[633,2368,2369],{},"api\u002Ffrankenphp\u002Fmercure-hub.caddy",[633,2371,2277],{}," too, and the ",[633,2374,1135],{}," line that copies them to ",[633,2377,2378],{},"\u002Fetc\u002Fcaddy\u002F",", then rebuild php. See ",[618,2381,2383],{"href":2382},"\u002Fapi\u002Freal-time#in-the-template","Real-Time Updates (Mercure) › In the Template",[629,2385,2387,2388],{"id":2386},"php-keeps-restarting-with-token-recognition-error-at","php keeps restarting with ",[633,2389,2390],{},"token recognition error at: '#'",[611,2392,2393,2394,2397,2398,2400,2401,2404,2405,2407,2408,2411],{},"Comments inside the ",[633,2395,2396],{},"@use_cache"," expression break Caddy, and ",[633,2399,2262],{}," does not warn you. The text between the backticks is a CEL expression, not Caddyfile syntax. CEL has no comments. A ",[633,2402,2403],{},"#"," line inside the backticks is a parse error, but ",[633,2406,2262],{}," does not check the text inside the backticks, so it reports no problem. The error only appears at runtime, and the php container keeps restarting. Put comments above the block. After changing it, run ",[633,2409,2410],{},"adapt",", then recreate the container and check that it becomes healthy:",[820,2413,2415],{"className":1021,"code":2414,"language":1023,"meta":826,"style":826},"docker compose up -d --force-recreate php\ndocker compose ps php      # should reach \"healthy\", not \"Restarting\"\n",[633,2416,2417,2432],{"__ignoreMap":826},[1027,2418,2419,2421,2423,2425,2427,2430],{"class":1029,"line":1030},[1027,2420,771],{"class":1033},[1027,2422,1060],{"class":1036},[1027,2424,1711],{"class":1036},[1027,2426,1277],{"class":1040},[1027,2428,2429],{"class":1040}," --force-recreate",[1027,2431,1716],{"class":1036},[1027,2433,2434,2436,2438,2440,2442],{"class":1029,"line":1075},[1027,2435,771],{"class":1033},[1027,2437,1060],{"class":1036},[1027,2439,1063],{"class":1036},[1027,2441,1069],{"class":1036},[1027,2443,2445],{"class":2444},"sTBSN","      # should reach \"healthy\", not \"Restarting\"\n",[629,2447,2449],{"id":2448},"a-cache-matcher-never-matches-when-a-cookie-is-absent","A cache matcher never matches when a cookie is absent",[611,2451,2452,2453,2455,2456,2462,2463,2466,2467,2470,2471,2474,2475,2478],{},"If you edit the Souin ",[633,2454,2396],{}," expression in the Caddyfile, note that an ",[666,2457,2458,2459],{},"absent cookie does not equal ",[633,2460,2461],{},"\"\""," in Caddy matchers — unlike a missing ",[1564,2464,2465],{},"header",", which does. A clause like ",[633,2468,2469],{},"{http.request.cookie.api_component} == \"\""," only matches when the cookie is ",[1564,2472,2473],{},"present but empty",", so requests with no cookie at all (SSR, curl, health checks) silently miss the cache. Match on the ",[633,2476,2477],{},"Cookie"," header instead, so an absent or empty cookie both count as \"not authenticated\":",[820,2480,2484],{"className":2481,"code":2482,"language":2483,"meta":826,"style":826},"language-caddyfile shiki shiki-themes github-light github-dark material-theme-palenight","!{http.request.header.Cookie}.matches(\"api_component=[^;]+\")\n","caddyfile",[633,2485,2486],{"__ignoreMap":826},[1027,2487,2488],{"class":1029,"line":1030},[1027,2489,2482],{},[629,2491,2493],{"id":2492},"nothing-is-cached-after-a-full-flush","Nothing is cached after a full flush",[611,2495,2496,2497,734,2500,2503,2504,702,2507,2510,2511,2514,2515,2517],{},"The php log shows ",[633,2498,2499],{},"BADGER-INSERTION-ERROR",[633,2501,2502],{},"DB Closed",". Unpatched Souin v1.7.9 closes the badger and nuts storages on a full flush, and they stay closed until php restarts. A full flush is ",[666,2505,2506],{},"Purge all cached data",[633,2508,2509],{},"purge-http-cache",", every fixture load, a deploy in scaled mode, and a deploy whose Cloudflare edge-only purge fails. The default in-memory otter storage is not affected. The template's build applies ",[633,2512,2513],{},"v1.7.9-flush-no-close.patch",", which empties the store and leaves it open, so this means the php image was built without it. Rebuild the API image with the patch and its lines in ",[633,2516,1135],{},". Until then, restart php after each full flush.",[629,2519,2521,2522],{"id":2520},"the-first-visitor-after-a-flush-or-a-rollout-gets-a-504","The first visitor after a flush or a rollout gets a ",[633,2523,2524],{},"504",[611,2526,2527,2528,2530,2531,1443,2534,2537,2538,2541,2542,2546,2547,622],{},"A page that renders cold in more than Souin's backend timeout gets a ",[633,2529,2524],{}," from the cache, and Cloudflare passes it on. Souin's own default is 10 seconds, and a heavy CMS page took about 12. The Caddyfile sets 30 seconds; raise it with ",[633,2532,2533],{},"CWA_API_CADDY_CACHE_BACKEND_TIMEOUT",[633,2535,2536],{},"timeout"," block in ",[633,2539,2540],{},"CADDY_CACHE_EXTRA_CONFIG"," replaces the Caddyfile's whole block and brings the 10 seconds back. See ",[618,2543,2545],{"href":2544},"\u002Fdeployment\u002Fdocker#the-cache-store-and-backend-timeout","Docker › The cache store and backend timeout",". To avoid cold renders in the first place, purge one page instead of everything: see ",[618,2548,2550],{"href":2549},"\u002Fdeployment\u002Fcloudflare#purges-must-reach-cloudflare","Cloudflare › Purges must reach Cloudflare",[629,2552,2554,2555],{"id":2553},"requests-that-waited-on-a-failed-render-get-an-empty-200","Requests that waited on a failed render get an empty ",[633,2556,2194],{},[611,2558,2559,2560,702,2563,702,2566,734,2568,2570,2571,2573,2574,2577,2578,2581,2582,2585,2586,2589,2590,2592,2593,936,2596,2599,2600,622],{},"Souin sends only one request upstream for a URL that isn't cached, and the others for that URL wait for its response. In unpatched Souin v1.7.9, when that response is a ",[633,2561,2562],{},"500",[633,2564,2565],{},"502",[633,2567,1102],{},[633,2569,2524],{},", the waiting requests get a ",[633,2572,2194],{}," with an empty body and no ",[633,2575,2576],{},"Cache-Status"," header, and an SSR render can cache a broken page. The template's build applies ",[633,2579,2580],{},"v1.7.9-singleflight-5xx.patch",", so they get the same error. A ",[633,2583,2584],{},"5xx"," that is private or sets a cookie isn't shared: each waiting request goes upstream itself. ",[633,2587,2588],{},"bin\u002Ftest\u002Fsouin-coalesced-5xx.sh",", in the unit-tests job, fails on an unpatched binary. If you see empty ",[633,2591,2194],{},"s, rebuild the API image with the patch and its ",[633,2594,2595],{},"COPY",[633,2597,2598],{},"patch"," lines in ",[633,2601,1135],{},[629,2603,2605],{"id":2604},"php-is-oomkilled-while-serving-a-large-file","php is OOMKilled while serving a large file",[611,2607,2608,2609,2611,2612,622],{},"Souin reads the whole body of a cacheable response into memory before it decides anything, so a large file served through the cache can take php past its memory limit. Keep large files out of ",[633,2610,2396],{},": see ",[618,2613,2615],{"href":2614},"\u002Fdeployment\u002Fdocker#what-the-page-cache-stores","Docker › What the page cache stores",[629,2617,2619,734,2622,2625,2626],{"id":2618},"purge-or-ban-sent-to-the-site-returns-405",[633,2620,2621],{},"PURGE",[633,2623,2624],{},"BAN"," sent to the site returns ",[633,2627,2628],{},"405",[611,2630,2631,2632,622],{},"That's intended: they never purged anything. See ",[618,2633,2635,2636,936,2638],{"href":2634},"\u002Fdeployment\u002Fdocker#purge-and-ban-sent-to-the-site-return-405","Docker › ",[633,2637,2621],{},[633,2639,2624],{},[624,2641,2642],{"id":2361},"Mercure",[611,2644,2645,2646,622],{},"The module's own checks for live updates are in ",[618,2647,2649],{"href":2648},"\u002Fnuxt-module\u002Fcomponent-helpers\u002Freal-time-updates#troubleshooting","Real-time Updates › Troubleshooting",[629,2651,2653,2654,2657],{"id":2652},"php-wont-start-with-a-mercure-issuer-or-compatibility-mode-error","php won't start with a Mercure ",[633,2655,2656],{},"issuer"," or compatibility-mode error",[611,2659,2660,2661,734,2664,2667,2668,2671,2672,2675,2676,2678],{},"FrankenPHP 1.13 ships Mercure 1.0, which treats unknown directives as errors and has no ",[633,2662,2663],{},"demo",[633,2665,2666],{},"ui",". If ",[633,2669,2670],{},"MERCURE_EXTRA_DIRECTIVES"," sets either, use ",[633,2673,2674],{},"debugger"," instead, in development only. The template's Caddyfile uses an ",[633,2677,2656],{}," block, which a 0.x Mercure doesn't know, so the Caddyfile and the php image must come from the same template. Two errors mean the Caddyfile and the binary don't match:",[660,2680,2681,2701],{},[663,2682,2683,2686,2687,936,2690,2693,2694,2696,2697,622],{},[633,2684,2685],{},"the \"publisher_jwt\", \"subscriber_jwt\", … directives work only in compatibility mode",": the Caddyfile has top-level ",[633,2688,2689],{},"publisher_jwt",[633,2691,2692],{},"subscriber_jwt"," directives. Move the keys into an ",[633,2695,2656],{}," block, as described in ",[618,2698,2700],{"href":2699},"\u002Fapi\u002Freal-time#hub-caddyfile","Hub Caddyfile",[663,2702,2703,2706,2707,2710,2711,936,2713,2715,2716,936,2719,2722,2723,2725],{},[633,2704,2705],{},"an issuer identifier is required in modern mode",": the Caddyfile is still set up for compatibility mode (",[633,2708,2709],{},"protocol_version_compatibility"," with top-level ",[633,2712,2689],{},[633,2714,2692],{},"), but the binary was built without the ",[633,2717,2718],{},"deprecated_topic",[633,2720,2721],{},"deprecated_claim"," tags, so the hub runs in modern mode anyway. Remove the directive and move the keys into an ",[633,2724,2656],{}," block.",[611,2727,2728,2731],{},[633,2729,2730],{},"caddy adapt"," reports neither error. Only a running hub does, so check php's logs after it starts.",[629,2733,2297,2735],{"id":2734},"php-wont-start-with-only-one-unnamed-mercure-hub-is-allowed-per-configuration",[633,2736,2737],{},"only one unnamed Mercure hub is allowed per configuration",[611,2739,2740,2741,2743,2744,2747,2748,2750,2751,2754,2755,2757,2758,2760,2761,622],{},"An API image built with Mercure 1.0.3 or later refuses the compose stack's Caddyfile unless the ",[633,2742,2361],{}," block has a ",[633,2745,2746],{},"name",". Compose's ",[633,2749,2018],{}," has several ports, so Caddy runs one server per port, each with its own hub. The healthcheck never passes, and the error only shows in the php container's log. The template sets ",[633,2752,2753],{},"name cwa"," in the ",[633,2756,2361],{}," block, in ",[633,2759,2365],{},": add it back if it's missing, then recreate php. See ",[618,2762,2700],{"href":2699},[629,2764,2766],{"id":2765},"admins-get-no-private-updates-locally","Admins get no private updates locally",[611,2768,2769,2770,2773,2774,2777,2778,2781,2782,2785,2786,2789,2790,2793,2794,2797,2798,2801,2802,2804,2805,715,2807,877,2809,885],{},"With a Mercure 1.0 hub, the cookie is ",[633,2771,2772],{},"__Secure-mercure_access_token",", and browsers only accept it over HTTPS. The template's dev stack serves the hub at ",[633,2775,2776],{},"https:\u002F\u002Flocalhost\u002F.well-known\u002Fmercure",", so this works out of the box. ",[633,2779,2780],{},"mercure.yaml"," pins the name as ",[633,2783,2784],{},"cookie_name",", because mercure-bundle 0.6 drops the ",[633,2787,2788],{},"__Secure-"," prefix when ",[633,2791,2792],{},"kernel.debug"," is on, as it is in dev, and the hub then wouldn't read the cookie. If you change ",[633,2795,2796],{},"CADDY_MERCURE_PUBLIC_URL"," to a plain ",[633,2799,2800],{},"http:\u002F\u002F"," URL, change that ",[633,2803,2784],{}," to a prefix-less name, and set the same name as the hub's ",[633,2806,2784],{},[633,2808,2365],{},[618,2810,2812],{"href":2811},"\u002Fapi\u002Freal-time#the-subscriber-cookie","The subscriber cookie",[611,2814,2815,2816,2611,2819,622],{},"If the API and the front end are on different registrable domains, the cookie needs ",[633,2817,2818],{},"SameSite=None",[618,2820,2822],{"href":2821},"\u002Fapi\u002Fusers\u002Fauthentication#the-cookies-samesite","API Authentication › The cookie's SameSite",[624,2824,2826],{"id":2825},"nuxt","Nuxt",[629,2828,2830,2831],{"id":2829},"the-sitemap-is-empty-or-the-nuxt-log-shows-und_err_headers_overflow","The sitemap is empty, or the Nuxt log shows ",[633,2832,2833],{},"UND_ERR_HEADERS_OVERFLOW",[611,2835,2836,2837,2840,2841,2844,2845,2848,2849,2852,2853,622],{},"Node refuses a response whose headers pass 16 KB by default. A large collection's ",[633,2838,2839],{},"Surrogate-Key"," passes that, so Caddy removes the header from ",[633,2842,2843],{},"\u002F_api"," responses on server-side rendering's internal hostname; if a response still carries it there, check the Caddyfile has that rule. The app image also raises Node's limit with ",[633,2846,2847],{},"NODE_OPTIONS=--max-http-header-size=65536","; a deploy that sets its own ",[633,2850,2851],{},"NODE_OPTIONS"," replaces that. See ",[618,2854,2550],{"href":2549},[624,2856,2857],{"id":650},"Database",[629,2859,2861],{"id":2860},"the-in-cluster-postgresql-restarts-empty-or-the-api-cant-connect-after-a-credentials-change","The in-cluster PostgreSQL restarts empty, or the API can't connect after a credentials change",[611,2863,2864,2865,622],{},"Changing the user, password or database name restarts the PostgreSQL pod. Without persistence it starts empty; with persistence it keeps the old values, so the API can't connect until you change them in PostgreSQL. See ",[618,2866,2868],{"href":2867},"\u002Fdeployment\u002Fvariables#database","CI\u002FCD Variables › Database",[624,2870,2872],{"id":2871},"kubernetes-deploys","Kubernetes deploys",[629,2874,2876,2877,702,2880,2883],{"id":2875},"the-php-image-build-hangs-on-apt-get-install-php-extensions-or-the-go-build","The php image build hangs on ",[633,2878,2879],{},"apt-get",[633,2881,2882],{},"install-php-extensions"," or the Go build",[611,2885,2886,2887,2890,2891,2894,2895,2898,2899,2902,2903,2906,2907,2910,2911,936,2915,622],{},"An uncached php build fetches packages from Debian, which can stall on a slow runner. apt uses HTTPS, and each fetching step has a time limit and up to three attempts, logged as ",[633,2888,2889],{},"retry-fetch: attempt \u003Cn> failed or passed \u003Cs>s: …",". The ",[633,2892,2893],{},"builder"," stage's Go module fetches and ",[633,2896,2897],{},"xcaddy build"," get the same limits and retries, and Go fetches over HTTP\u002F1.1; a ",[633,2900,2901],{},"sum.golang.org"," stream error (",[633,2904,2905],{},"stream error … INTERNAL_ERROR",") is the Go-side symptom. The Dockerfile pulls the prebuilt ",[633,2908,2909],{},"cwa-php-base",", so a build runs neither the Go build nor the extension installs unless you build the base from source. See ",[618,2912,2914],{"href":2913},"\u002Fdeployment\u002Fdocker#patches-in-the-build","Docker › Patches in the build",[618,2916,2918],{"href":2917},"\u002Fdeployment\u002Fdocker#where-the-base-comes-from","Docker › Where the base comes from",[629,2920,2922,2923],{"id":2921},"the-review-job-is-orange-and-says-no-namespace-name","The review job is orange and says ",[633,2924,2925],{},"No namespace '\u003Cname>'",[611,2927,2928,2929,622],{},"The branch has no Kubernetes namespace, so it has no review app. Create the namespace and its role bindings, then run the job again. See ",[618,2930,2932],{"href":2931},"\u002Fdeployment\u002Fci-cd#review-apps","CI\u002FCD › Review Apps",[629,2934,2936],{"id":2935},"the-deploy-fails-before-helm-runs","The deploy fails before Helm runs",[611,2938,2939],{},"The deploy checks several things before it changes anything in the cluster, and its log says which one failed and what to change:",[660,2941,2942,2964,2973,2984,2995,3004,3015,3035,3054,3077],{},[663,2943,2944,2951,2952,2955,2956,2959,2960,622],{},[666,2945,2946,2947,2950],{},"No ",[633,2948,2949],{},"DOMAIN",", or one that starts or ends with a dot:"," the environment url or ",[633,2953,2954],{},"KUBE_INGRESS_BASE_DOMAIN"," is empty. See ",[633,2957,2958],{},"check_kube_domain"," in ",[618,2961,2963],{"href":2962},"\u002Fdeployment\u002Fci-cd#the-devops-scripts","The Devops Scripts",[663,2965,2966,850,2969,622],{},[666,2967,2968],{},"The php memory budget doesn't fit:",[618,2970,2972],{"href":2971},"\u002Fdeployment\u002Fvariables#php-memory-budget","CI\u002FCD Variables › php memory budget",[663,2974,2975,850,2980,622],{},[666,2976,2977,1107],{},[633,2978,2979],{},"Site environment variables (NUXT_*, CWA_API_*) that can't be passed through",[618,2981,2983],{"href":2982},"\u002Fdeployment\u002Fvariables#reserved-names","Reserved names",[663,2985,2986,850,2991,622],{},[666,2987,2988,1107],{},[633,2989,2990],{},"CWA_ENVIRONMENT must be letters, digits, '.', '_' or '-'",[618,2992,2994],{"href":2993},"\u002Fdeployment\u002Fvariables#the-environment-name","The environment name",[663,2996,2997,850,3000,622],{},[666,2998,2999],{},"The PostgreSQL persistence settings differ from the live database:",[618,3001,3003],{"href":3002},"\u002Fdeployment\u002Fvariables#in-cluster-postgresql-persistence","In-cluster PostgreSQL persistence",[663,3005,3006,850,3011,622],{},[666,3007,3008,1107],{},[633,3009,3010],{},"CADDY_CACHE_CDN_CONFIG is incomplete, so php would not start",[618,3012,3014],{"href":3013},"\u002Fdeployment\u002Fcloudflare#secrets","Cloudflare › Secrets",[663,3016,3017,1202,3022,3024,3025,3027,3028,3030,3031,622],{},[666,3018,3019,1107],{},[633,3020,3021],{},"The Souin cache settings would be ignored or replaced",[633,3023,2540],{}," names a store or holds a ",[633,3026,2536],{}," block, or ",[633,3029,2533],{}," isn't a Go duration. See ",[618,3032,3034],{"href":3033},"\u002Fdeployment\u002Fvariables#http-cache","CI\u002FCD Variables › HTTP cache",[663,3036,3037,850,3050,622],{},[666,3038,3039,3042,3043,3046,3047,1107],{},[633,3040,3041],{},"A storage path prefix cannot contain \".\""," (or ",[633,3044,3045],{},"\"..\"","), or ",[633,3048,3049],{},"GCLOUD_PATH_PREFIX can't contain a 'review' segment",[618,3051,3053],{"href":3052},"\u002Fdeployment\u002Fvariables#storage-path-prefix","Storage path prefix",[663,3055,3056,3061,3062,3065,3066,3068,3069,950,3073,622],{},[666,3057,3058,1107],{},[633,3059,3060],{},"The API's scaling settings can't be deployed"," more than one API pod, or a ",[633,3063,3064],{},"redis"," cache store, without scaled mode. Keep the API at one pod, or turn on ",[618,3067,557],{"href":558},". The message lists each problem; the other refusals are in ",[618,3070,3072],{"href":3071},"\u002Fdeployment\u002Fscaled-mode#what-the-deploy-refuses","Scaled Mode › What the deploy refuses",[618,3074,3076],{"href":3075},"\u002Fdeployment\u002Fkubernetes#php-stays-at-one-replica","Kubernetes & Helm › php stays at one replica",[663,3078,3079,3082,3083,622],{},[666,3080,3081],{},"A new hostname's certificate wasn't issued:"," point its DNS at the ingress first. See ",[618,3084,3086],{"href":3085},"\u002Fdeployment\u002Fci-cd#changing-a-live-sites-hostnames","Changing a live site's hostnames",[629,3088,3090,3091,702,3094,734,3097],{"id":3089},"the-deploy-stops-with-migrations-plan-failed-database-backup-failed-or-migrations-failed","The deploy stops with ",[633,3092,3093],{},"MIGRATIONS PLAN FAILED",[633,3095,3096],{},"DATABASE BACKUP FAILED",[633,3098,3099],{},"MIGRATIONS FAILED",[611,3101,3102,3103,734,3106,3109,3110,3113,3114,3118,3119,3122,3123,3126,3127,3130,3131,3135,3136,3139,3140,877,3143,1967,3147,622],{},"These come from the database step before the rollout, and none of them changes the live site's code. Every production deploy runs the plan, and the migrate Job when migrations are pending. The plan needs PostgreSQL, the only database the template supports. A plan fails when the dry run of the migrations fails. A migration that writes data from ",[633,3104,3105],{},"up()",[633,3107,3108],{},"postUp()"," instead of with ",[633,3111,3112],{},"addSql()"," doesn't fail it: the plan lists it without SQL and the deploy warns (see ",[618,3115,3117],{"href":3116},"\u002Fdeployment\u002Fmigrations-and-backups#the-migrations-plan","Migrations and Backups › The migrations plan","). A backup fails on a missing, public or media ",[633,3120,3121],{},"CWA_CI_DATABASE_BACKUP_BUCKET",", or a missing or media ",[633,3124,3125],{},"CWA_CI_DATABASE_BACKUP_GCLOUD_JSON",", and a dump bigger than ",[633,3128,3129],{},"CWA_CI_DATABASE_BACKUP_STORAGE"," (5Gi) gets its Job evicted. On GitLab, a Protected key variable never reaches a branch that isn't protected (see ",[618,3132,3134],{"href":3133},"\u002Fdeployment\u002Fmigrations-and-backups#the-backup-bucket","Migrations and Backups › The backup bucket","). If the log says ",[666,3137,3138],{},"MAINTENANCE MODE IS ON",", a maintenance window was open: its last lines say how to restore the backup and turn maintenance off. The next production deploy ends the window, with or without ",[633,3141,3142],{},"CWA_CI_MAINTENANCE_DURING_MIGRATIONS",[618,3144,3146],{"href":3145},"\u002Fdeployment\u002Fmigrations-and-backups#when-the-window-fails","Migrations and Backups › When the window fails",[618,3148,565],{"href":566},[629,3150,3152,3153],{"id":3151},"a-review-deploy-stops-with-review-app-not-deployed","A review deploy stops with ",[633,3154,3155],{},"REVIEW APP NOT DEPLOYED",[611,3157,3158,3159,3162,3163,936,3165,3168,3169,3172,3173,3176,3177,622],{},"The review app's external ",[633,3160,3161],{},"DATABASE_URL"," names production's database (the same host, port and database), which a review app would migrate. On GitLab this usually means production's ",[633,3164,3161],{},[633,3166,3167],{},"CWA_CI_POSTGRES_ENABLED=\"false\""," are scoped to ",[666,3170,3171],{},"All"," environments. Scope them to ",[633,3174,3175],{},"production",", or give review apps a database of their own. See ",[618,3178,3180],{"href":3179},"\u002Fdeployment\u002Fmigrations-and-backups#a-track-on-productions-database","Migrations and Backups › A track on production's database",[611,3182,3183,3184,3187,3188,734,3191,3194],{},"If the log instead warns ",[633,3185,3186],{},"could not read production's database URL",", the check couldn't find production: set ",[633,3189,3190],{},"RELEASE_PRODUCTION",[633,3192,3193],{},"KUBE_NAMESPACE_PRODUCTION"," if production's release or namespace has another name, or give the review job read access to production's Secret.",[629,3196,3198,3199],{"id":3197},"a-scaled-deploy-prints-the-api-cache-is-not-shared-in-redis","A scaled deploy prints ",[633,3200,3201],{},"THE API CACHE IS NOT SHARED IN REDIS",[820,3203,3206],{"className":3204,"code":3205,"language":825},[823],"!!!! SCALED MODE: THE API CACHE IS NOT SHARED IN REDIS !!!!\n",[633,3207,3205],{"__ignoreMap":826},[611,3209,3210,3211,3214,3215,3218,3219,3221,3222,622],{},"After a scaled deploy's flush and warm, three requests through the API Service got no cache hit from Redis (on GitHub, also an ",[633,3212,3213],{},"::error"," annotation, \"API cache not shared\"). The deploy still succeeds, because the release is already live. Until it's fixed, a purge on one php pod can leave the others' copies stale, so an edit may show on some refreshes and not others. Read the Redis pod's log (",[633,3216,3217],{},"kubectl logs deploy\u002F\u003Cfullname>-redis",", as the message prints), look for Redis errors in the php pods' logs, then press ",[666,3220,2506],{}," once Redis answers. See ",[618,3223,3225],{"href":3224},"\u002Fdeployment\u002Fscaled-mode#checking-the-cache-is-shared","Scaled Mode › Checking the cache is shared",[629,3227,3229,3230],{"id":3228},"a-scaled-deploy-warns-up-to-database-connections","A scaled deploy warns ",[633,3231,3232],{},"up to … database connections",[611,3234,3235,3236,3239,3240,702,3243,734,3246,3248,3249,950,3252,622],{},"The php pods at their peak, times ",[633,3237,3238],{},"CWA_CI_API_WORKERS",", plus 5, is more than the in-cluster PostgreSQL's 97 usable connections. The deploy goes ahead. Lower ",[633,3241,3242],{},"CWA_CI_API_AUTOSCALE_MAX",[633,3244,3245],{},"CWA_CI_API_WARM_SURGE",[633,3247,3238],{},", or raise the database's ",[633,3250,3251],{},"max_connections",[618,3253,3255],{"href":3254},"\u002Fdeployment\u002Fscaled-mode#database-connections","Scaled Mode › Database connections",[629,3257,3259],{"id":3258},"a-deploy-didnt-restart-the-pods","A deploy didn't restart the pods",[611,3261,3262,3263,734,3266,3268,3269,3042,3272,3275,3276,3279,3280,622],{},"A deploy restarts a Deployment's pods only when its image digest or settings change. An image built from unchanged inputs is reused with the same digest, so a push that didn't touch ",[633,3264,3265],{},"api\u002F",[633,3267,1429],{}," leaves those pods running. To restart them, run ",[633,3270,3271],{},"kubectl rollout restart deploy\u002F\u003Cfullname>",[633,3273,3274],{},"\u003Cfullname>-pwa","), or set ",[633,3277,3278],{},"CWA_CI_BUILD_FORCE=\"true\""," and run the pipeline. See ",[618,3281,3283],{"href":3282},"\u002Fdeployment\u002Fkubernetes#when-a-deploy-restarts-pods","Kubernetes & Helm › When a deploy restarts pods",[629,3285,3287,3288,3291,3292],{"id":3286},"every-deploy-job-fails-on-its-first-line-or-stop-review-cant-load-k8ssh","Every deploy job fails on its first line, or ",[633,3289,3290],{},"stop review"," can't load ",[633,3293,3294],{},"k8s.sh",[611,3296,3297,3300,3301,3304,3305,3308,3309,3312,3313,3316,3317,3319,3320,3323],{},[633,3298,3299],{},"bin\u002Fdevops\u002Fk8s.sh"," sources ",[633,3302,3303],{},"bin\u002Flib\u002Fsite.sh"," from the repository root. Keep ",[633,3306,3307],{},"bin\u002Flib\u002F"," beside ",[633,3310,3311],{},"bin\u002Fdevops\u002F",", and add ",[633,3314,3315],{},"bin\u002Flib"," to the sparse checkout of a project's own ",[633,3318,3290],{}," job. From another directory, set ",[633,3321,3322],{},"CWA_SITE_LIB"," to the file's path.",[629,3325,3327,3328,3331,3332],{"id":3326},"the-deploy-fails-with-no-digest-for-or-warns-couldnt-tag-as-production","The deploy fails with ",[633,3329,3330],{},"No digest for …",", or warns ",[633,3333,3334],{},"Couldn't tag … as production",[611,3336,3337,3340,3341,3344,3345,622],{},[633,3338,3339],{},"No digest for \u003Cimage>:\u003Ctag>"," stops the deploy before Helm runs. It means the pipeline had no build digest and the branch tag isn't in the registry, for example after ",[633,3342,3343],{},"CWA_CI_BUILD_DISABLED"," on a branch that was never built. See ",[618,3346,3348],{"href":3347},"\u002Fdeployment\u002Fci-cd#which-build-a-deploy-runs","CI\u002FCD › Which build a deploy runs",[611,3350,3351,3354,3355,702,3358,734,3361,3364,3365,3368,3369,622],{},[633,3352,3353],{},"Couldn't tag \u003Cimage>@\u003Cdigest> as production"," comes after a deploy that succeeded, when the registry refused to copy the build to the track's tag (",[633,3356,3357],{},":staging",[633,3359,3360],{},":canary",[633,3362,3363],{},":production",") three times. The deploy only warns, the pods run the new build, and the next deploy copies the tag. On GitHub, check that the deploy job's ",[633,3366,3367],{},"permissions"," include ",[633,3370,3371],{},"packages: write",[629,3373,3327,3375],{"id":3374},"the-deploy-fails-with-the-cwa-rollout-did-not-finish",[633,3376,3377],{},"THE cwa ROLLOUT DID NOT FINISH",[611,3379,3380,3381,3384,3385,3388,3389,936,3392,3395,3396,622],{},"The new API pods (or Nuxt pods, for ",[633,3382,3383],{},"cwa-pwa",") didn't become ready within ",[633,3386,3387],{},"CWA_CI_DEPLOY_ROLLOUT_TIMEOUT"," seconds (900). The usual cause is a migration that fails when the pod starts: on staging, canary or review apps, or on production's first deploy, since production's deploy otherwise runs its migrations in a Job before Helm and stops there when they fail. The old pods keep serving and the track's image tag stays on the last good build. The log prints the ",[633,3390,3391],{},"kubectl describe",[633,3393,3394],{},"kubectl logs"," commands to find out why. Fix forward and deploy again, or roll back with ",[618,3397,3399],{"href":3398},"\u002Fdeployment\u002Fkubernetes#rollback","Kubernetes & Helm › Rollback",[629,3401,3403],{"id":3402},"a-new-php-pod-takes-longer-to-become-ready","A new php pod takes longer to become ready",[611,3405,3406,3407,3410,3411,3413,3414,3418,3419,622],{},"Its ",[633,3408,3409],{},"postStart"," hook warms the ",[633,3412,2843],{}," cache first, in two passes (gzip, then br) of up to the track's warm time each (60 seconds on production and canary; staging and review apps don't warm by default, see ",[618,3415,3417],{"href":3416},"\u002Fdeployment\u002Fvariables#php-api","CI\u002FCD Variables › php (API)","), and Kubernetes starts no probe until the hook returns. The old pod keeps serving meanwhile. The warm's summary is in the pod's log. See ",[618,3420,3422],{"href":3421},"\u002Fdeployment\u002Fkubernetes#warming-the-api-cache-on-start","Kubernetes › Warming the API cache on start",[629,3424,3426,3429],{"id":3425},"varlogprodlog-is-missing-or-the-apis-log-is-empty",[633,3427,3428],{},"var\u002Flog\u002Fprod.log"," is missing, or the API's log is empty",[611,3431,3432,3433,3435,3436,622],{},"Production logs to stderr as JSON: read it with ",[633,3434,3394],{}," or your cluster's log explorer. A request's lines are written only when it logs an error. See ",[618,3437,3439],{"href":3438},"\u002Fdeployment\u002Fkubernetes#logs","Kubernetes › Logs",[629,3441,3443],{"id":3442},"no-error-mail-arrives","No error mail arrives",[611,3445,3446,3449,3450,3453,3454,3457,3458,3460,3461,622],{},[633,3447,3448],{},"CWA_API_ERROR_MAIL_TO"," needs a working ",[633,3451,3452],{},"MAILER_DSN"," and a ",[633,3455,3456],{},"MAILER_EMAIL"," to send from. A failed send is dropped without a log line, only a ",[633,3459,2584],{}," sends one, and each unique error sends at most one an hour per pod. See ",[618,3462,3464],{"href":3463},"\u002Fdeployment\u002Fkubernetes#error-mail","Kubernetes › Error mail",[629,3466,3468],{"id":3467},"the-api-pod-stays-unready-and-every-probe-times-out","The API pod stays unready, and every probe times out",[611,3470,3471,3472,3475],{},"The php probes request ",[633,3473,3474],{},"\u002F_api\u002F_\u002Fsite_config_parameters.jsonld",", a path that would pass through the Souin cache. Souin combines concurrent requests for the same cache key into one request to php. In unpatched Souin 1.7.9, a request that is given up while php is still working keeps hold of that key, so every later probe waits for the 10-second backend timeout and fails, and the pod stays unready for good. The template prevents this three ways:",[660,3477,3478,3491,3500],{},[663,3479,3480,3481,2152,3487,3490],{},"The template's Souin build applies the fix from ",[618,3482,3486],{"href":3483,"rel":3484},"https:\u002F\u002Fgithub.com\u002Fdarkweak\u002Fsouin\u002Fpull\u002F850",[3485],"nofollow","darkweak\u002Fsouin#850",[633,3488,3489],{},"api\u002Ffrankenphp\u002Fsouin\u002Fv1.7.9-singleflight-fix.patch","), so a request that times out releases the key. The patch can be removed once a Souin release includes it.",[663,3492,2358,3493,3495,3496,3499],{},[633,3494,2396],{}," skips the probe path for the kubelet's ",[633,3497,3498],{},"kube-probe\u002F"," user agent, so probes always reach php.",[663,3501,3502],{},"Liveness uses the same request as readiness, so a pod whose php has stopped answering is restarted.",[611,3504,3505,3506,3509,3510,622],{},"Also keep ",[633,3507,3508],{},"timeoutSeconds: 5"," on both probes. See ",[618,3511,3513],{"href":3512},"\u002Fdeployment\u002Fkubernetes#health-checks","Kubernetes › Health Checks",[629,3515,3517,3518,3520],{"id":3516},"visitors-get-502-while-a-deploy-rolls-out","Visitors get ",[633,3519,2565],{}," while a deploy rolls out",[611,3522,3523,3524,3527,3528,622],{},"A pod that closes its listener as soon as it's told to stop still gets requests for a few seconds, until the ingress and php's proxy drop its endpoint. The chart's ",[633,3525,3526],{},"preStop"," hooks hold both pods open for 5 seconds first. See ",[618,3529,3531],{"href":3530},"\u002Fdeployment\u002Fkubernetes#stopping-a-pod-without-502s","Kubernetes › Stopping a pod without 502s",[629,3533,3535,3536],{"id":3534},"the-image-variants-and-warm-job-ends-with-ran-out-of-memory-exit-137","The image variants and warm job ends with ",[633,3537,3538],{},"ran out of memory (exit 137)",[611,3540,3541,3542,3545,3546,3549,3550,622],{},"The Job generating variants went over its own memory limit. The site isn't affected, because the Job doesn't serve requests. Raise ",[633,3543,3544],{},"CWA_CI_IMAGE_VARIANTS_MEMORY",", or lower ",[633,3547,3548],{},"CWA_CI_IMAGE_VARIANTS_MAX_MEMORY"," so each Job stops sooner and the next one carries on. See ",[618,3551,3553],{"href":3552},"\u002Fdeployment\u002Fci-cd#generating-image-variants-after-a-deploy","CI\u002FCD › Generating image variants after a deploy",[629,3555,3517,3557,3559],{"id":3556},"visitors-get-503-under-load",[633,3558,1102],{}," under load",[611,3561,3562,3563,950,3565,622],{},"php gives up on a request that waits too long for a free worker, rather than letting it queue into a ",[633,3564,2524],{},[618,3566,3568],{"href":3567},"\u002Fdeployment\u002Forigin-protection#load-shedding","Origin Protection › Load shedding",[629,3570,3572],{"id":3571},"the-per-visitor-rate-limit-never-applies","The per-visitor rate limit never applies",[611,3574,3575,3576,3579,3580,3583,3584,3587,3588,622],{},"Every visitor looks internal when the ingress controller's Service uses ",[633,3577,3578],{},"externalTrafficPolicy: Cluster",". Set it to ",[633,3581,3582],{},"Local",", and check ",[633,3585,3586],{},"visitor_ip"," in php's access log. See ",[618,3589,3591],{"href":3590},"\u002Fdeployment\u002Forigin-protection#ingress-rate-limits","Origin Protection › Ingress rate limits",[624,3593,3595],{"id":3594},"browser-tests","Browser tests",[629,3597,3599,3600],{"id":3598},"the-browser-tests-fail-with-api-writes-the-api-refused-as-a-mismatched-request","The browser tests fail with ",[633,3601,3602],{},"API writes the API refused as a mismatched request",[611,3604,3605,3606,702,3608,734,3611,3614,3615,734,3618,3621,3622,622],{},"An API write got a ",[633,3607,2628],{},[633,3609,3610],{},"406",[633,3612,3613],{},"415",": the front end sent a method, ",[633,3616,3617],{},"Accept",[633,3619,3620],{},"Content-Type"," the API doesn't take for that resource. The message lists each request. See ",[618,3623,3625],{"href":3624},"\u002Fdeployment\u002Fbrowser-tests#what-they-check","Browser Tests › What they check",[629,3627,3599,3629],{"id":3628},"the-browser-tests-fail-with-the-api-answered-with-a-server-error-during-the-run",[633,3630,3631],{},"The API answered with a server error during the run",[611,3633,3634,3635,3637,3638,3641,3642,622],{},"Every test passed, but the API sent an error mail, so a request got a ",[633,3636,2584],{}," that the UI didn't show. Find it in the php log the job prints, or keep the stack with ",[633,3639,3640],{},"E2E_KEEP_STACK=true"," and read its log. See ",[618,3643,3645],{"href":3644},"\u002Fdeployment\u002Fbrowser-tests#running-them-locally","Running them locally",[629,3647,3599,3649],{"id":3648},"the-browser-tests-fail-with-not-every-test-ran-and-passed",[633,3650,3651],{},"Not every test ran and passed",[611,3653,3654,3655,734,3658,3661,3662,622],{},"A test was skipped, or ",[633,3656,3657],{},"--grep",[633,3659,3660],{},"test.only"," left one out. Every listed test must run in CI. See ",[618,3663,593],{"href":3624},[629,3665,3667,3668,702,3671,734,3674],{"id":3666},"the-browser-tests-fail-on-pages-in-the-sitemap-a-second-page-in-the-sitemap-or-ogimage","The browser tests fail on ",[633,3669,3670],{},"pages in the sitemap",[633,3672,3673],{},"a second page in the sitemap",[633,3675,3676],{},"og:image",[611,3678,3679,3680,3682,3683,3685,3686,3689,3690,622],{},"The template's tests take their pages from the sitemap. The sitemap must list at least two pages, and the first one must have an ",[633,3681,3676],{}," tag. Run the tests with ",[633,3684,3640],{},", read the sitemap with ",[633,3687,3688],{},"docker exec \u003Cstack>-php-1 curl -sk https:\u002F\u002Flocalhost\u002Fsitemap.xml",", and make sure your fixtures publish the pages it should list. See ",[618,3691,3625],{"href":3624},[629,3693,3599,3695],{"id":3694},"the-browser-tests-fail-with-cache-checks-failed",[633,3696,3697],{},"cache check(s) failed",[611,3699,3700,3701,3704,3705,3708],{},"Before the browser tests, ",[633,3702,3703],{},"e2e\u002Fcache-checks.sh"," checks the cache on the production configuration, and the line starting ",[633,3706,3707],{},"FAILED:"," says which check and what it got:",[660,3710,3711,3743,3754,3764],{},[663,3712,3713,3731,3732,3735,3736,3738,3739,3742],{},[666,3714,3715,734,3718,3721,3722,673,3725,3721,3728,1107],{},[633,3716,3717],{},"first view",[633,3719,3720],{},"after purge-rendered-html"," wanted ",[633,3723,3724],{},"200 stored",[633,3726,3727],{},"second view",[633,3729,3730],{},"200 hit"," that sitemap page isn't cached for a visitor. The ",[633,3733,3734],{},"got"," part is the status and the page's ",[633,3737,2576],{}," header, or ",[633,3740,3741],{},"no-cache-status",". Every page the sitemap lists must be cacheable.",[663,3744,3745,3750,3751,3753],{},[666,3746,3747,1107],{},[633,3748,3749],{},"no Surrogate-Key on \u003Cfirst page> that \u003Csecond page> doesn't also have"," the check purges a tag only the first sitemap page has, and found none. Each page has its own route tag, so this means the first page's response carried no ",[633,3752,2839],{}," of its own: check that it's a CWA page served from a route.",[663,3755,3756,3763],{},[666,3757,3758,3721,3761,1107],{},[633,3759,3760],{},"the other page stays cached",[633,3762,3730],{}," purging one page's tag also dropped the other page.",[663,3765,3766,3774],{},[666,3767,3768,734,3771,1107],{},[633,3769,3770],{},"over HTTPS",[633,3772,3773],{},"http:\u002F\u002Fphp.local…"," the API didn't answer on that route. Check the php log the job prints.",[611,3776,3777,3778,3781,3782,622],{},"Port 2019 has its own message: ",[633,3779,3780],{},"Caddy's admin API (port 2019) answered from the app container."," means Caddy's admin API isn't bound to php's loopback only. See ",[618,3783,3785],{"href":3784},"\u002Fdeployment\u002Fbrowser-tests#the-test-stack","Browser Tests › The test stack",[629,3787,3789],{"id":3788},"the-browser-tests-cant-start-the-images-on-an-arm-mac","The browser tests can't start the images on an ARM Mac",[611,3791,3792,3793,3796,3797,622],{},"Images from your CI registry are amd64 only. Set ",[633,3794,3795],{},"DOCKER_DEFAULT_PLATFORM=linux\u002Famd64",", or build the images locally. See ",[618,3798,3645],{"href":3644},[624,3800,573],{"id":3801},"cloudflare",[629,3803,3805,3806,3809],{"id":3804},"cloudflare-returns-526-for-a-new-hostname-or-after-a-staging-deploy","Cloudflare returns ",[633,3807,3808],{},"526"," for a new hostname or after a staging deploy",[611,3811,3812,3813,936,3817,622],{},"The origin's certificate isn't valid yet. See ",[618,3814,3816],{"href":3815},"\u002Fdeployment\u002Fcloudflare#dns-and-tls","Cloudflare › DNS and TLS",[618,3818,3820],{"href":3819},"\u002Fdeployment\u002Fcloudflare#each-deploy-track","Each deploy track",[629,3822,3824],{"id":3823},"a-certificate-can-never-be-issued-or-renewed-behind-cloudflare","A certificate can never be issued or renewed behind Cloudflare",[611,3826,3827,3830,3831,622],{},[666,3828,3829],{},"Always Use HTTPS"," redirects the HTTP-01 challenge. Turn it off. See ",[618,3832,3820],{"href":3819},[629,3834,3836],{"id":3835},"an-edit-doesnt-show-at-the-edge","An edit doesn't show at the edge",[611,3838,3839,3840,3844],{},"See ",[618,3841,3843],{"href":3842},"\u002Fdeployment\u002Fcloudflare#checking-purges","Cloudflare › Checking purges"," for the log messages and how to check that purges arrive.",[629,3846,3848,3849,3852],{"id":3847},"pages-stay-dynamic-under-the-cache-rule","Pages stay ",[633,3850,3851],{},"DYNAMIC"," under the Cache Rule",[611,3854,3855,3856,950,3859,622],{},"Cloudflare doesn't cache a response that carries ",[633,3857,3858],{},"Set-Cookie",[618,3860,3862],{"href":3861},"\u002Fdeployment\u002Fcloudflare#the-cache-rules-must-match-the-templates-exclusions","The cache rules must match the template's exclusions",[629,3864,3866],{"id":3865},"pages-built-from-page-data-all-show-one-pages-content-after-client-side-navigation","Pages built from page data all show one page's content after client-side navigation",[611,3868,3869,3870,622],{},"The API rule is missing the component positions exclusion. See ",[618,3871,3873],{"href":3872},"\u002Fdeployment\u002Fcloudflare#api-responses-optional","Cloudflare › API responses",[629,3875,3877,3878,3881],{"id":3876},"browsing-quickly-gets-a-429-from-cloudflare","Browsing quickly gets a ",[633,3879,3880],{},"429"," from Cloudflare",[611,3883,3884,3885,622],{},"A rate limiting rule is set too low. See ",[618,3886,3888],{"href":3887},"\u002Fdeployment\u002Fcloudflare#free-protection-worth-turning-on","Free protection worth turning on",[624,3890,621],{"id":3891},"elsewhere-in-the-docs",[660,3893,3894,3899,3906,3913,3919,3926],{},[663,3895,3896,3898],{},[618,3897,2649],{"href":2648},": live updates don't arrive.",[663,3900,3901,3905],{},[618,3902,3904],{"href":3903},"\u002Fapi\u002Fdebugging#error-status-codes","Debugging & Errors › Error Status Codes",": what each API error status means.",[663,3907,3908,3912],{},[618,3909,3911],{"href":3910},"\u002Fnuxt-module\u002Fcwa-layer\u002Fdate-pickers#troubleshooting-the-picker-shows-no-time","Date Pickers › Troubleshooting",": the picker shows no time.",[663,3914,3915,3918],{},[618,3916,3917],{"href":515},"Styling & CSS Isolation",": your global styles leak into the admin UI.",[663,3920,3921,3925],{},[618,3922,3924],{"href":3923},"\u002Fdeployment\u002Fload-testing#traps","Load Testing › Traps",": a load test reports failures against a healthy server.",[663,3927,3928,3930],{},[618,3929,20],{"href":21},": many errors above come from copying one part of a release without the rest, so follow each release's upgrade notes in full.",[3932,3933,3934],"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 .szhYu, html code.shiki .szhYu{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#C3E88D}html pre.shiki code .seSrl, html code.shiki .seSrl{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#89DDFF}html pre.shiki code .sOvfz, html code.shiki .sOvfz{--shiki-light:#24292E;--shiki-default:#E1E4E8;--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 .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 .sm4w6, html code.shiki .sm4w6{--shiki-light:#D73A49;--shiki-light-font-style:inherit;--shiki-default:#F97583;--shiki-default-font-style:inherit;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sKpYG, html code.shiki .sKpYG{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#82AAFF}html pre.shiki code .sDHlG, html code.shiki .sDHlG{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#F07178}html pre.shiki code .swWMF, html code.shiki .swWMF{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#FF9CAC}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}",{"title":826,"searchDepth":1075,"depth":1075,"links":3936},[3937,3965,3985,4001,4008,4012,4015,4049,4062,4073],{"id":626,"depth":1075,"text":627,"children":3938},[3939,3941,3943,3945,3947,3949,3951,3952,3954,3956,3957,3959,3961,3963,3964],{"id":631,"depth":1309,"text":3940},"create-cwa says a port is in use",{"id":745,"depth":1309,"text":3942},"create-cwa says docker info failed",{"id":775,"depth":1309,"text":3944},"create-cwa warns that Docker has too little memory",{"id":798,"depth":1309,"text":3946},"bin\u002Fup fails at \"Starting the stack\"",{"id":913,"depth":1309,"text":3948},"bin\u002Fup fails at \"Loading fixtures\"",{"id":958,"depth":1309,"text":3950},"bin\u002Fup warns that the first visit may be slow",{"id":994,"depth":1309,"text":854},{"id":1169,"depth":1309,"text":3953},"The database container exits because its data is from an older PostgreSQL",{"id":1418,"depth":1309,"text":3955},"Every page returns 502 or 503 after you run pnpm install on the host",{"id":1488,"depth":1309,"text":1489},{"id":1582,"depth":1309,"text":3958},"composer update fails with a raw.githubusercontent.com 404",{"id":1664,"depth":1309,"text":3960},"Composer or php reports a missing ffi extension",{"id":1719,"depth":1309,"text":3962},"Caddy reports unexpected EOF after you edit the Caddyfile",{"id":1805,"depth":1309,"text":1806},{"id":1911,"depth":1309,"text":1912},{"id":1926,"depth":1075,"text":1927,"children":3966},[3967,3969,3971,3973,3975,3977,3979,3981,3983],{"id":1930,"depth":1309,"text":3968},"docker compose stops with required variable … is missing a value",{"id":1974,"depth":1309,"text":3970},"php won't start: SITE_HOST and SITE_ALIASES must be host names without a scheme or port",{"id":2025,"depth":1309,"text":3972},"bin\u002Fserver\u002Finit says .env.prod already exists",{"id":2048,"depth":1309,"text":3974},"A script says No .env.prod: run bin\u002Fserver\u002Finit first.",{"id":2083,"depth":1309,"text":3976},"deploy.sh stops with ❌ The migrations plan failed: nothing was deployed.",{"id":2098,"depth":1309,"text":3978},"A backup fails with ❌ The dump doesn't read back (pg_restore --list): no backup written.",{"id":2129,"depth":1309,"text":3980},"deploy.sh ends with ❌ Deployed, but image variants failed",{"id":2182,"depth":1309,"text":3982},"deploy.sh warns the page warm failed",{"id":2198,"depth":1309,"text":3984},"The site stays in maintenance mode after restore.sh fails",{"id":2250,"depth":1075,"text":2251,"children":3986},[3987,3989,3990,3992,3993,3994,3996,3998,3999],{"id":2296,"depth":1309,"text":3988},"php won't start with unrecognized directive after you copy the template's Caddyfile",{"id":2354,"depth":1309,"text":2355},{"id":2386,"depth":1309,"text":3991},"php keeps restarting with token recognition error at: '#'",{"id":2448,"depth":1309,"text":2449},{"id":2492,"depth":1309,"text":2493},{"id":2520,"depth":1309,"text":3995},"The first visitor after a flush or a rollout gets a 504",{"id":2553,"depth":1309,"text":3997},"Requests that waited on a failed render get an empty 200",{"id":2604,"depth":1309,"text":2605},{"id":2618,"depth":1309,"text":4000},"PURGE or BAN sent to the site returns 405",{"id":2361,"depth":1075,"text":2642,"children":4002},[4003,4005,4007],{"id":2652,"depth":1309,"text":4004},"php won't start with a Mercure issuer or compatibility-mode error",{"id":2734,"depth":1309,"text":4006},"php won't start with only one unnamed Mercure hub is allowed per configuration",{"id":2765,"depth":1309,"text":2766},{"id":2825,"depth":1075,"text":2826,"children":4009},[4010],{"id":2829,"depth":1309,"text":4011},"The sitemap is empty, or the Nuxt log shows UND_ERR_HEADERS_OVERFLOW",{"id":650,"depth":1075,"text":2857,"children":4013},[4014],{"id":2860,"depth":1309,"text":2861},{"id":2871,"depth":1075,"text":2872,"children":4016},[4017,4019,4021,4022,4024,4026,4028,4030,4031,4033,4035,4037,4038,4040,4041,4042,4044,4046,4048],{"id":2875,"depth":1309,"text":4018},"The php image build hangs on apt-get, install-php-extensions or the Go build",{"id":2921,"depth":1309,"text":4020},"The review job is orange and says No namespace '\u003Cname>'",{"id":2935,"depth":1309,"text":2936},{"id":3089,"depth":1309,"text":4023},"The deploy stops with MIGRATIONS PLAN FAILED, DATABASE BACKUP FAILED or MIGRATIONS FAILED",{"id":3151,"depth":1309,"text":4025},"A review deploy stops with REVIEW APP NOT DEPLOYED",{"id":3197,"depth":1309,"text":4027},"A scaled deploy prints THE API CACHE IS NOT SHARED IN REDIS",{"id":3228,"depth":1309,"text":4029},"A scaled deploy warns up to … database connections",{"id":3258,"depth":1309,"text":3259},{"id":3286,"depth":1309,"text":4032},"Every deploy job fails on its first line, or stop review can't load k8s.sh",{"id":3326,"depth":1309,"text":4034},"The deploy fails with No digest for …, or warns Couldn't tag … as production",{"id":3374,"depth":1309,"text":4036},"The deploy fails with THE cwa ROLLOUT DID NOT FINISH",{"id":3402,"depth":1309,"text":3403},{"id":3425,"depth":1309,"text":4039},"var\u002Flog\u002Fprod.log is missing, or the API's log is empty",{"id":3442,"depth":1309,"text":3443},{"id":3467,"depth":1309,"text":3468},{"id":3516,"depth":1309,"text":4043},"Visitors get 502 while a deploy rolls out",{"id":3534,"depth":1309,"text":4045},"The image variants and warm job ends with ran out of memory (exit 137)",{"id":3556,"depth":1309,"text":4047},"Visitors get 503 under load",{"id":3571,"depth":1309,"text":3572},{"id":3594,"depth":1075,"text":3595,"children":4050},[4051,4053,4055,4057,4059,4061],{"id":3598,"depth":1309,"text":4052},"The browser tests fail with API writes the API refused as a mismatched request",{"id":3628,"depth":1309,"text":4054},"The browser tests fail with The API answered with a server error during the run",{"id":3648,"depth":1309,"text":4056},"The browser tests fail with Not every test ran and passed",{"id":3666,"depth":1309,"text":4058},"The browser tests fail on pages in the sitemap, a second page in the sitemap or og:image",{"id":3694,"depth":1309,"text":4060},"The browser tests fail with cache check(s) failed",{"id":3788,"depth":1309,"text":3789},{"id":3801,"depth":1075,"text":573,"children":4063},[4064,4066,4067,4068,4070,4071],{"id":3804,"depth":1309,"text":4065},"Cloudflare returns 526 for a new hostname or after a staging deploy",{"id":3823,"depth":1309,"text":3824},{"id":3835,"depth":1309,"text":3836},{"id":3847,"depth":1309,"text":4069},"Pages stay DYNAMIC under the Cache Rule",{"id":3865,"depth":1309,"text":3866},{"id":3876,"depth":1309,"text":4072},"Browsing quickly gets a 429 from Cloudflare",{"id":3891,"depth":1075,"text":621},"Find a problem by its symptom or error message — local dev, single-server production, Caddy and Souin, Mercure, Nuxt, the database, Kubernetes deploys and Cloudflare — with a fix for each and a link to the full explanation.","md",{},{"title":601,"description":4074},"dJEXGgglKenzVQQ0s8RYLiFO_cLMUzycp6sj822x-78",[4080,606],{"title":597,"path":598,"stem":599,"description":4081,"children":-1},"When a project's functional test suite slows CI down — measure where the time goes, reset the database without rebuilding the schema, and shard the job by test.",1793565690146]