Deploying to Cloudflare

Cherry's Cloudflare target is Workers static assets, Cloudflare's own recommendation for new sites. Requests for static assets are free and unmetered, and a _site/ tree is exactly what it wants. The story is the same one-verb story as GitHub Pages: you never write YAML, and you never write a Worker script either.

Generate the pipeline

$ cherry gen.action --host cloudflare
Wrote wrangler.jsonc (worker: my-site) and .github/workflows/cloudflare.yml — add the CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID repository secrets once, then every push deploys to my-site.<account>.workers.dev.

Two files. The first is the Worker configuration:

// Generated by `mix cherry.gen.action --host cloudflare` — regenerate
// with --force rather than editing, or delete this header to take
// ownership.
{
"name": "my-site",
"compatibility_date": "2026-08-21",
"assets": {
"directory": "./_site",
"not_found_handling": "404-page"
}
}

There is no main entry because there is no Worker script: the whole Worker is your built site served as static assets. The name comes from a slug of your site title; pass --name to choose your own. not_found_handling points at the 404.html every Cherry build emits, so unknown routes get your themed 404 with a proper status code. Trailing-slash handling is left on Cloudflare's default, which matches the directory-index URLs Cherry emits.

The second file, .github/workflows/cloudflare.yml, is the deploy workflow: on every push to main (choose another with --branch) it builds with the published Cherry action, which installs the toolchain, runs cherry check --strict and cherry build, and then ships _site/ with wrangler deploy.

One-time Cloudflare setup

  1. Create an API token: Cloudflare dashboard, My Profile, API Tokens, Create Token, and use the Edit Cloudflare Workers template.
  2. Add two repository secrets under Settings, Secrets and variables, Actions: CLOUDFLARE_API_TOKEN (the token) and CLOUDFLARE_ACCOUNT_ID (shown on the dashboard's Workers overview page).
  3. Push.

The first deploy creates the Worker and the site is live at https://my-site.<account>.workers.dev. For a real domain, open the Worker in the dashboard and add it under Settings, Domains & Routes; Cloudflare handles DNS and the certificate since it already serves the zone. Keep url in cherry.exs as the real domain so canonical links, feeds, and the sitemap agree.

Note

Keep base_path: "/". A Worker serves at the root of its domain, so the project-pages prefix dance that GitHub needs does not exist here.

Headers and redirects

Anything in static/ is copied byte-for-byte into the site root, and that includes Cloudflare's two magic files. A static/_headers file sets response headers:

/assets/*
Cache-Control: public, max-age=31536000, immutable

and a static/_redirects file declares redirects, one per line:

/old-post/ /new-post/ 301

Cloudflare parses both out of the deployed assets (they are never served themselves) and applies them at the edge. No Cherry configuration involved: drop the file in, push.

search: "cherry" needs nothing anywhere. If you chose search: "pagefind", the build shells out to npx pagefind; GitHub's runners ship with Node, so the generated workflow works as-is.

Without GitHub Actions at all

Cloudflare can also build the site itself on every push through Workers Builds, its Git integration. Its build image has no Elixir, and that is fine, because the standalone binary needs none. Connect the repository to the Worker and set the build command to:

curl -fsSL https://cherrybomb.dev/install.sh | sh && ~/.local/bin/cherry build

with the default deploy command (npx wrangler deploy) left alone. Install, build, verify, and deploy, on Cloudflare's machines, with no toolchain anywhere in your repo.

Prefer GitHub Pages? That walkthrough is the GitHub Pages guide; the reference for both hosts is Deploying.