Skip to main content
Service versioning is critical for durable execution. When requests can sleep for hours or days, Restate needs to ensure they always use the same code version they started with. When Restate resumes an invocation, it replays the journal. The journal should be executed against the same code version to ensure deterministic behaviour. This requires understanding two fundamental approaches: immutable versions and in-place updates.

Immutable versions vs In-place updates

Immutable versions create a completely new deployment for each code change. Once a deployment is registered with Restate, its code cannot change. Restate automatically proxies new requests to the latest version, while ongoing requests continue with their original version until completion. In-place updates modify the code at an existing endpoint without creating a new deployment. While sometimes necessary for critical bug fixes, this approach breaks determinism if not handled carefully. Restate requires the use of immutable versions. They guarantee that your durable executions remain deterministic without requiring you to think about the complexities of breaking existing in-flight invocations. You should in-place updates only in special situations, such as bug fixes.

What is a deployment?

A deployment in Restate is a specific, versioned instance of your service code, whether running as an HTTP endpoint, a Lambda function, or another supported environment. Each deployment is immutable by design: once registered, its code and endpoint must not change. An invocation is bound to a specific deployment: it starts and completes within that same deployment. This ensures that in-flight requests always see the code they started with, preserving correctness and determinism.
If possible, avoid long-running handlers (days or months). Otherwise, you need to keep old deployments around for a long time (until all invocations complete).Instead, break work into smaller chunks and chain them via service-to-service calls.For example, instead of a handler that sleeps for hours, have it schedule a new invocation via a delayed message.
It’s possible to resume an invocation on a different deployment. This is useful if there is a bug in the original deployment’s application code, and you want to move the invocation to a new deployment which contains a fix.

Registering a deployment

After deploying your service, you must register it with Restate so it can be discovered and invoked. You can register a deployment using:
  • The Restate UI
  • The CLI:
    restate deployments register http://localhost:9080
    
  • The Admin API:
    curl localhost:9070/deployments --json '{"uri": "http://localhost:9080"}'
    
Note:
  • For AWS Lambda, use the function ARN instead of a URL (e.g., arn:aws:lambda:region:account-id:function:function-name:version).
  • If running Restate in Docker, use host.docker.internal instead of localhost.

Deployments supporting only HTTP1.1

Some deployments only support HTTP/1.1, and not HTTP/2. This means Restate cannot use bidirectional streaming of journal entries and needs to communicate with the service in request-response mode (learn more). To register such deployments, you need to specify using HTTP/1.1 during registration:
restate deployments register http://localhost:9080 --use-http1.1
When registering deployments via the UI, you can select the HTTP/1.1 checkbox.

Automatic versioning with FaaS platforms

Function-as-a-Service (FaaS) platforms automatically handle immutable versioning through version-specific URLs or ARNs. This makes them ideal for Restate deployments as they eliminate the complexity of manual version management. Have a look at the dedicated deployment docs to learn more:
  • Vercel: Register the Commit URL so that Restate can address specific Vercel deployments.
  • AWS Lambda: When you publish a Lambda function, it automatically creates an immutable version with a unique ARN that never changes.
  • Deno Deploy: Register the Preview URLs so that Restate can target specific Deno deployments.
  • Cloudflare Workers: Register the Preview URLs so that Restate can target specific Cloudflare deployments.

Automatic versioning with Kubernetes Operator

The Restate Kubernetes operator provides a higher-level abstraction for managing service deployments and their versions automatically. The operator handles the complete versioning lifecycle:
  1. Deploy new versions: Create a new RestateDeployment with your updated container image
  2. Automatic registration: The operator registers the new deployment with the Restate cluster
  3. Traffic routing: New requests automatically route to the latest version
  4. Graceful draining: The operator monitors older deployments for ongoing invocations
  5. Auto-scaling to zero: Once drained, older versions automatically scale to zero
For complete examples and specifications, see Restate Kubernetes Operator.

Manual deployment management

For non-FaaS deployments or when you need direct control, you can manually manage immutable deployments.

