Gradual Deployments
Gradual Deployments
Gradual deployments let you roll out a new Worker version to a configurable percentage of production traffic before committing to a full release. This is the recommended strategy for any change that touches compilation logic, rate-limiting, or authentication — where a bad deploy could affect every user simultaneously.
Table of contents
- How it works
- Quick-start checklist
- Step-by-step rollout
- deno task shortcuts
- GitHub Actions — manual gradual rollout workflow
- Integration with feature flags
- Monitoring during a rollout
- Rollback
- wrangler.toml — no required changes
- Caveats and limitations
1. How it works
Cloudflare Workers versioning decouples building a version from routing traffic to it. The workflow has two distinct commands:
| Command | What it does |
|---|---|
wrangler versions upload | Bundles and uploads a new Worker version — no traffic change |
wrangler versions deploy --percentage N | Routes N % of requests to the uploaded version |
The standard wrangler deploy command is equivalent to
versions upload + versions deploy --percentage 100 in a single step.
flowchart LR
upload["wrangler versions upload\n(new code, no traffic shift)"]
deploy10["versions deploy --percentage 10\n(10 % of requests)"]
deploy50["versions deploy --percentage 50\n(50 % of requests)"]
deploy100["versions deploy --percentage 100\n(full rollout)"]
rollback["versions deploy (previous version)\n(instant rollback)"]
upload --> deploy10 --> monitor{"healthy?"}
monitor -->|"yes"| deploy50 --> monitor2{"healthy?"}
monitor2 -->|"yes"| deploy100
monitor -->|"no"| rollback
monitor2 -->|"no"| rollback
All traffic percentages are per-colo (Cloudflare edge location), not global. Cloudflare uses deterministic routing so a given visitor tends to consistently hit the same version during a partial rollout.
2. Quick-start checklist
Before your first gradual rollout:
-
CLOUDFLARE_API_TOKENsecret hasAccount > Workers:Editpermission -
CLOUDFLARE_ACCOUNT_IDsecret is set - You have access to the GitHub Actions manual workflows in this repository
- Review Monitoring during a rollout so you know what to watch
3. Step-by-step rollout
Step 1 — Upload the new version (no traffic change)
# From repo root:deno task wrangler:versions:uploadThis bundles the worker and uploads it to Cloudflare.
The output includes a version ID like v2026-04-01T12:00:00-abc1234.
Step 2 — Route 10 % of traffic to the new version
# Interactive: wrangler asks you to confirm the version and percentagedeno task wrangler:versions:deploy# Wrangler will prompt you to choose the version and percentageOr non-interactively (useful in CI):
deno run -A npm:wrangler versions deploy --version-id <VERSION_ID> --percentage 10 --yesStep 3 — Monitor for 10–30 minutes
Check the Cloudflare dashboard and the metrics endpoint:
# Live log tail (filter for errors)deno task wrangler:tail
# Health endpointcurl https://api.bloqr.dev/api/health
# Error rates (Cloudflare Analytics Engine)curl https://api.bloqr.dev/api/metricsStep 4 — Increase or roll back
If healthy, increase the percentage:
# 50 % of trafficdeno run -A npm:wrangler versions deploy --version-id <VERSION_ID> --percentage 50 --yes
# 100 % full rolloutdeno run -A npm:wrangler versions deploy --version-id <VERSION_ID> --percentage 100 --yesIf unhealthy, see Rollback.
4. deno task shortcuts
The following tasks are available in deno.json:
| Task | Equivalent command |
|---|---|
deno task wrangler:versions:upload | wrangler versions upload |
deno task wrangler:versions:deploy | wrangler versions deploy (interactive) |
deno task wrangler:versions:list | wrangler versions list |
deno task wrangler:versions:view | wrangler versions view |
deno task wrangler:deployments:list | wrangler deployments list |
deno task wrangler:deployments:status | wrangler deployments status |
5. GitHub Actions — manual gradual rollout workflow
The .github/workflows/gradual-deploy.yml workflow provides a manual trigger for
gradual deployments with configurable percentages directly from the GitHub Actions UI.
Inputs
| Input | Default | Description |
|---|---|---|
percentage | 10 | Traffic percentage (1–100) to route to the new version |
version_id | (required) | Cloudflare version ID from a previous versions upload |
confirm_full_rollout | false | Must be true to allow percentage = 100 |
Triggering a gradual deploy
- Go to Actions → Gradual Deploy (Manual) in the GitHub UI.
- Click Run workflow.
- Fill in the version ID (from a prior upload or
versions listoutput). - Set the traffic percentage.
- Click Run workflow.
To get the version ID of the most recently uploaded version:
deno task wrangler:versions:listFull rollout via workflow
Set percentage = 100 and check the confirm_full_rollout checkbox. This extra
confirmation prevents accidental 100 % deploys from a mis-click.
6. Integration with feature flags
For changes that affect compilation behaviour, combine gradual deployments with the KV-backed Feature Flags system:
-
Wrap the new code path behind a feature flag:
const enabled = await featureFlagService.isEnabled('NEW_COMPILATION_PIPELINE', userId);if (enabled) {return runNewPipeline(config);}return runLegacyPipeline(config); -
Deploy with
--percentage 100(always) but target the new path only for users in the feature-flag rollout group:Terminal window # Enable for 5 % of users:wrangler kv:key put --binding FEATURE_FLAGS flag:NEW_COMPILATION_PIPELINE \'{"enabled":true,"rollout_percentage":5,"updatedAt":"2026-04-01T00:00:00Z"}' -
Use gradual Worker traffic and the feature flag together for maximum safety on high-risk changes.
7. Monitoring during a rollout
Key signals to watch
| Signal | Source | Threshold |
|---|---|---|
| HTTP error rate | GET /api/metrics | < 1 % of requests |
console.error log count | wrangler tail --format pretty | 0 unexpected errors |
| Worker exception count | Cloudflare dashboard → Workers → Errors | 0 new exceptions |
| Compilation success rate | Analytics Engine (ANALYTICS_ENGINE binding) | ≥ 99 % |
| P95 response time | Cloudflare dashboard → Workers → Metrics | < 2 s |
Live tail commands
# All logs (structured JSON):deno task wrangler:tail
# Filter for errors only:deno run -A npm:wrangler tail --format json | jq 'select(.outcome != "ok")'
# Show only compilation failures:deno run -A npm:wrangler tail --format json \ | jq 'select(.logs[].message[] | strings | test("compilation.*fail|error"; "i"))'Persistent Workers Logs
Because persist = true is set in wrangler.toml, logs are retained beyond the
live-tail window. Query them in the Cloudflare dashboard:
- Workers & Pages → bloqr-backend → Logs
- Filter by time range, outcome, and log level
See Cloudflare Native Observability for the full details on Workers Logs and Logpush retention.
8. Rollback
Instant rollback to the previous stable version
# List current deployments to identify the stable version IDdeno task wrangler:deployments:list
# Redeploy the previous version at 100 %deno run -A npm:wrangler versions deploy --version-id <STABLE_VERSION_ID> --percentage 100 --yesThis takes effect immediately — Cloudflare routes 100 % of traffic back to the previous version within seconds.
Rollback via standard deploy
If you don’t have the previous version ID, a standard wrangler deploy of the
last-known-good commit is equivalent:
git checkout <LAST_GOOD_SHA>deno task wrangler:deployAutomatic rollback (future work)
Integration with health-check monitoring to trigger automatic rollback is tracked in
the deployment versioning system. See GET /api/health and the HEALTH_MONITORING_WORKFLOW
Cloudflare Workflow for the health-check infrastructure.
9. wrangler.toml — no required changes
No wrangler.toml changes are required to enable gradual deployments. The feature is
available to all Workers Paid plan accounts via the wrangler versions CLI.
The wrangler.toml already includes relevant observability settings that make gradual
rollouts safe:
# Forward worker logs to Cloudflare Logpush (production only).logpush = true
[observability.logs]enabled = truehead_sampling_rate = 1 # capture every request during rolloutpersist = true # retain logs for post-rollout analysisinvocation_logs = true # include duration, status, exception data per invocation10. Caveats and limitations
| Limitation | Detail |
|---|---|
| Durable Objects | Multiple versions cannot share the same Durable Object class. If your change modifies DO class logic, deploy at 100 % to avoid version skew. |
| KV / D1 / R2 | Shared by all versions — schema changes must be backward-compatible. Use the database migration checklist when altering D1 or Neon schemas. |
| Queue consumers | All versions share the same queue consumer configuration. |
| Compatibility flags | All versions in a deployment share the same compatibility_date and compatibility_flags. |
| Percentage is per-colo | A --percentage 10 deploy may result in slightly more or less than 10 % of global traffic depending on colo sizes. |
| Tail consumers | The bloqr-tail log sink receives events from all active versions. |
See also
- Cloudflare Workers Versioning docs
- Deployment Environments — local, dev, production environments
- Deployment Versioning — internal build-number tracking
- Cloudflare Native Observability — logging and tracing
- Feature Flags — KV-backed feature flags for fine-grained rollouts