cloudflare / cloudflare/workers-sdk
Allow atomically setting a Worker's complete runtime env (vars + secrets) at deploy
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
Config-as-code and secrets-manager workflows (e.g. [varlock](https://varlock.dev)) want the deploy to _own the complete runtime env_: every deploy should converge the Worker's vars and secrets to the declared set, in **one atomic operation** - no lingering state from previous deploys, no partial-state windows, no out-of-band follow-up calls. While the `--secrets-file` flag opened up some new possibilities for us, currently it (and related apis) are _additive_, so clearing out additional config that should not be set requires a second step, and may result in a second deployment.
Some of this can be achieved through changes in wrangler only, but some will need backend changes. It also begs a question of whether the split handling and flags between vars/secrets should be reconsidered more broadly. They could be treated as part of the same system, with the distinction being only which ones are sensitive/secret.
### `wrangler deploy` (and `wrangler versions upload`)
- vars -- already atomic and declarative (the upload carries the total binding set; `--keep-vars` is the opt-out)
- secrets -- forcibly additive - `--secrets-file` always sends `keep_bindings: ["secret_text", "secret_key"]` with no opt-out
**Proposed solution:** (without a larger redesign)
A new `--secrets-file-mode ` could toggle this behavior.
### Bulk secret commands (`wrangler secret bulk`, `wrangler versions secret bulk`, `wrangler preview secret bulk`)
These update secrets without deploying new code (e.g. rotating between releases), and they are additive too: a JSON file with null values can delete individual keys in `secret bulk` and `preview secret bulk` (`versions secret bulk` silently ignores nulls and is purely additive), but converging to a declared set requires the caller to compute the deletion diff and know the remote names. A similar mode toggle fits all three, and atomically.
### `wrangler preview` (beta)
The complication here is that the end-result is a product of merging the preview base config, secrets from previous preview deployments, and the new deploy. In our proposed flow, we want the deploy to fully own the runtime vars/secrets. Any persistent data can be fed in using build-time vars/secrets.
`ignore_base_config` is too blunt (it also discards observability, logpush, limits, placement, cache, tail consumers). Client-side convergence requires a second deployment, which violates atomicity and leaves a live window with the wrong env.
**Proposed solution:** an `env_mode: "replace"` (or similar) option on the Preview deployment create API. The request's `env` is the complete runtime env; base config still applies for everything except `env`. This mirrors the upload API's existing total-replacement semantics. Since Previews are in private beta, now seems like the right time to shape this.
### `cloudflare.config.ts` (beta)
The same question needs an answer in the new IaC-style config, but there it should probably not be a CLI flag at all. A `cloudflare.config.ts` file is a declarative statement of the Worker's desired state, so the merge-vs-replace posture is a property of the config itself, not of a particular deploy invocation - something like a settings-level mode declaring that the configured env (vars and secrets alike) is the complete set, with `bindings.secret()` declarations marking the externally-managed exceptions. The CLI flag proposed above is the right shape for the current `wrangler.json` + `--secrets-file` world; the new format should be able to express the same intent in-config so every deploy path (wrangler, the Vite plugin's Build Output flow, future deploy tooling) honors it consistently.
---
### Persistent secrets
Note that secrets which would otherwise be hand-set on the Worker (and therefore need preserving across deploys) can instead be stored as build secrets (Workers Builds build variables and secrets, a separate store that deploys never modify). They are then available in the deploy environment, where the tool managing the secret set can pass them through into the runtime set like any other value. This means replace mode needs no carve-out for manually-managed secrets beyond the existing `secrets.required` mechanism.
---
I worked on implementing the `--secrets-file-mode` and other client-side only changes in #15357, but realized that the previews side of things is a bit more complex, and that now may be a good time to discuss the overall design before adding more flags.
Related context: this came out of exploring config-as-code secret management in discussion #15122.
Contributor guide
Research direction
Review the entry points named here: wrangler deploy, versions upload, the three secret bulk commands, wrangler preview, and cloudflare.config.ts. Compare the proposed secrets-file mode and Preview env_mode with the client-side work in #15357, then determine the backend and config design needed. Done means one agreed atomic replacement model that covers each deploy path without follow-up calls or partial runtime environments.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend-api-design, cli, cloud
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100