Creating immutable deployments

Since deployments are immutable, updates require creating new deployments:
1

Deploy new version

Deploy your updated service code to a new endpoint (e.g., http://greeter-v2/).
2

Register the new deployment

Then register it with Restate:
restate deployments register http://greeter-v2/
Restate automatically routes new requests to the latest deployment. Existing requests continue on the original deployment.
3

Monitor deployment status

Check for in-flight invocations on deployments via the UI or CLI.
CLI
# Find the deployment ID of your service
restate services list
# Check the number of active invocations for each deployment
restate deployments list
# Get detailed information about a specific deployment
restate deployment describe <deployment_id>
4

Remove old deployment

Once all invocations are complete, you can safely remove the old deployment.
restate deployments remove dp_14LsPzGz9HBxXIeBoH5wYUh
If you need to force removal before the deployment is fully drained, use the --force flag in CLI, or ?force=true for curl.
Virtual Object state persists across versions. Ensure your state schema remains backward compatible.

Removing a service

Restate does not support removing individual services directly. Instead, you must remove the deployment that contains the service. To do this safely, follow the steps below:
  1. Ensure no other handlers or services have business logic that calls the service you’re removing.
  2. If several services are bundled in the same deployment, you can’t remove only one of them. You have to remove the whole deployment. So make sure that you first deploy the services you want to keep in a separate new deployment.
  3. Make the service private to avoid accepting new HTTP requests.
  4. Check whether the service has pending invocations by filtering the invocations on deployment ID in the UI or via restate services status, and wait until the service is drained (i.e. no ongoing invocations).
When all prerequisites are fulfilled, you can remove the deployment containing the service via the UI or via:
restate deployments remove dp_14LsPzGz9HBxXIeBoH5wYUh
If the deployment isn’t drained yet but you still want to remove it, use the --force flag in CLI, or ?force=true for curl.

Advanced: Updating deployments in-place

While deployments should be immutable, critical bugs sometimes require updating deployed code to fix stuck invocations.

When This is Needed

If a bug (like a null pointer exception) occurs mid-execution, registering a new deployment only fixes new invocations. Existing stuck invocations need the original deployment fixed.

Two Approaches

  1. Update underlying code at the same URI (does not work for Restate Kubernetes Operator or many FaaS platforms)
  2. Update deployment endpoint to point to a patched version:
    curl -X PUT localhost:9070/deployments/dp_14LsPzGz9HBxXIeBoH5wYUh \
      --json '{"uri": "http://greeter-patched/"}'
    

Common Scenarios

The current deployment handling new invocations has bugs:
  1. Develop a fix, based on the current deployed version, that resolves the failing invocations. Care should be taken to ensure that the new version has the same behaviour as the old version, for any code paths that in-flight invocations have successfully completed (ie, any changes must be from the point of failure onwards).
  2. By updating the underlying code or with the update deployment API, change the active deployment to include the fix. Verify that this resolves the issue both for new invocations, and for those already failing.
It’s common to notice failing invocations because they are preventing an old deployment from fully draining. In this case there are several concerns; the failing invocations on deployment 1, any failing invocations on deployment 2, and the potential for new failing invocations to occur on deployment 2 as well. The following steps should be taken:
  1. Develop a fix as above, based on the version backing deployment 1.
  2. By updating the underlying code or with the update deployment API, change deployment 1 to include the fix. Verify that this resolves the failing invocations on this deployment.
  3. Rebase the fix onto the version backing deployment 2.
  4. By updating the underlying code or with the update deployment API, change deployment 2 to include the fix. Verify that this resolves any failing invocations, if any, new invocations.
It is possible to use the update deployment API to give a deployment the same URI/ARN as another deployment. This is useful where the an appropriate fix for a drained deployment has already been registered as a new deployment. If this is done, there will be two deployments with the same endpoint, which is otherwise not allowed. It is strongly recommended that you delete one of the two deployments when the failing invocations have been resolved.
I