Astro boilerplateCloudflare

Astro

Pre-rendered and server-rendered Astro pages.

astro-boilerplate

Vercel's Astro boilerplate — a pre-rendered home page, two server-rendered pages (one with stale-while-revalidate cache headers) and an optimised-image page — running on Cloudflare Workers + Static Assets through Astro's own @astrojs/cloudflare adapter, and packaged as an Orun baseline: one command takes a new site from nothing to live on stage and prod, in its own repository, under its own name.

Swapping the Vercel adapter for the Cloudflare one is the whole port; the pages do not change, except that the home page reads the site's name from one module. What the baseline adds is everything needed to make a copy of it somebody else's — built the way cirrus-baseline is. See specs/epics/astro-boilerplate for the design, the milestones and the as-built status.

Instantiating a site

git clone --depth 1 --branch <baseline-tag> https://github.com/sourceplane/astro-boilerplate
cd astro-boilerplate
orun new --blueprint repo-blueprint.yaml --out ~/sites/acme-notes --run-hooks --resume \
  --set reponame=acme-notes --set productname="Acme Notes" \
  --set githuborg=acme-inc --set subdomain=<workers-dev-subdomain>

Or, once the platform's registry lists it, from the Orun console (Baselines → astro-boilerplate → Build). Two phases — 01-product (the repository created, then the whole site in one pull request, deployed to stage and prod and smoke-tested) and 03-docs (the live deployment recorded) — each landing one pull request in the new repository. BOOTSTRAP.md is the operator guide; docs/phases/ has a page per phase.

The new repository receives the site only — the app and its tests, its CI, its tooling, a README of its own and its operating contract — renamed throughout to the site's name, repository and Cloudflare Worker. None of this repository's machinery ships.

What makes it a baselineWhere
The console card (inputs, secrets, programme)blueprint.yaml
The build document (modules, phases, hooks)repo-blueprint.yaml
This tree's identity, and the bootstrap's task contracts.baseline/
The rename enginetooling/rebrand/rebrand.mjs
The bootstrap's helpers (deferrals, redeploy marker, work-tree strip)tooling/bootstrap/
The deployment-record rendererhooks/render-deployment-docs.sh
The site's README and context pack.baseline/product/README.md, ai/context/
The baseline's tests: contract, leak, offline bootstrap, end to endtesting/, .github/workflows/baseline.yml
Cutting a baseline-vN tag.github/workflows/tag.yml

What is here (the site)

SurfaceWhere
The site's name (the home page's heading and title)apps/web/src/site.ts
/ (pre-rendered), /ssr, /ssr-with-swr-caching, /imageapps/web/src/pages
Layout (with the optional analytics beacon) and the link cardapps/web/src/layouts/Layout.astro, apps/web/src/components/Card.astro
Adapter, Worker config, deploy contractapps/web/astro.config.ts, apps/web/wrangler.jsonc (what the adapter builds from), apps/web/wrangler.deploy.jsonc (what deploys), apps/web/component.yaml
Tests (Astro container API)apps/web/src/test

Getting started

pnpm install
pnpm typecheck        # astro check
pnpm test             # vitest over the container API
pnpm build            # astro build → apps/web/dist/server (Worker) + dist/client (assets)
pnpm --filter @app/web dev        # http://localhost:4321
pnpm --filter @app/web preview    # wrangler dev over the built Worker

Optional: PUBLIC_CF_BEACON_TOKEN at build time renders the Cloudflare Web Analytics beacon (stands in for the Vercel adapter's web analytics).

How it maps to Vercel

VercelHere
@astrojs/vercel serverless output@astrojs/cloudflare: Worker under dist/server/, static output under dist/client/
Vercel image optimisationBuild-time optimisation (imageService: 'compile') for pre-rendered pages; the SSR /image page serves the original through the adapter's passthrough endpoint
Web analyticsCloudflare Web Analytics beacon, opt-in by token
s-maxage / stale-while-revalidate honoured by the CDNHeader emitted unchanged; add a Cache Rule for /ssr-with-swr-caching to have Cloudflare cache it
Deploy on pushOrun deploys <repo>-web-{stage,prod} on merge to main (apps/web/component.yaml)
"Deploy with Vercel" (clone the template)orun new --blueprint repo-blueprint.yaml — a new repository, branded, deployed

CI

WorkflowWhat it runsShips to a site
ci.ymlthe Orun pipeline, and the only workflow a site carries: orun plan --changed, then one job per lane. A pull request runs the verify lanes (the site built and dry-deployed per environment — they do not type-check, lint or run the tests; baseline.yml's e2e tier runs those before each tag, and a site's README asks its developers to run them before a push), except the bootstrap's own landings (orun/BASE-*), which are checked in the box before they are pushed (checks: local) and whose PR runs plan alone; a merge deploys stage → prod. It plans nothing while intent.yaml names the placeholder workspace ws_CHANGE_ME, as this repository does. A manual run (Orunbase's Deploy button, or Run workflow on main) plans the environment and components it names under the push-to-main trigger, so they deploy as a merge deploys themyes
baseline.ymlthe baseline's own tiers: contract tests, the blueprint read by the pinned orun, the leak gate, the offline bootstrap, the site built, dry-deployed and servedno
tag.ymlcuts a baseline-vN tag (manual)no