[{"data":1,"prerenderedAt":1990},["ShallowReactive",2],{"page:\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds":3,"all-docs-nav":1747},{"id":4,"title":5,"body":6,"breadcrumb":1720,"dateModified":1730,"datePublished":1730,"description":1731,"extension":1732,"faq":1733,"meta":1741,"navigation":520,"path":1742,"seo":1743,"slug":12,"stem":1744,"type":1745,"__hash__":1746},"content\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds\u002Findex.md","Scheduling Content Publication With Cron-Triggered Builds",{"type":7,"value":8,"toc":1702},"minimark",[9,13,17,26,31,54,58,236,239,332,415,432,443,447,745,760,764,767,1191,1194,1271,1275,1287,1307,1310,1391,1395,1398,1471,1554,1557,1561,1607,1611,1619,1623,1628,1631,1635,1638,1642,1645,1649,1652,1656,1659,1663,1698],[10,11,5],"h1",{"id":12},"scheduling-content-publication-with-cron-triggered-builds",[14,15,16],"p",{},"A static site has no runtime, so there is nothing to check the clock and reveal a page at nine o'clock. That is usually presented as a limitation; it is actually a useful property, because it means an unpublished page is not merely hidden — it does not exist in the deployed artifact at all.",[14,18,19,20,25],{},"The pattern has two halves: exclude future-dated content at build time, and rebuild on a schedule so pages appear when their moment arrives. This guide covers both, plus the details that decide whether it works reliably — timezones, no-op deploys, and keeping genuinely embargoed content out of the repository's public history. It is part of ",[21,22,24],"a",{"href":23},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002F","Content Workflows for Documentation Teams",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,38,46],"ul",{},[35,36,37],"li",{},"Content with a reliable date field in front matter, including a timezone offset.",[35,39,40,41,45],{},"A CI system that supports scheduled workflows (GitHub Actions ",[42,43,44],"code",{},"schedule",", GitLab pipeline schedules, or a host-side cron calling a deploy hook).",[35,47,48,49,53],{},"A deploy that is cheap enough to run on a schedule — see ",[21,50,52],{"href":51},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002F","Incremental Builds and Build Caching for SSGs"," if a full build is expensive.",[27,55,57],{"id":56},"filter-at-build-time","Filter at Build Time",[59,60,61,232],"figure",{},[62,63,70,71,70,75,70,79,70,86,70,217],"svg",{"viewBox":64,"role":65,"ariaLabelledBy":66,"xmlns":69},"0 0 760 300","img",[67,68],"sch-filter-title","sch-filter-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[72,73,74],"title",{"id":67},"Where future-dated content is excluded",[76,77,78],"desc",{"id":68},"A pipeline showing three content items entering the build: two with past dates and one with a future date. The build filter excludes the future-dated item, so the deployed artifact contains only two pages. A later scheduled rebuild includes all three once the date has passed.",[80,81],"rect",{"x":82,"y":82,"width":83,"height":84,"fill":85},"0","760","300","#ffffff",[87,88,90,91,90,99,90,110,90,116,90,119,90,123,90,129,90,133,90,141,90,147,90,153,90,160,90,165,90,170,90,174,90,178,90,183,90,188,90,192,70],"g",{"style":89},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[92,93,98],"text",{"x":94,"y":95,"fill":96,"style":97},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Excluded from the artifact, not hidden in it",[80,100],{"x":101,"y":102,"width":103,"height":104,"rx":105,"fill":106,"opacity":107,"stroke":108,"style":109},"40","56","160","36","6","#8ac926","0.18","#5a8a16","stroke-width:1.5px",[92,111,115],{"x":112,"y":113,"fill":96,"style":114},"120","79","font-size:12px;text-anchor:middle","guide-a · 12 Jul",[80,117],{"x":101,"y":118,"width":103,"height":104,"rx":105,"fill":106,"opacity":107,"stroke":108,"style":109},"100",[92,120,122],{"x":112,"y":121,"fill":96,"style":114},"123","guide-b · 28 Jul",[80,124],{"x":101,"y":125,"width":103,"height":104,"rx":105,"fill":126,"opacity":127,"stroke":128,"style":109},"144","#ffca3a","0.3","#a97b00",[92,130,132],{"x":112,"y":131,"fill":96,"style":114},"167","guide-c · 8 Aug",[80,134],{"x":135,"y":136,"width":137,"height":102,"rx":138,"fill":139,"opacity":140,"stroke":139,"style":109},"250","90","150","10","#6a4c93","0.14",[92,142,146],{"x":143,"y":144,"fill":96,"style":145},"325","114","font-size:12px;font-weight:700;text-anchor:middle","build filter",[92,148,152],{"x":143,"y":149,"fill":150,"style":151},"132","#556071","font-size:11px;text-anchor:middle","date \u003C= now",[80,154],{"x":155,"y":156,"width":157,"height":158,"rx":138,"fill":106,"opacity":159,"stroke":108,"style":109},"450","70","270","46","0.16",[92,161,164],{"x":162,"y":163,"fill":96,"style":145},"585","98","deployed: guide-a, guide-b",[80,166],{"x":155,"y":167,"width":157,"height":158,"rx":138,"fill":168,"opacity":169,"stroke":150,"style":109},"130","#d9e2ef","0.5",[92,171,173],{"x":162,"y":172,"fill":96,"style":114},"152","guide-c: no HTML, no sitemap entry",[92,175,177],{"x":162,"y":176,"fill":150,"style":151},"168","nothing to discover",[80,179],{"x":135,"y":180,"width":181,"height":158,"rx":138,"fill":182,"opacity":140,"stroke":182,"style":109},"212","470","#1982c4",[92,184,187],{"x":185,"y":186,"fill":96,"style":145},"485","234","Scheduled rebuild on 8 Aug → guide-c is now eligible and ships",[92,189,191],{"x":185,"y":190,"fill":150,"style":151},"252","no runtime check, no CMS involvement, no manual step",[87,193,196,197,196,202,196,205,196,208,196,211,196,214,90],{"stroke":150,"fill":194,"style":195},"none","stroke-width:2px","\n      ",[198,199],"path",{"d":200,"style":201},"M202 74 L248 106","marker-end:url(#sch-arrow)",[198,203],{"d":204,"style":201},"M202 118 L248 118",[198,206],{"d":207,"style":201},"M202 162 L248 130",[198,209],{"d":210,"style":201},"M402 106 L448 93",[198,212],{"d":213,"style":201},"M402 130 L448 150",[198,215],{"d":216,"style":201},"M325 148 L325 210",[218,219,90,220,70],"defs",{},[221,222,196,229,90],"marker",{"id":223,"viewBox":224,"refX":225,"refY":226,"markerWidth":227,"markerHeight":227,"orient":228},"sch-arrow","0 0 10 10","8","5","7","auto-start-reverse",[198,230],{"d":231,"fill":150},"M0 0 L10 5 L0 10 z",[233,234,235],"figcaption",{},"Because the page is never rendered, there is no HTML to find, no sitemap entry to crawl and no payload to inspect — the strongest form of \"not published yet\" a website can offer.",[14,237,238],{},"Every generator expresses the filter differently, but they all do it in one place:",[240,241,246],"pre",{"className":242,"code":243,"language":244,"meta":245,"style":245},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Astro — content collection filter\nconst guides = await getCollection('guides', ({ data }) =>\n  !data.draft && data.date.getTime() \u003C= Date.now());\n","javascript","",[42,247,248,257,299],{"__ignoreMap":245},[249,250,253],"span",{"class":251,"line":252},"line",1,[249,254,256],{"class":255},"sJ8bj","\u002F\u002F Astro — content collection filter\n",[249,258,260,264,268,271,274,278,282,286,289,293,296],{"class":251,"line":259},2,[249,261,263],{"class":262},"szBVR","const",[249,265,267],{"class":266},"sj4cs"," guides",[249,269,270],{"class":262}," =",[249,272,273],{"class":262}," await",[249,275,277],{"class":276},"sScJk"," getCollection",[249,279,281],{"class":280},"sVt8B","(",[249,283,285],{"class":284},"sZZnC","'guides'",[249,287,288],{"class":280},", ({ ",[249,290,292],{"class":291},"s4XuR","data",[249,294,295],{"class":280}," }) ",[249,297,298],{"class":262},"=>\n",[249,300,302,305,308,311,314,317,320,323,326,329],{"class":251,"line":301},3,[249,303,304],{"class":262},"  !",[249,306,307],{"class":280},"data.draft ",[249,309,310],{"class":262},"&&",[249,312,313],{"class":280}," data.date.",[249,315,316],{"class":276},"getTime",[249,318,319],{"class":280},"() ",[249,321,322],{"class":262},"\u003C=",[249,324,325],{"class":280}," Date.",[249,327,328],{"class":276},"now",[249,330,331],{"class":280},"());\n",[240,333,335],{"className":242,"code":334,"language":244,"meta":245,"style":245},"\u002F\u002F Eleventy — collection filter in eleventy.config.js\neleventyConfig.addCollection('guides', (api) =>\n  api.getFilteredByGlob('content\u002Fguides\u002F**\u002F*.md')\n     .filter((item) => item.date \u003C= new Date()));\n",[42,336,337,342,365,381],{"__ignoreMap":245},[249,338,339],{"class":251,"line":252},[249,340,341],{"class":255},"\u002F\u002F Eleventy — collection filter in eleventy.config.js\n",[249,343,344,347,350,352,354,357,360,363],{"class":251,"line":259},[249,345,346],{"class":280},"eleventyConfig.",[249,348,349],{"class":276},"addCollection",[249,351,281],{"class":280},[249,353,285],{"class":284},[249,355,356],{"class":280},", (",[249,358,359],{"class":291},"api",[249,361,362],{"class":280},") ",[249,364,298],{"class":262},[249,366,367,370,373,375,378],{"class":251,"line":301},[249,368,369],{"class":280},"  api.",[249,371,372],{"class":276},"getFilteredByGlob",[249,374,281],{"class":280},[249,376,377],{"class":284},"'content\u002Fguides\u002F**\u002F*.md'",[249,379,380],{"class":280},")\n",[249,382,384,387,390,393,396,398,401,404,406,409,412],{"class":251,"line":383},4,[249,385,386],{"class":280},"     .",[249,388,389],{"class":276},"filter",[249,391,392],{"class":280},"((",[249,394,395],{"class":291},"item",[249,397,362],{"class":280},[249,399,400],{"class":262},"=>",[249,402,403],{"class":280}," item.date ",[249,405,322],{"class":262},[249,407,408],{"class":262}," new",[249,410,411],{"class":276}," Date",[249,413,414],{"class":280},"()));\n",[240,416,420],{"className":417,"code":418,"language":419,"meta":245,"style":245},"language-go-html-template shiki shiki-themes github-light github-dark","{{\u002F* Hugo — buildFuture is false by default, so this is automatic *\u002F}}\n{{ range where .Site.RegularPages \"Section\" \"guides\" }}\n","go-html-template",[42,421,422,427],{"__ignoreMap":245},[249,423,424],{"class":251,"line":252},[249,425,426],{},"{{\u002F* Hugo — buildFuture is false by default, so this is automatic *\u002F}}\n",[249,428,429],{"class":251,"line":259},[249,430,431],{},"{{ range where .Site.RegularPages \"Section\" \"guides\" }}\n",[14,433,434,435,438,439,442],{},"Hugo is the odd one out in a good way: it excludes future-dated content by default (",[42,436,437],{},"buildFuture: false","), so scheduling works with no code at all. Check the setting explicitly rather than assuming, because a ",[42,440,441],{},"--buildFuture"," flag left in a script silently publishes everything.",[27,444,446],{"id":445},"rebuild-on-a-schedule","Rebuild on a Schedule",[240,448,452],{"className":449,"code":450,"language":451,"meta":245,"style":245},"language-yaml shiki shiki-themes github-light github-dark","# .github\u002Fworkflows\u002Fscheduled-publish.yml\nname: Scheduled publish\non:\n  schedule:\n    - cron: '5 * * * *'        # hourly at :05 — avoid :00, when runners are busiest\n  workflow_dispatch:            # manual trigger for \"publish it now\"\n\nconcurrency:\n  group: publish\n  cancel-in-progress: false     # never cancel a deploy mid-flight\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-node@v4\n        with: { node-version: 20, cache: npm }\n      - run: npm ci\n\n      - name: Anything newly eligible?\n        id: check\n        run: node scripts\u002Fpending-publish.mjs   # exits 1 when there is nothing to do\n\n      - name: Build and deploy\n        if: steps.check.outcome == 'success'\n        run: npm run build && npx wrangler deploy\n","yaml",[42,453,454,459,471,479,486,503,515,522,530,541,555,560,568,576,587,595,609,621,652,665,670,682,693,707,712,724,735],{"__ignoreMap":245},[249,455,456],{"class":251,"line":252},[249,457,458],{"class":255},"# .github\u002Fworkflows\u002Fscheduled-publish.yml\n",[249,460,461,465,468],{"class":251,"line":259},[249,462,464],{"class":463},"s9eBZ","name",[249,466,467],{"class":280},": ",[249,469,470],{"class":284},"Scheduled publish\n",[249,472,473,476],{"class":251,"line":301},[249,474,475],{"class":266},"on",[249,477,478],{"class":280},":\n",[249,480,481,484],{"class":251,"line":383},[249,482,483],{"class":463},"  schedule",[249,485,478],{"class":280},[249,487,489,492,495,497,500],{"class":251,"line":488},5,[249,490,491],{"class":280},"    - ",[249,493,494],{"class":463},"cron",[249,496,467],{"class":280},[249,498,499],{"class":284},"'5 * * * *'",[249,501,502],{"class":255},"        # hourly at :05 — avoid :00, when runners are busiest\n",[249,504,506,509,512],{"class":251,"line":505},6,[249,507,508],{"class":463},"  workflow_dispatch",[249,510,511],{"class":280},":            ",[249,513,514],{"class":255},"# manual trigger for \"publish it now\"\n",[249,516,518],{"class":251,"line":517},7,[249,519,521],{"emptyLinePlaceholder":520},true,"\n",[249,523,525,528],{"class":251,"line":524},8,[249,526,527],{"class":463},"concurrency",[249,529,478],{"class":280},[249,531,533,536,538],{"class":251,"line":532},9,[249,534,535],{"class":463},"  group",[249,537,467],{"class":280},[249,539,540],{"class":284},"publish\n",[249,542,544,547,549,552],{"class":251,"line":543},10,[249,545,546],{"class":463},"  cancel-in-progress",[249,548,467],{"class":280},[249,550,551],{"class":266},"false",[249,553,554],{"class":255},"     # never cancel a deploy mid-flight\n",[249,556,558],{"class":251,"line":557},11,[249,559,521],{"emptyLinePlaceholder":520},[249,561,563,566],{"class":251,"line":562},12,[249,564,565],{"class":463},"jobs",[249,567,478],{"class":280},[249,569,571,574],{"class":251,"line":570},13,[249,572,573],{"class":463},"  publish",[249,575,478],{"class":280},[249,577,579,582,584],{"class":251,"line":578},14,[249,580,581],{"class":463},"    runs-on",[249,583,467],{"class":280},[249,585,586],{"class":284},"ubuntu-latest\n",[249,588,590,593],{"class":251,"line":589},15,[249,591,592],{"class":463},"    steps",[249,594,478],{"class":280},[249,596,598,601,604,606],{"class":251,"line":597},16,[249,599,600],{"class":280},"      - ",[249,602,603],{"class":463},"uses",[249,605,467],{"class":280},[249,607,608],{"class":284},"actions\u002Fcheckout@v4\n",[249,610,612,614,616,618],{"class":251,"line":611},17,[249,613,600],{"class":280},[249,615,603],{"class":463},[249,617,467],{"class":280},[249,619,620],{"class":284},"actions\u002Fsetup-node@v4\n",[249,622,624,627,630,633,635,638,641,644,646,649],{"class":251,"line":623},18,[249,625,626],{"class":463},"        with",[249,628,629],{"class":280},": { ",[249,631,632],{"class":463},"node-version",[249,634,467],{"class":280},[249,636,637],{"class":266},"20",[249,639,640],{"class":280},", ",[249,642,643],{"class":463},"cache",[249,645,467],{"class":280},[249,647,648],{"class":284},"npm",[249,650,651],{"class":280}," }\n",[249,653,655,657,660,662],{"class":251,"line":654},19,[249,656,600],{"class":280},[249,658,659],{"class":463},"run",[249,661,467],{"class":280},[249,663,664],{"class":284},"npm ci\n",[249,666,668],{"class":251,"line":667},20,[249,669,521],{"emptyLinePlaceholder":520},[249,671,673,675,677,679],{"class":251,"line":672},21,[249,674,600],{"class":280},[249,676,464],{"class":463},[249,678,467],{"class":280},[249,680,681],{"class":284},"Anything newly eligible?\n",[249,683,685,688,690],{"class":251,"line":684},22,[249,686,687],{"class":463},"        id",[249,689,467],{"class":280},[249,691,692],{"class":284},"check\n",[249,694,696,699,701,704],{"class":251,"line":695},23,[249,697,698],{"class":463},"        run",[249,700,467],{"class":280},[249,702,703],{"class":284},"node scripts\u002Fpending-publish.mjs",[249,705,706],{"class":255},"   # exits 1 when there is nothing to do\n",[249,708,710],{"class":251,"line":709},24,[249,711,521],{"emptyLinePlaceholder":520},[249,713,715,717,719,721],{"class":251,"line":714},25,[249,716,600],{"class":280},[249,718,464],{"class":463},[249,720,467],{"class":280},[249,722,723],{"class":284},"Build and deploy\n",[249,725,727,730,732],{"class":251,"line":726},26,[249,728,729],{"class":463},"        if",[249,731,467],{"class":280},[249,733,734],{"class":284},"steps.check.outcome == 'success'\n",[249,736,738,740,742],{"class":251,"line":737},27,[249,739,698],{"class":463},[249,741,467],{"class":280},[249,743,744],{"class":284},"npm run build && npx wrangler deploy\n",[14,746,747,748,751,752,755,756,759],{},"Two details in that file matter more than they look. Scheduling at ",[42,749,750],{},":05"," rather than ",[42,753,754],{},":00"," avoids the moment when every hosted runner in the world is contending for capacity, which on a busy platform can delay a job by several minutes. And ",[42,757,758],{},"cancel-in-progress: false"," prevents the next hour's run from killing a deploy that is halfway through.",[27,761,763],{"id":762},"skip-the-build-when-nothing-changed","Skip the Build When Nothing Changed",[14,765,766],{},"An hourly cron that always builds costs 24 builds a day to publish perhaps two pages a week. A pre-check makes the common case free:",[240,768,770],{"className":242,"code":769,"language":244,"meta":245,"style":245},"\u002F\u002F scripts\u002Fpending-publish.mjs — exit 0 if something is newly eligible, 1 otherwise\nimport { readFile } from 'node:fs\u002Fpromises';\nimport { globSync } from 'node:fs';\nimport matter from 'gray-matter';\n\nconst lastRun = new Date(process.env.LAST_RUN || Date.now() - 3600_000);\nconst now = new Date();\nlet pending = [];\n\nfor (const file of globSync('content\u002F**\u002F*.md')) {\n  const { data } = matter(await readFile(file, 'utf8'));\n  if (!data.date || data.draft) continue;\n  const d = new Date(data.date);\n  if (d > lastRun && d \u003C= now) pending.push({ file, date: d.toISOString() });\n  if (data.expires) {\n    const e = new Date(data.expires);\n    if (e > lastRun && e \u003C= now) pending.push({ file, expires: e.toISOString() });\n  }\n}\n\nif (!pending.length) { console.log('nothing newly eligible'); process.exit(1); }\nconsole.log(`publishing ${pending.length}:`, pending.map((p) => p.file).join(', '));\n",[42,771,772,777,794,808,822,826,863,879,893,897,924,959,983,999,1034,1041,1058,1088,1093,1098,1102,1142],{"__ignoreMap":245},[249,773,774],{"class":251,"line":252},[249,775,776],{"class":255},"\u002F\u002F scripts\u002Fpending-publish.mjs — exit 0 if something is newly eligible, 1 otherwise\n",[249,778,779,782,785,788,791],{"class":251,"line":259},[249,780,781],{"class":262},"import",[249,783,784],{"class":280}," { readFile } ",[249,786,787],{"class":262},"from",[249,789,790],{"class":284}," 'node:fs\u002Fpromises'",[249,792,793],{"class":280},";\n",[249,795,796,798,801,803,806],{"class":251,"line":301},[249,797,781],{"class":262},[249,799,800],{"class":280}," { globSync } ",[249,802,787],{"class":262},[249,804,805],{"class":284}," 'node:fs'",[249,807,793],{"class":280},[249,809,810,812,815,817,820],{"class":251,"line":383},[249,811,781],{"class":262},[249,813,814],{"class":280}," matter ",[249,816,787],{"class":262},[249,818,819],{"class":284}," 'gray-matter'",[249,821,793],{"class":280},[249,823,824],{"class":251,"line":488},[249,825,521],{"emptyLinePlaceholder":520},[249,827,828,830,833,835,837,839,842,845,848,850,852,854,857,860],{"class":251,"line":505},[249,829,263],{"class":262},[249,831,832],{"class":266}," lastRun",[249,834,270],{"class":262},[249,836,408],{"class":262},[249,838,411],{"class":276},[249,840,841],{"class":280},"(process.env.",[249,843,844],{"class":266},"LAST_RUN",[249,846,847],{"class":262}," ||",[249,849,325],{"class":280},[249,851,328],{"class":276},[249,853,319],{"class":280},[249,855,856],{"class":262},"-",[249,858,859],{"class":266}," 3600_000",[249,861,862],{"class":280},");\n",[249,864,865,867,870,872,874,876],{"class":251,"line":517},[249,866,263],{"class":262},[249,868,869],{"class":266}," now",[249,871,270],{"class":262},[249,873,408],{"class":262},[249,875,411],{"class":276},[249,877,878],{"class":280},"();\n",[249,880,881,884,887,890],{"class":251,"line":524},[249,882,883],{"class":262},"let",[249,885,886],{"class":280}," pending ",[249,888,889],{"class":262},"=",[249,891,892],{"class":280}," [];\n",[249,894,895],{"class":251,"line":532},[249,896,521],{"emptyLinePlaceholder":520},[249,898,899,902,905,907,910,913,916,918,921],{"class":251,"line":543},[249,900,901],{"class":262},"for",[249,903,904],{"class":280}," (",[249,906,263],{"class":262},[249,908,909],{"class":266}," file",[249,911,912],{"class":262}," of",[249,914,915],{"class":276}," globSync",[249,917,281],{"class":280},[249,919,920],{"class":284},"'content\u002F**\u002F*.md'",[249,922,923],{"class":280},")) {\n",[249,925,926,929,932,934,937,939,942,944,947,950,953,956],{"class":251,"line":557},[249,927,928],{"class":262},"  const",[249,930,931],{"class":280}," { ",[249,933,292],{"class":266},[249,935,936],{"class":280}," } ",[249,938,889],{"class":262},[249,940,941],{"class":276}," matter",[249,943,281],{"class":280},[249,945,946],{"class":262},"await",[249,948,949],{"class":276}," readFile",[249,951,952],{"class":280},"(file, ",[249,954,955],{"class":284},"'utf8'",[249,957,958],{"class":280},"));\n",[249,960,961,964,966,969,972,975,978,981],{"class":251,"line":562},[249,962,963],{"class":262},"  if",[249,965,904],{"class":280},[249,967,968],{"class":262},"!",[249,970,971],{"class":280},"data.date ",[249,973,974],{"class":262},"||",[249,976,977],{"class":280}," data.draft) ",[249,979,980],{"class":262},"continue",[249,982,793],{"class":280},[249,984,985,987,990,992,994,996],{"class":251,"line":570},[249,986,928],{"class":262},[249,988,989],{"class":266}," d",[249,991,270],{"class":262},[249,993,408],{"class":262},[249,995,411],{"class":276},[249,997,998],{"class":280},"(data.date);\n",[249,1000,1001,1003,1006,1009,1012,1014,1017,1019,1022,1025,1028,1031],{"class":251,"line":578},[249,1002,963],{"class":262},[249,1004,1005],{"class":280}," (d ",[249,1007,1008],{"class":262},">",[249,1010,1011],{"class":280}," lastRun ",[249,1013,310],{"class":262},[249,1015,1016],{"class":280}," d ",[249,1018,322],{"class":262},[249,1020,1021],{"class":280}," now) pending.",[249,1023,1024],{"class":276},"push",[249,1026,1027],{"class":280},"({ file, date: d.",[249,1029,1030],{"class":276},"toISOString",[249,1032,1033],{"class":280},"() });\n",[249,1035,1036,1038],{"class":251,"line":589},[249,1037,963],{"class":262},[249,1039,1040],{"class":280}," (data.expires) {\n",[249,1042,1043,1046,1049,1051,1053,1055],{"class":251,"line":597},[249,1044,1045],{"class":262},"    const",[249,1047,1048],{"class":266}," e",[249,1050,270],{"class":262},[249,1052,408],{"class":262},[249,1054,411],{"class":276},[249,1056,1057],{"class":280},"(data.expires);\n",[249,1059,1060,1063,1066,1068,1070,1072,1075,1077,1079,1081,1084,1086],{"class":251,"line":611},[249,1061,1062],{"class":262},"    if",[249,1064,1065],{"class":280}," (e ",[249,1067,1008],{"class":262},[249,1069,1011],{"class":280},[249,1071,310],{"class":262},[249,1073,1074],{"class":280}," e ",[249,1076,322],{"class":262},[249,1078,1021],{"class":280},[249,1080,1024],{"class":276},[249,1082,1083],{"class":280},"({ file, expires: e.",[249,1085,1030],{"class":276},[249,1087,1033],{"class":280},[249,1089,1090],{"class":251,"line":623},[249,1091,1092],{"class":280},"  }\n",[249,1094,1095],{"class":251,"line":654},[249,1096,1097],{"class":280},"}\n",[249,1099,1100],{"class":251,"line":667},[249,1101,521],{"emptyLinePlaceholder":520},[249,1103,1104,1107,1109,1111,1114,1117,1120,1123,1125,1128,1131,1134,1136,1139],{"class":251,"line":672},[249,1105,1106],{"class":262},"if",[249,1108,904],{"class":280},[249,1110,968],{"class":262},[249,1112,1113],{"class":280},"pending.",[249,1115,1116],{"class":266},"length",[249,1118,1119],{"class":280},") { console.",[249,1121,1122],{"class":276},"log",[249,1124,281],{"class":280},[249,1126,1127],{"class":284},"'nothing newly eligible'",[249,1129,1130],{"class":280},"); process.",[249,1132,1133],{"class":276},"exit",[249,1135,281],{"class":280},[249,1137,1138],{"class":266},"1",[249,1140,1141],{"class":280},"); }\n",[249,1143,1144,1147,1149,1151,1154,1157,1159,1161,1164,1167,1170,1172,1174,1176,1178,1181,1184,1186,1189],{"class":251,"line":684},[249,1145,1146],{"class":280},"console.",[249,1148,1122],{"class":276},[249,1150,281],{"class":280},[249,1152,1153],{"class":284},"`publishing ${",[249,1155,1156],{"class":280},"pending",[249,1158,25],{"class":284},[249,1160,1116],{"class":266},[249,1162,1163],{"class":284},"}:`",[249,1165,1166],{"class":280},", pending.",[249,1168,1169],{"class":276},"map",[249,1171,392],{"class":280},[249,1173,14],{"class":291},[249,1175,362],{"class":280},[249,1177,400],{"class":262},[249,1179,1180],{"class":280}," p.file).",[249,1182,1183],{"class":276},"join",[249,1185,281],{"class":280},[249,1187,1188],{"class":284},"', '",[249,1190,958],{"class":280},[14,1192,1193],{},"The alternative, useful when the build is cheap, is to build unconditionally and compare a hash of the output with what is deployed, skipping the deploy when they match. That also catches the case where a build produces identical output for an unrelated reason — a dependency bump that changes nothing visible.",[1195,1196,1197,1216],"table",{},[1198,1199,1200],"thead",{},[1201,1202,1203,1207,1210,1213],"tr",{},[1204,1205,1206],"th",{},"Approach",[1204,1208,1209],{},"Build minutes\u002Fday",[1204,1211,1212],{},"Publish precision",[1204,1214,1215],{},"Catches unrelated drift",[1217,1218,1219,1234,1246,1258],"tbody",{},[1201,1220,1221,1225,1228,1231],{},[1222,1223,1224],"td",{},"Hourly build, always deploy",[1222,1226,1227],{},"~48 min",[1222,1229,1230],{},"1 hour",[1222,1232,1233],{},"No",[1201,1235,1236,1239,1241,1243],{},[1222,1237,1238],{},"Hourly build, deploy on output change",[1222,1240,1227],{},[1222,1242,1230],{},[1222,1244,1245],{},"Yes",[1201,1247,1248,1251,1254,1256],{},[1222,1249,1250],{},"Hourly pre-check, build only when needed",[1222,1252,1253],{},"~2 min",[1222,1255,1230],{},[1222,1257,1233],{},[1201,1259,1260,1263,1266,1269],{},[1222,1261,1262],{},"5-minute pre-check, build when needed",[1222,1264,1265],{},"~6 min",[1222,1267,1268],{},"7 min",[1222,1270,1233],{},[27,1272,1274],{"id":1273},"get-the-timezone-right","Get the Timezone Right",[14,1276,1277,1278,1281,1282,1286],{},"The most common scheduling bug is a date with no offset. ",[42,1279,1280],{},"date: 2026-08-08"," means midnight in ",[1283,1284,1285],"em",{},"some"," timezone, and hosted CI runs in UTC, so a page intended for 9 a.m. in Berlin publishes at 2 a.m. or not until the next day depending on how the generator parses it.",[240,1288,1290],{"className":449,"code":1289,"language":451,"meta":245,"style":245},"# Unambiguous — publishes at 09:00 Central European Summer Time\ndate: 2026-08-08T09:00:00+02:00\n",[42,1291,1292,1297],{"__ignoreMap":245},[249,1293,1294],{"class":251,"line":252},[249,1295,1296],{"class":255},"# Unambiguous — publishes at 09:00 Central European Summer Time\n",[249,1298,1299,1302,1304],{"class":251,"line":259},[249,1300,1301],{"class":463},"date",[249,1303,467],{"class":280},[249,1305,1306],{"class":266},"2026-08-08T09:00:00+02:00\n",[14,1308,1309],{},"Store the offset, compare in UTC, and display in whatever the reader's locale is. If your CMS writes dates without an offset, fix that in the CMS configuration rather than in the templates — one setting instead of a rule everyone has to remember.",[59,1311,1312,1388],{},[62,1313,70,1318,70,1321,70,1324,70,1327],{"viewBox":1314,"role":65,"ariaLabelledBy":1315,"xmlns":69},"0 0 760 280",[1316,1317],"sch-tz-title","sch-tz-desc",[72,1319,1320],{"id":1316},"The same date field interpreted three ways",[76,1322,1323],{"id":1317},"A date written as 2026-08-08 with no timezone is interpreted as midnight UTC by the build, as 2 a.m. local by an author in Berlin, and as 8 p.m. the previous day by a reader in New York. Writing the offset explicitly makes all three agree on one instant.",[80,1325],{"x":82,"y":82,"width":83,"height":1326,"fill":85},"280",[87,1328,90,1329,90,1332,90,1336,90,1340,90,1347,90,1352,90,1355,90,1359,90,1362,90,1366,90,1369,90,1373,90,1375,90,1379,90,1383,70],{"style":89},[92,1330,1331],{"x":94,"y":95,"fill":96,"style":97},"One field, three different instants",[80,1333],{"x":101,"y":102,"width":84,"height":101,"rx":225,"fill":1334,"opacity":140,"stroke":1335,"style":109},"#ff595e","#d83b41",[92,1337,1280],{"x":1338,"y":1339,"fill":96,"style":145},"190","81",[80,1341],{"x":1342,"y":1343,"width":1344,"height":1345,"rx":105,"fill":168,"opacity":1346,"stroke":150,"style":109},"400","48","320","30","0.55",[92,1348,1351],{"x":1349,"y":1350,"fill":96,"style":151},"560","68","build (UTC): 08 Aug 00:00",[80,1353],{"x":1342,"y":1354,"width":1344,"height":1345,"rx":105,"fill":168,"opacity":1346,"stroke":150,"style":109},"82",[92,1356,1358],{"x":1349,"y":1357,"fill":96,"style":151},"102","author in Berlin expects: 08 Aug 09:00 (+02:00)",[80,1360],{"x":1342,"y":1361,"width":1344,"height":1345,"rx":105,"fill":168,"opacity":1346,"stroke":150,"style":109},"116",[92,1363,1365],{"x":1349,"y":1364,"fill":96,"style":151},"136","reader in New York sees: 07 Aug 20:00",[80,1367],{"x":101,"y":1368,"width":84,"height":101,"rx":225,"fill":106,"opacity":107,"stroke":108,"style":109},"176",[92,1370,1372],{"x":1338,"y":1371,"fill":96,"style":145},"201","date: 2026-08-08T09:00:00+02:00",[80,1374],{"x":1342,"y":1368,"width":1344,"height":101,"rx":225,"fill":106,"opacity":159,"stroke":108,"style":109},[92,1376,1378],{"x":1349,"y":1377,"fill":96,"style":151},"196","one instant: 08 Aug 07:00 UTC",[92,1380,1382],{"x":1349,"y":180,"fill":108,"style":1381},"font-size:11px;font-weight:700;text-anchor:middle","build, author and reader all agree",[92,1384,1387],{"x":101,"y":1385,"fill":150,"style":1386},"254","font-size:11px","Daylight saving makes this worse twice a year — the offset, not the local time, is what to store",[233,1389,1390],{},"The failure is silent: nothing errors, the page simply appears at a time nobody intended, and the person who notices is usually the one who announced it.",[27,1392,1394],{"id":1393},"measured-impact","Measured Impact",[14,1396,1397],{},"A documentation site publishing roughly eight scheduled pages a month, GitHub Actions, two-minute builds:",[1195,1399,1400,1416],{},[1198,1401,1402],{},[1201,1403,1404,1407,1410,1413],{},[1204,1405,1406],{},"Configuration",[1204,1408,1409],{},"Builds\u002Fmonth",[1204,1411,1412],{},"Minutes\u002Fmonth",[1204,1414,1415],{},"Worst-case publish lag",[1217,1417,1418,1431,1445,1457],{},[1201,1419,1420,1423,1426,1428],{},[1222,1421,1422],{},"Manual publish (merge when ready)",[1222,1424,1425],{},"—",[1222,1427,1425],{},[1222,1429,1430],{},"Hours to days, human-dependent",[1201,1432,1433,1436,1439,1442],{},[1222,1434,1435],{},"Hourly cron, always build",[1222,1437,1438],{},"720",[1222,1440,1441],{},"1,440",[1222,1443,1444],{},"62 min",[1201,1446,1447,1450,1453,1455],{},[1222,1448,1449],{},"Hourly cron with pre-check",[1222,1451,1452],{},"738 checks, 9 builds",[1222,1454,1345],{},[1222,1456,1444],{},[1201,1458,1459,1462,1465,1468],{},[1222,1460,1461],{},"15-minute cron with pre-check",[1222,1463,1464],{},"2,952 checks, 9 builds",[1222,1466,1467],{},"58",[1222,1469,1470],{},"17 min",[59,1472,1473,1551],{},[62,1474,70,1478,70,1481,70,1484,70,1486],{"viewBox":1314,"role":65,"ariaLabelledBy":1475,"xmlns":69},[1476,1477],"sch-cost-title","sch-cost-desc",[72,1479,1480],{"id":1476},"Build minutes per month by scheduling strategy",[76,1482,1483],{"id":1477},"A horizontal bar chart on a shared scale. Always building hourly costs 1,440 build minutes a month. An hourly pre-check that builds only when needed costs 30 minutes. A fifteen-minute pre-check costs 58 minutes while cutting worst-case publish lag from 62 minutes to 17.",[80,1485],{"x":82,"y":82,"width":83,"height":1326,"fill":85},[87,1487,90,1488,90,1492,90,1495,90,1500,90,1503,90,1507,90,1512,90,1516,90,1519,90,1525,90,1529,90,1533,90,1537,90,1542,90,1546,70],{"style":89},[92,1489,1491],{"x":94,"y":1490,"fill":96,"style":97},"26","Same publish latency, 98% fewer build minutes",[251,1493],{"x1":157,"y1":1494,"x2":157,"y2":180,"stroke":168,"style":109},"44",[92,1496,1499],{"x":1497,"y":1354,"fill":96,"style":1498},"60","font-size:12px;font-weight:700","Hourly, always build",[92,1501,1502],{"x":1497,"y":163,"fill":150,"style":1386},"lag 62 min",[80,1504],{"x":157,"y":1505,"width":1506,"height":1345,"rx":226,"fill":1334,"opacity":159,"stroke":1335,"style":109},"64","420",[92,1508,1511],{"x":1509,"y":1510,"fill":1335,"style":145},"480","84","1,440 min",[92,1513,1515],{"x":1497,"y":1514,"fill":96,"style":1498},"146","Hourly + pre-check",[92,1517,1502],{"x":1497,"y":1518,"fill":150,"style":1386},"162",[80,1520],{"x":157,"y":1521,"width":1522,"height":1345,"rx":1523,"fill":106,"opacity":1524,"stroke":108,"style":109},"128","9","4","0.34",[92,1526,1528],{"x":84,"y":1527,"fill":108,"style":1498},"148","30 min",[92,1530,1532],{"x":1497,"y":1531,"fill":96,"style":1498},"210","15-min + pre-check",[92,1534,1536],{"x":1497,"y":1535,"fill":150,"style":1386},"226","lag 17 min",[80,1538],{"x":157,"y":1539,"width":1540,"height":1345,"rx":1523,"fill":106,"opacity":1541,"stroke":108,"style":109},"192","17","0.28",[92,1543,1545],{"x":1544,"y":180,"fill":108,"style":1498},"308","58 min",[92,1547,1550],{"x":1548,"y":1549,"fill":150,"style":1386},"243","262","Shared linear scale · 8 scheduled pages a month, 2-minute build, 2-second pre-check",[233,1552,1553],{},"The pre-check is the whole trick: parsing front matter costs seconds, so the expensive step only runs on the few occasions when something is genuinely due.",[14,1555,1556],{},"The pre-check row is the one to adopt: publish latency is unchanged and build minutes drop by 98%, because a check that parses front matter takes about two seconds while a build takes two minutes. The 15-minute variant costs twice as much in checks and quarters the worst-case lag, which is worth it for release-day content and not for anything else.",[27,1558,1560],{"id":1559},"pitfalls-rollback","Pitfalls & Rollback",[32,1562,1563,1570,1576,1582,1591,1601],{},[35,1564,1565,1569],{},[1566,1567,1568],"strong",{},"Dates without an offset."," The single most common cause of content appearing at the wrong hour.",[35,1571,1572,1575],{},[1566,1573,1574],{},"Hiding future content client-side."," It is in the HTML, therefore it is published. Filter at build.",[35,1577,1578,1581],{},[1566,1579,1580],{},"Cancelling in-progress deploys."," A scheduled run that cancels a running deploy can leave a host mid-promotion.",[35,1583,1584,1587,1588,1590],{},[1566,1585,1586],{},"Building on the hour."," Runner contention at ",[42,1589,754],{}," adds minutes of unpredictable delay; schedule a few minutes past.",[35,1592,1593,1596,1597,1600],{},[1566,1594,1595],{},"No manual trigger."," Always keep a ",[42,1598,1599],{},"workflow_dispatch"," so someone can publish immediately without editing a date.",[35,1602,1603,1606],{},[1566,1604,1605],{},"Rollback:"," the schedule is one workflow file. Disabling it stops future publication with no effect on what is already live, and content simply publishes on the next manual deploy instead.",[27,1608,1610],{"id":1609},"conclusion","Conclusion",[14,1612,1613,1614,1618],{},"Scheduled publishing on a static site is a filter plus a cron, and its main virtue is that unpublished content is genuinely absent rather than merely hidden. Store dates with an explicit offset, filter them out of the build, run a cheap pre-check on a schedule and only build when something is newly eligible, and keep a manual trigger for the day someone needs a page out now. Combine it with the debounced webhook from ",[21,1615,1617],{"href":1616},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build\u002F","Wiring a Headless CMS to a Static Build"," and the publishing path is fully automatic.",[27,1620,1622],{"id":1621},"faq","FAQ",[1624,1625,1627],"h3",{"id":1626},"how-precise-can-scheduled-publishing-be-on-a-static-site","How precise can scheduled publishing be on a static site?",[14,1629,1630],{},"As precise as your cron interval plus the build and deploy time. An hourly cron with a two-minute build publishes within about an hour of the target; a five-minute cron publishes within about seven minutes. Precision costs build minutes, so pick the loosest interval the content actually needs.",[1624,1632,1634],{"id":1633},"can-i-hide-future-content-with-javascript-instead","Can I hide future content with JavaScript instead?",[14,1636,1637],{},"No. Anything rendered into the HTML is in the artifact and visible to anyone who reads the source, which defeats an embargo entirely. Future-dated content must be excluded at build time so it never reaches the deployed files.",[1624,1639,1641],{"id":1640},"how-do-i-avoid-deploying-when-nothing-has-changed","How do I avoid deploying when nothing has changed?",[14,1643,1644],{},"Compare a hash of the build output with the previously deployed one, or check whether any content is newly eligible before building at all. The cheap version is a pre-build check that exits early when no page's publish date has passed since the last run.",[1624,1646,1648],{"id":1647},"what-timezone-does-the-schedule-use","What timezone does the schedule use?",[14,1650,1651],{},"Cron on hosted CI runs in UTC, and content dates are often written in local time. Store dates with an explicit offset and compare in UTC; a date with no timezone is the most common cause of content publishing a few hours early or late.",[1624,1653,1655],{"id":1654},"does-this-work-for-un-publishing-too","Does this work for un-publishing too?",[14,1657,1658],{},"Yes, and it is the same mechanism: filter out pages whose expiry date has passed and let the next scheduled build remove them. Add a redirect for the removed URL so the page does not simply start returning 404.",[27,1660,1662],{"id":1661},"related","Related",[32,1664,1665,1674,1679,1686,1691],{},[35,1666,1667,1670,1671,1673],{},[1566,1668,1669],{},"Parent:"," ",[21,1672,24],{"href":23}," — the workflow this automates the end of.",[35,1675,1676,1678],{},[21,1677,1617],{"href":1616}," — where the dates are entered.",[35,1680,1681,1685],{},[21,1682,1684],{"href":1683},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates\u002F","Netlify Build Hooks for Content Updates"," — triggering the same build from outside CI.",[35,1687,1688,1690],{},[21,1689,52],{"href":51}," — making a scheduled build cheap enough to run often.",[35,1692,1693,1697],{},[21,1694,1696],{"href":1695},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration\u002F","Keeping Redirects Working After an SSG Migration"," — what to do with an expired page's URL.",[1699,1700,1701],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":245,"searchDepth":259,"depth":259,"links":1703},[1704,1705,1706,1707,1708,1709,1710,1711,1712,1719],{"id":29,"depth":259,"text":30},{"id":56,"depth":259,"text":57},{"id":445,"depth":259,"text":446},{"id":762,"depth":259,"text":763},{"id":1273,"depth":259,"text":1274},{"id":1393,"depth":259,"text":1394},{"id":1559,"depth":259,"text":1560},{"id":1609,"depth":259,"text":1610},{"id":1621,"depth":259,"text":1622,"children":1713},[1714,1715,1716,1717,1718],{"id":1626,"depth":301,"text":1627},{"id":1633,"depth":301,"text":1634},{"id":1640,"depth":301,"text":1641},{"id":1647,"depth":301,"text":1648},{"id":1654,"depth":301,"text":1655},{"id":1661,"depth":259,"text":1662},[1721,1724,1727,1728],{"name":1722,"item":1723},"Home","\u002F",{"name":1725,"item":1726},"Production-Ready Deployment & CI\u002FCD Workflows","\u002Fproduction-ready-deployment-cicd-workflows\u002F",{"name":24,"item":23},{"name":5,"item":1729},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds\u002F","2026-08-01","Publish future-dated pages on a static site with no runtime: filter at build time, rebuild on a cron, skip no-op deploys, and keep embargoed content out of the artifact.","md",[1734,1735,1736,1737,1738],{"q":1627,"a":1630},{"q":1634,"a":1637},{"q":1641,"a":1644},{"q":1648,"a":1651},{"q":1655,"a":1739},{"Yes, and it is the same mechanism":1740},"filter out pages whose expiry date has passed and let the next scheduled build remove them. Add a redirect for the removed URL so the page does not simply start returning 404.",{},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds",{"title":5,"description":1731},"production-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds\u002Findex","article","Za8vqXuszkhfbRuZTr9qZPBfzkp_87xltT6dHWHLzvA",[1748,1751,1754,1757,1760,1763,1766,1769,1772,1775,1778,1781,1784,1787,1789,1792,1795,1798,1801,1804,1807,1810,1813,1816,1819,1822,1825,1828,1831,1834,1837,1840,1843,1846,1849,1852,1855,1858,1861,1864,1867,1870,1873,1876,1879,1882,1885,1888,1891,1894,1897,1900,1903,1906,1909,1912,1915,1918,1921,1924,1926,1927,1929,1932,1935,1938,1941,1944,1947,1949,1952,1955,1958,1961,1963,1966,1969,1972,1975,1978,1981,1984,1987],{"path":1749,"title":1750},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1752,"title":1753},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1755,"title":1756},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1758,"title":1759},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1761,"title":1762},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1764,"title":1765},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1767,"title":1768},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1770,"title":1771},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1773,"title":1774},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs","Eleventy vs Jekyll for Markdown-Heavy Blogs",{"path":1776,"title":1777},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1779,"title":1780},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy","Replacing Jekyll Plugins When Migrating to Eleventy",{"path":1782,"title":1783},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins","Running Jekyll on GitHub Pages Without Plugins",{"path":1785,"title":1786},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1788,"title":1696},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration",{"path":1790,"title":1791},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo","Migrating a Docs Site From Jekyll to Hugo",{"path":1793,"title":1794},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1796,"title":1797},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators","Porting Shortcodes and Includes Between Generators",{"path":1799,"title":1800},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1802,"title":1803},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1805,"title":1806},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1808,"title":1809},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1811,"title":1812},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1814,"title":1815},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1817,"title":1818},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1820,"title":1821},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1823,"title":1824},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1826,"title":1827},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1829,"title":1830},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1832,"title":1833},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1835,"title":1836},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1838,"title":1839},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts","Eliminating Layout Shift From Web Fonts",{"path":1841,"title":1842},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1844,"title":1845},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1847,"title":1848},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js","Measuring CLS in the Field With web-vitals.js",{"path":1850,"title":1851},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift","Reserving Space for Images and Embeds",{"path":1853,"title":1854},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1856,"title":1857},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":1859,"title":1860},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render","Subsetting Variable Fonts for Faster First Render",{"path":1862,"title":1863},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":1865,"title":1866},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1868,"title":1869},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":1871,"title":1872},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":1874,"title":1875},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1877,"title":1878},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1880,"title":1881},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":1883,"title":1884},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1886,"title":1887},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":1889,"title":1890},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":1892,"title":1893},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1895,"title":1896},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":1898,"title":1899},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":1901,"title":1902},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":1904,"title":1905},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1907,"title":1908},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":1910,"title":1911},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":1913,"title":1914},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1916,"title":1917},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":1919,"title":1920},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1922,"title":1923},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":1925,"title":24},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams",{"path":1742,"title":5},{"path":1928,"title":1617},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build",{"path":1930,"title":1931},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":1933,"title":1934},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":1936,"title":1937},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":1939,"title":1940},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1942,"title":1943},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":1945,"title":1946},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1948,"title":52},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs",{"path":1950,"title":1951},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":1953,"title":1954},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":1956,"title":1957},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1959,"title":1960},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1962,"title":1684},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates",{"path":1964,"title":1965},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":1967,"title":1968},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":1970,"title":1971},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":1973,"title":1974},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1976,"title":1977},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1979,"title":1980},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1982,"title":1983},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1985,"title":1986},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":1988,"title":1989},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",1785611671058]