Wrangler deploy dry-run in CI before merging to main
I have merged PRs where npm run build passed and production still broke five minutes later. Astro finished, TypeScript looked fine, and then Wrangler complained about a missing asset binding or a worker bundle that no longer matched wrangler.json.
That gap is why I treat wrangler deploy --dry-run as part of CI—not as a deploy step, but as a final packaging check before anything hits main.
What a dry-run actually validates
astro build proves the site compiles. wrangler deploy --dry-run proves Wrangler can assemble and upload the Worker bundle without touching production.
On this repo, a successful dry-run prints something like:
Total Upload: 611 KiB / gzip: 151 KiB
Your Worker has access to the following bindings:
Binding Resource
env.SESSION KV Namespace
env.ASSETS Assets
--dry-run: exiting now.
That short output is doing real work. It catches problems I have actually hit:
mainorassets.directorypoints at the wrong path after a refactor- Missing or renamed bindings in
wrangler.json - Worker bundle size regressions from a new dependency or accidental server import
- Invalid Wrangler config that only surfaces at deploy time
Dry-run does not replace an end-to-end production test. It will not prove Turnstile, Resend, or dashboard secrets are set. It will not send mail or verify DNS. It just keeps the deploy path honest before merge.
The one script I run locally and in CI
This site already wires the full gate into package.json:
"check": "npm run blog:check-heroes && astro build && tsc && wrangler deploy --dry-run"
That order matters to me:
blog:check-heroes— fast content guardrails when posts changeastro build— producesdist/and the Worker entry Wrangler expectstsc— catches type errors the build might not surface the same waywrangler deploy --dry-run— validates the Cloudflare packaging step
Locally and in CI, I run the same command:
npm run check
On mayfield.io, GitHub Actions mirrors that:
- Site build (every push/PR to
main) —npm ci+npm run checkwithNASA_API_KEY(heroes,astro build,tsc,wrangler deploy --dry-run) - Blog PR checks —
npm run blog:check-heroesonly whensrc/content/blog/**changes (fast duplicate-hero signal without waiting for a full build)
Dry-run does not need Cloudflare deploy credentials; Wrangler validates the bundle locally and exits before upload. Production deploy stays manual on main.
The workflow lives in .github/workflows/site-build.yml:
- name: Check (heroes, build, tsc, wrangler dry-run)
env:
NASA_API_KEY: ${{ secrets.NASA_API_KEY }}
run: npm run check
What I still check outside CI
Dry-run will not save you from missing production secrets. Before I ship anything that touches /api/contact or other server routes, I still walk the human checklist:
- required secrets exist in the Cloudflare dashboard, not just in
.dev.vars - one happy-path and one failure-path request against the real endpoint
- logs show the route I expect (not just the home page)
Those steps belong in the pre-ship habit, not in every PR—but they complement CI instead of replacing it.
When the dry-run fails
Read the Wrangler error literally. Common fixes on this stack:
| Symptom | Likely cause |
|---|---|
Cannot resolve main in wrangler.json | Build did not run, or main is stale (Astro 6 expects @astrojs/cloudflare/entrypoints/server) |
| Unknown binding | wrangler.json out of sync with Astro adapter output |
| Upload size jumped | Accidental client import in a server route, or a heavy dependency pulled into the Worker |
Fix, rerun npm run check, then merge. The goal is never to learn about these on a Friday deploy.
Why I bother on a personal site
mayfield.io is small, but the stack is real: Astro 6, @astrojs/cloudflare v13, Workers assets, Turnstile, Resend. A broken deploy is still a broken site, even without a staging cluster.
Dry-run in CI is cheap insurance. It takes a minute on GitHub’s runners and saves the awkward “revert main” commit when Wrangler and Astro disagree.
For the broader pre-launch habit, see Checklist before shipping a small personal site. For the secrets side dry-run cannot cover, Local .dev.vars vs production secrets for Astro on Workers is the companion read.
Hero photo: Katze okkupiert PC-Tastatur by Mattes, CC BY-SA 2.0.