---
title: Gradual deployments · Cloudflare Workers docs
description: Incrementally deploy code changes to your Workers with gradual deployments.
lastUpdated: 2025-11-03T20:28:35.000Z
chatbotDeprioritize: false
source_url:
  html: https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/
  md: https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/index.md
---

Gradual Deployments give you the ability to incrementally deploy new [versions](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#versions) of Workers by splitting traffic across versions.

![Gradual Deployments](https://developers.cloudflare.com/_astro/gradual-deployments.C6F9MQ6U_Z1KFl3a.webp)

Using gradual deployments, you can:

* Gradually shift traffic to a newer version of your Worker.
* Monitor error rates and exceptions across versions using [analytics and logs](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/#observability) tooling.
* [Roll back](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/rollbacks/) to a previously stable version if you notice issues when deploying a new version.

## Use gradual deployments

The following section guides you through an example usage of gradual deployments. You will choose to use either [Wrangler](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/#via-wrangler) or the Cloudflare dashboard to:

* Create a new Worker.
* Publish a new version of that Worker without deploying it.
* Create a gradual deployment between the two versions.
* Progress the deployment of the new version to 100% of traffic.

### Via Wrangler

Note

Minimum required Wrangler version: 3.40.0. Versions before 3.73.0 require you to specify a `--x-versions` flag.

#### 1. Create and deploy a new Worker

Create a new `"Hello World"` Worker using the [`create-cloudflare` CLI (C3)](https://developers.cloudflare.com/pages/get-started/c3/) and deploy it.

```sh
npm create cloudflare@latest <NAME> -- --type=hello-world
```

Answer `yes` or `no` to using TypeScript. Answer `yes` to deploying your application. This is the first version of your Worker.

#### 2. Create a new version of the Worker

To create a new version of the Worker, edit the Worker code by changing the `Response` content to your desired text and upload the Worker by using the [`wrangler versions upload`](https://developers.cloudflare.com/workers/wrangler/commands/#versions-upload) command.

```sh
npx wrangler versions upload
```

This will create a new version of the Worker that is not automatically deployed.

#### 3. Create a new deployment

Use the [`wrangler versions deploy`](https://developers.cloudflare.com/workers/wrangler/commands/#versions-deploy) command to create a new deployment that splits traffic between two versions of the Worker. Follow the interactive prompts to create a deployment with the versions uploaded in [step #1](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/#1-create-and-deploy-a-new-worker) and [step #2](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/#2-create-a-new-version-of-the-worker). Select your desired percentages for each version.

```sh
npx wrangler versions deploy
```

#### 4. Test the split deployment

Run a cURL command on your Worker to test the split deployment.

```bash
for j in {0..10}
do
    curl -s https://$WORKER_NAME.$SUBDOMAIN.workers.dev
done
```

You should see 10 responses. Responses will reflect the content returned by the versions in your deployment. Responses will vary depending on the percentages configured in [step #3](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/#3-create-a-new-deployment).

You can test also target a specific version using [version overrides](#version-overrides).

#### 5. Set your new version to 100% deployment

Run `wrangler versions deploy` again and follow the interactive prompts. Select the version uploaded in [step 2](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/#2-create-a-new-version-of-the-worker) and set it to 100% deployment.

```sh
npx wrangler versions deploy
```

### Via the Cloudflare dashboard

1. In the Cloudflare dashboard, go to the **Workers & Pages** page.

   [Go to **Workers & Pages**](https://dash.cloudflare.com/?to=/:account/workers-and-pages)

2. Select **Create application** > **Hello World** template > deploy your Worker.

3. Once the Worker is deployed, go to the online code editor through **Edit code**. Edit the Worker code (change the `Response` content) and upload the Worker.

4. To save changes, select the **down arrow** next to **Deploy** > **Save**. This will create a new version of your Worker.

5. Create a new deployment that splits traffic between the two versions created in step 3 and 5 by going to **Deployments** and selecting **Deploy Version**.

6. cURL your Worker to test the split deployment.

```bash
for j in {0..10}
do
    curl -s https://$WORKER_NAME.$SUBDOMAIN.workers.dev
done
```

You should see 10 responses. Responses will reflect the content returned by the versions in your deployment. Responses will vary depending on the percentages configured in step #6.

## Gradual deployments with static assets

When your Worker serves [static assets](https://developers.cloudflare.com/workers/static-assets/), gradual deployments can cause asset compatibility issues where users receive HTML from one version that references assets only available in another version, leading to 404 errors.

For detailed guidance on handling static assets during gradual rollouts, including specific examples and configuration steps, refer to [Gradual rollouts](https://developers.cloudflare.com/workers/static-assets/routing/advanced/gradual-rollouts/).

## Version affinity

By default, the percentages configured when using gradual deployments operate on a per-request basis — a request has a X% probability of invoking one of two versions of the Worker in the [deployment](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#deployments).

You may want requests associated with a particular identifier (such as user, session, or any unique ID) to be handled by a consistent version of your Worker to prevent version skew. Version skew occurs when there are multiple versions of an application deployed that are not forwards/backwards compatible. You can configure version affinity to prevent the Worker's version from changing back and forth on a per-request basis.

You can do this by setting the `Cloudflare-Workers-Version-Key` header on the incoming request to your Worker. For example:

```sh
curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: foo'
```

For a given [deployment](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#deployments), all requests with a version key set to `foo` will be handled by the same version of your Worker. The specific version of your Worker that the version key `foo` corresponds to is determined by the percentages you have configured for each Worker version in your deployment.

You can set the `Cloudflare-Workers-Version-Key` header both when making an external request from the Internet to your Worker, as well as when making a subrequest from one Worker to another Worker using a [service binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/).

### Setting `Cloudflare-Workers-Version-Key` using Ruleset Engine

You may want to extract a version key from certain properties of your request such as the URL, headers or cookies. You can configure a [Ruleset Engine](https://developers.cloudflare.com/ruleset-engine/) rule on your zone to do this. This allows you to specify version affinity based on these properties without having to modify the external client that makes the request.

For example, if your worker serves video assets under the URI path `/assets/` and you wanted requests to each unique asset to be handled by a consistent version, you could define the following [request header transform rule](https://developers.cloudflare.com/rules/transform/request-header-modification/):

Text in **Expression Editor**:

```txt
starts_with(http.request.uri.path, "/asset/")
```

Selected operation under **Modify request header**: *Set dynamic*

**Header name**: `Cloudflare-Workers-Version-Key`

**Value**: `regex_replace(http.request.uri.path, "/asset/(.*)", "${1}")`

## Version overrides

You can use version overrides to send a request to a specific version of your Worker in your gradual deployment.

To specify a version override in your request, you can set the `Cloudflare-Workers-Version-Overrides` header on the request to your Worker. For example:

```sh
curl -s https://example.com -H 'Cloudflare-Workers-Version-Overrides: my-worker-name="dc8dcd28-271b-4367-9840-6c244f84cb40"'
```

`Cloudflare-Workers-Version-Overrides` is a [Dictionary Structured Header](https://www.rfc-editor.org/rfc/rfc8941#name-dictionaries).

The dictionary can contain multiple key-value pairs. Each key indicates the name of the Worker the override should be applied to. The value indicates the version ID that should be used and must be a [String](https://www.rfc-editor.org/rfc/rfc8941#name-strings).

A version override will only be applied if the specified version is in the current deployment. The versions in the current deployment can be found using the [`wrangler deployments list`](https://developers.cloudflare.com/workers/wrangler/commands/#list-5) command or on the **Workers & Pages** page of the Cloudflare dashboard > Select your Workers > Deployments > Active Deployment.

Verifying that the version override was applied

There are a number of reasons why a request's version override may not be applied. For example:

* The deployment containing the specified version may not have propagated yet.
* The header value may not be a valid [Dictionary](https://www.rfc-editor.org/rfc/rfc8941#name-dictionaries).

In the case that a request's version override is not applied, the request will be routed according to the percentages set in the gradual deployment configuration.

To make sure that the request's version override was applied correctly, you can [observe](#observability) the version of your Worker that was invoked. You could even automate this check by using the [runtime binding](#runtime-binding) to return the version in the Worker's response.

### Example

You may want to test a new version in production before gradually deploying it to an increasing proportion of external traffic.

In this example, your deployment is initially configured to route all traffic to a single version:

| Version ID | Percentage |
| - | - |
| db7cd8d3-4425-4fe7-8c81-01bf963b6067 | 100% |

Create a new deployment using [`wrangler versions deploy`](https://developers.cloudflare.com/workers/wrangler/commands/#versions-deploy) and specify 0% for the new version whilst keeping the previous version at 100%.

| Version ID | Percentage |
| - | - |
| dc8dcd28-271b-4367-9840-6c244f84cb40 | 0% |
| db7cd8d3-4425-4fe7-8c81-01bf963b6067 | 100% |

Now test the new version with a version override before gradually progressing the new version to 100%:

```sh
curl -s https://example.com -H 'Cloudflare-Workers-Version-Overrides: my-worker-name="dc8dcd28-271b-4367-9840-6c244f84cb40"'
```

## Gradual deployments for Durable Objects

To provide [global uniqueness](https://developers.cloudflare.com/durable-objects/platform/known-issues/#global-uniqueness), only one version of each [Durable Object](https://developers.cloudflare.com/durable-objects/) can run at a time. This means that gradual deployments work slightly differently for Durable Objects.

When you create a new gradual deployment for a Worker with Durable Objects, each Durable Object is assigned a Worker version based on the percentages you configured in your [deployment](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#deployments). This version will not change until you create a new deployment.

![Gradual Deployments Durable Objects](https://developers.cloudflare.com/_astro/durable-objects.D92CiuSQ_Z1KD3Vq.webp)

### Example

This example assumes that you have previously created 3 Durable Object instances with names "foo", "bar" and "baz".

Your Worker is currently on a version that we will call version "A" and you want to gradually deploy a new version "B" of your Worker.

Here is how the versions of your Durable Objects might change as you progress your gradual deployment:

| Deployment config | "foo" | "bar" | "baz" |
| - | - | - | - |
| Version A: 100%  | A | A | A |
| Version B: 20% Version A: 80% | B | A | A |
| Version B: 50% Version A: 50% | B | B | A |
| Version B: 100%  | B | B | B |

This is only an example, so the versions assigned to your Durable Objects may be different. However, the following is guaranteed:

* For a given deployment, requests to each Durable Object will always use the same Worker version.
* When you specify each version in the same order as the previous deployment and increase the percentage of a version, Durable Objects which were previously assigned that version will not be assigned a different version. In this example, Durable Object "foo" would never revert from version "B" to version "A".
* The Durable Object will only be [reset](https://developers.cloudflare.com/durable-objects/observability/troubleshooting/#durable-object-reset-because-its-code-was-updated) when it is assigned a different version, so each Durable Object will only be reset once in this example.

Note

Typically, a Worker bundle will define both the Durable Object class and a Worker that interacts with it. In this case, you cannot deploy changes to your Durable Object and its Worker independently.

You should ensure that API changes between your Durable Object and its Worker are [forwards and backwards compatible](https://developers.cloudflare.com/durable-objects/platform/known-issues/#code-updates) whether you are using gradual deployments or not. However, using gradual deployments will make it even more likely that different versions of your Durable Objects and its Worker will interact with each other.

### Migrations

Versions of Worker bundles containing new Durable Object migrations cannot be uploaded. This is because Durable Object migrations are atomic operations. Durable Object migrations can be deployed with the following command:

```sh
npx wrangler versions deploy
```

To limit the blast radius of Durable Object migration deployments, migrations should be deployed independently of other code changes.

To understand why Durable Object migrations are atomic operations, consider the hypothetical example of gradually deploying a delete migration. If a delete migration were applied to 50% of Durable Object instances, then Workers requesting those Durable Object instances would fail because they would have been deleted.

To do this without producing errors, a version of the Worker which does not depend on any Durable Object instances would have to have already been rolled out. Then, you can deploy a delete migration without affecting any traffic and there is no reason to do so gradually.

## Observability

When using gradual deployments, you may want to attribute Workers invocations to a specific version in order to get visibility into the impact of deploying new versions.

### Logpush

A new `ScriptVersion` object is available in [Workers Logpush](https://developers.cloudflare.com/workers/observability/logs/logpush/). `ScriptVersion` can only be added through the Logpush API right now. Sample API call:

```bash
curl -X POST 'https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/logpush/jobs' \
-H 'Authorization: Bearer <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"name": "workers-logpush",
"output_options": {
    "field_names": ["Event", "EventTimestampMs", "Outcome", "Logs", "ScriptName", "ScriptVersion"],
},
"destination_conf": "<DESTINATION_URL>",
"dataset": "workers_trace_events",
"enabled": true
}'| jq .
```

`ScriptVersion` is an object with the following structure:

```json
scriptVersion: {
    id: "<UUID>",
    message: "<MESSAGE>",
    tag: "<TAG>"
}
```

### Runtime binding

Use the [Version metadata binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/version-metadata/) in to access version ID or version tag in your Worker.

## Limits

### Deployments limit

You can only create a new deployment with the last 100 uploaded versions of your Worker.
