Every deploy uploads the whole site as one artifact, and GitHub Pages rejects an artifact over 1 GB. The cap is on the compressed upload, not the directory tree — HTML and JS pack around 3×, so a site that is 734 MiB on disk deploys as about 223 MiB.
A docs build is a few megabytes, so this only bites long-lived projects — but it bites
them hard. blueapi, with 131 released
docs.zips, was at 452 MB and adding ~5 MB per release.
What happens when you hit it¶
actions/deploy-pages compares the uploaded artifact against the limit and warns:
Uploaded artifact size of 1103175680 bytes exceeds the allowed size of 1 GB. Deployment might fail.That warning only appears once you are already over, so treat a deploy that emits it as
needing action now. Nothing is lost when it happens: cap the site with the two inputs
below and re-run the deploy — every release still has its docs.zip asset, so raising
the cap again brings the old versions straight back.
Check where you are¶
Every deploy prints the size of the tree it assembled (on disk, so roughly 3× what will actually be uploaded):
assembled site: 734M on disk across 31 version dir(s)
...of which stable/ is a second copy of 4.2.1 (12M)The second line is not a rounding detail: stable/ is a symlink in the assembled tree,
but actions/upload-pages-artifact tars with --dereference, so the newest release is
uploaded twice. Budget for it — the largest release always counts double — and note
that the deploy that first publishes a release is the one that grows the site by 2× that
release, not 1×.
Or measure the released half directly:
gh api --paginate repos/ORG/REPO/releases \
| jq -rs '[.[][].assets[] | select(.name=="docs.zip") | .size] | add / 1048576 | floor'Cap what gets published¶
Two inputs, both set in your publish.yml’s with: block:
jobs:
publish:
uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/publish-gh-pages.yml@<tag>
with:
pr: ${{ inputs.pr }}
max-releases: "30" # ← publish only the 30 most recent releases
max-prs: "20" # ← and previews for only the 20 most recently built PRsBoth default to 0, meaning unlimited, so upgrading the pinned tag never removes
versions from a site that already has them. You opt in.
How the two caps rank¶
max-releasesranks by the tagged commit’s date (created_at), newest first. Nothing parses your version numbers — tags vary far too much between projects for that to be safe, andpublished_atis unreliable because re-publishing an old release stamps it as new.max-prsranks by when each PR’s docs were last built, newest first, so an abandoned PR’s preview drops off before an active one’s.
Both cap only what is eligible: a release with no docs.zip asset, a PR whose build
artifact has expired, and an unapproved fork PR never consume a slot.
Capping only affects what the site serves — nothing is deleted. Dropped versions
leave the switcher and their URLs 404, so pick N so the versions people actually link to
stay in. Note that prereleases occupy slots too: blueapi’s newest 20 releases include 5
prereleases, so max-releases: 20 would publish only 15 real ones.
It also bounds the Actions cache¶
The engine caches release zips so a deploy re-downloads only what changed. That cache
entry is as big as the published release set, and each new release writes a fresh
entry, all of it counting against the repo’s 10 GB Actions cache quota — which is
shared with your build caches, and evicted by LRU when full. At blueapi’s 458 MB of
release zips, an uncapped cache entry would push their other caches out within a few
releases. max-releases: 30 keeps it near 100 MB.
What isn’t capped¶
The default branch — always published; it is the one required version.
The migration seed release (
pages-default-seed) — it stands in for the default branch rather than being a version of its own.The
stable/alias — a full copy of the newest non-prerelease release, as above. Loweringmax-releasesnever removes it; only having no release at all does.
Related¶
choose
-publish -events — how often deploys run architecture — why reconstruct-everything has this cost at all