Deploy
How to ship the static site produced by grove build to Vercel, Netlify, Cloudflare Pages, or GitHub Pages.
The Astro adapter produces a fully static site. The build output is in dist/ after pnpm build. There is no server runtime — every host that serves static files works.
This guide covers the five hosts the CLI knows about (vercel, netlify, cloudflare, github-pages, none), plus notes for self-hosting. See DEPLOY_PROVIDERS in packages/cli/src/index.ts for the canonical list. Each --deploy value writes a provider-specific config file (e.g. vercel.json, netlify.toml, wrangler.jsonc) and a matching GitHub Actions workflow (e.g. .github/workflows/deploy-vercel.yml).
The build command
Section titled “The build command”pnpm buildThis runs (in order, from packages/astro/templates/default/package.json):
grove validate— schema-check every recordgrove generate— producedata/generated/records.index.jsonandrecords.full.jsongrove sitemap— writepublic/sitemap.xmlgrove llms— writepublic/llms.txtandpublic/llms-full.txtastro build— render the static site todist/
The site is now a folder of HTML, CSS, JS, and JSON. Any static host can serve it.
Environment variables
Section titled “Environment variables”Two are read at build time:
SITE_URL— the canonical URL the build uses for absolute links insitemap.xml, OpenGraph tags, and JSON-LD. Defaults tohttps://example.comif unset. Set this to your production URL in the host’s build environment.GITHUB_TOKEN— only needed if you rungrove sync githubas part of the build. Most sites don’t do this — sync is a separate scheduled workflow. See Sync GitHub metadata.
That’s it. The build is fully static and does not need any runtime secrets.
Vercel
Section titled “Vercel”Project type: Static (no framework preset needed, but selecting “Astro” works too).
Build command: pnpm build
Output directory: dist
Install command: pnpm install (or leave the default)
Environment variables:
SITE_URL→https://your-site.vercel.app(or your custom domain)
Custom domain: Add it in the Vercel dashboard under Settings → Domains. Set SITE_URL to the final https:// URL after the domain is verified.
GitHub integration: Connect the repo. Vercel will deploy every push to main as a production deploy, and every PR as a preview. Previews work out of the box because the build is fully static.
Netlify
Section titled “Netlify”Build command: pnpm build
Publish directory: dist
Environment variables:
SITE_URL→https://your-site.netlify.app(or your custom domain)NODE_VERSION→20(matches the template’sengines.node)
Netlify reads netlify.toml if you have one; the Astro template doesn’t ship one. Add a minimal config if you want to pin headers or redirects:
[build] command = "pnpm build" publish = "dist"
[[headers]] for = "/*" [headers.values] X-Content-Type-Options = "nosniff" Referrer-Policy = "strict-origin-when-cross-origin"Custom domain: Netlify DNS or external. Update SITE_URL once the certificate is issued.
Cloudflare Pages
Section titled “Cloudflare Pages”Build command: pnpm build
Build output directory: dist
Environment variables:
SITE_URL→https://your-site.pages.dev(or your custom domain)NODE_VERSION→20(set in “Environment variables” or via.nvmrc)
Notes:
- Cloudflare’s edge cache is aggressive. If you push a new deploy and the content looks stale, do a hard refresh (Cmd+Shift+R) or set a cache-busting query string in the URL. The Astro build outputs content-hashed asset filenames, so JS/CSS will update — only the HTML may be cached.
- For a custom domain on Cloudflare, you can either bring a domain you already have on Cloudflare, or transfer one in. The Pages project attaches to the domain automatically.
GitHub Pages
Section titled “GitHub Pages”This is the lowest-friction option for a public, community-maintained directory — the workflow file is generated automatically when you scaffold with --github public --deploy github-pages.
Setup:
- In your GitHub repo, go to Settings → Pages.
- Under “Build and deployment”, choose “GitHub Actions”.
- The
build.ymlworkflow that ships with the template already publishes to Pages. Confirm the workflow haspages: writeandid-token: writepermissions (the generated workflow does).
Environment variables:
- The build workflow can read
${{ vars.SITE_URL }}(repo variable) or${{ secrets.SITE_URL }}(repo secret). SetSITE_URLtohttps://<org>.github.io/<repo>/for a project page, orhttps://<custom-domain>/if you’ve added a custom domain in the Pages settings.
Custom domain: Add a CNAME file at public/CNAME containing your domain (e.g., mydir.dev). Configure DNS per the GitHub Pages docs. The CNAME file is part of the build output, so it persists across deploys.
Gotcha: GitHub Pages serves the site from a subpath on the default domain (<org>.github.io/<repo>/). If your build assumes the site lives at the root, the static asset URLs will be wrong. The Astro template’s astro.config.mjs does not set a base — set SITE_URL to the full subpath URL, and the build will produce correct absolute URLs in sitemap.xml and meta tags.
Self-hosting (“none”)
Section titled “Self-hosting (“none”)”If you already have a server, a CDN, or an S3 bucket, the none deploy option means “the CLI will not generate a host-specific config”. You run pnpm build locally (or in your own CI), then upload dist/ however you like.
Common targets:
- AWS S3 + CloudFront:
aws s3 sync dist/ s3://your-bucket --delete, then invalidate the CloudFront cache. - A static NGINX server: copy
dist/to/var/www/html/. - A Docker container:
FROM nginx:alpine; COPY dist/ /usr/share/nginx/html/.
No special config in the build — dist/ is portable.
Pre-deploy checklist
Section titled “Pre-deploy checklist”Before you point your domain at a new deploy, run through this:
pnpm buildsucceeds locally.dist/index.htmlexists and contains your site name (search for it).dist/sitemap.xmlis present and lists the right URLs.dist/llms.txtanddist/llms-full.txtare present (these are what AI crawlers will read).dist/projects/<some-slug>/index.htmlexists for at least one record.SITE_URLis set in the host’s environment, not hardcoded inastro.config.mjs.pnpm preview(Astro’s preview server) shows the site the way you expect.
If any of those fail, fix it locally before pushing to the host — debugging a deployed site is slower than debugging a local build.
Continuous deployment
Section titled “Continuous deployment”The default workflow, when you scaffold with --github public --deploy <host>, is:
- A
build.ymlworkflow runs on every push tomainand on PRs. - A separate deploy step (host-specific) runs only on
main. - A scheduled
sync-github-metadata.ymlworkflow runsgrove sync githubweekly and commits the result back.
For a private site (--github none), only the build.yml workflow is generated. You can add the deploy step manually.
See Customize the Astro template for editing the workflows and astro.config.mjs if the defaults don’t match your host.