Skip to main content
Deploying new versions of code without breaking ongoing work is complicated in every stateful application. You need to make sure that requests start, retry, and complete on the same version of your “business logic”, to avoid problems like inconsistent execution or state corruption. Restate helps via the concept of immutable deployments. When you deploy a version of your code, you give it an immutable, unique endpoint and register it with Restate. Restate then makes sure that requests start and end on the same version, by sending any retry attempts always to the same endpoint. This eliminates mid-execution version mismatches: no version compatibility logic is needed in your code.

Service deployments

A service deployment is a specific instance of your service code associated with an HTTP endpoint, a Lambda function, or other supported environment. Deployments can be managed through the Restate UI, the CLI, or the Admin REST API:
# Register a deployment
restate deployments register http://localhost:9080
# Get detailed information about a specific deployment
restate deployment describe <deployment_id>
# Check the registered deployments
restate deployments list
Some deployment targets support only HTTP/1.1. Use --use-http1.1 when registering these deployments.
For AWS Lambda, use the function ARN instead of a URL. See AWS Lambda deployment.
Avoid long-running handlers (days or months) - otherwise you need to keep old deployments around until all invocations complete. Instead, break work into smaller chunks using delayed calls.

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 Operator git repo.

Manual versioning

For non-FaaS deployments, you can use either the UI or the CLI to manage deployments. The typical update flow looks like this:
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:
CLI
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.
# Get detailed information about a specific deployment
# including the list of active invocations
restate deployment describe <deployment_id> --extra
4

Remove old deployment

Once all invocations are complete, you can safely remove the old deployment via the UI or CLI.
restate deployments remove <deployment_id>
If you need to force removal before the deployment is fully drained, use the --force flag in CLI.
Virtual Object state persists across versions. Ensure your state schema remains backward compatible.

Local development

During local development, you’re iterating quickly on your code and don’t need immutable deployments. You can safely re-register the same endpoint after code changes using the --force flag:
restate deployments register --force localhost:9080
This overwrites the existing deployment registration, allowing Restate to discover your updated service definition.
In-flight invocations might keep failing with non-determinism errors, but this is typically fine during development.You can kill all the in-flight invocations to a service using either CLI or UI:
CLI
restate invocations kill <SERVICE_NAME>

Advanced operations and troubleshooting

This section covers common scenarios you may encounter when managing deployments, and some of the troubleshooting best practices we recommend.

Journal mismatch errors

Restate performs journal compatibility checks during replay to prevent corruption. When you see a journal mismatch error (RT0016), it means the code executed during replay has produced a different journal than the original execution. This is typically caused by two scenarios: Non-deterministic code: Code that produces different results on each execution, even with the same inputs. Examples of non-deterministic operations:
  • External operations such as HTTP requests
  • Random value generation: Math.random(), uuid.v4(), etc.
  • Getting the current time or date: new Date()
  • Iterating over unordered collections, such as hash maps
To record non-deterministic operation results for replay, you need to record its results using ctx.run. For more info, see the durable steps documentation: TypeScript, Python, Java/Kotlin, Go. In-place code changes: Modifying deployed code at the same endpoint. This violates the immutable deployment principle and can cause in-flight invocations to fail when they replay with the updated code. Unsafe changes include:
  • Reordering Restate SDK operations (run, state access, service calls, awakeables)
  • Adding or removing SDK operations in the execution path
  • Changing operation inputs (state keys, service call payloads)
  • Modifying conditional logic that affects which operations execute
See the sections above for deployment best practices and the sections below for how to fix in-flight invocations.

Fixing a bug by updating deployed code

If you have a bug in your deployment code, sometimes it is safe to fix it by updating the deployment in-place.
ChangeSafe?
Fixing a bug inside ctx.run
Fixing a bug that consistently reproduces (e.g. deserialization error)
Changing the order of Restate operations, or adding/removing them
Changing operation inputs (state keys, call payloads, etc.)
This approach doesn’t work on FaaS platforms (e.g. Lambda) or with the Kubernetes Operator, since these use immutable deployments by design. In those cases, use pause and resume instead.

Pause invocations and resume on a new deployment

This is the recommended approach for fixing bugs affecting in-flight invocations. It works on all platforms, including FaaS and Kubernetes. When a bug affects in-flight invocations, they remain pinned to the original deployment. Registering a new deployment with a fix only helps new invocations. To fix existing invocations:
1

Register a new deployment with the fix

Deploy your fixed code and register it with Restate:
restate deployments register http://greeter-v2/
2

Pause the affected invocations

restate invocations pause <invocation_id>
3

Resume on the new deployment

restate invocations resume <invocation_id> --deployment <new_deployment_id>
The fix must be compatible with already-executed journal entries so they can replay successfully. If the business logic differs, the invocation will fail with non-determinism errors. For more details, see managing invocations.

Reassigning a deployment endpoint

This is an advanced technique. In most cases, use pause and resume instead.
You can use the Admin API to change which endpoint a deployment points to. This is useful when you’ve deployed a fix to a new URL and want stuck invocations to use it:
curl -X PUT localhost:9070/deployments/dp_14LsPzGz9HBxXIeBoH5wYUh \
  --json '{"uri": "http://greeter-patched/"}'
The same determinism rules apply: the fix must be compatible with already-executed journal entries.

Updating a service interface

When you register a new deployment, Restate validates that it doesn’t break existing service interfaces: adding handlers is allowed, but removing or renaming them is not. For example, given you register a new deployment for the already existing service Greeter, renaming its handler from greet to sayGreet is a breaking change, thus registration will fail. To allow breaking changes, use the --breaking flag:
restate deployments register --breaking http://greeter-v2/
Before using --breaking, make sure all callers have been updated to use the new service interface.

Removing a service

In Restate to remove a service, you must remove the deployment that contains it. 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 CLI:
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.