Skip to main content
Kubernetes is a common choice for running Restate services in production environments. This page explains how to deploy Restate applications on Kubernetes with Restate Operator. The operator lets you:

What is the Restate Operator?

The Restate Operator is the recommended way to deploy Restate services on Kubernetes. It manages the Kubernetes workloads for your services and handles registration with Restate and versioning.
restatedev/restate-operator
Loading repository data...
The operator extends Kubernetes with resources for running Restate and deploying your service applications:

What does the operator do for you?

For service deployments, the operator:
  • Deploys your application with ReplicaSets or Knative Serving
  • Registers its SDK endpoint with Restate Cloud, BYOC, or a self-hosted Restate environment
  • Creates a new service revision when your pod template changes
  • Keeps old revisions available until their invocations have drained
  • Establishes a secure outbound tunnel when connecting private services to Restate Cloud
In ReplicaSet mode, the operator keeps old ReplicaSets and their Service objects available while in-flight invocations drain. In Knative mode, it manages Configurations and Routes for each service version. Learn more.
Operator 3 introduced Helm-managed CRD upgrades. The first upgrade to version 3 requires a one-time CRD ownership handoff. Follow the operator 3 upgrade instructions before upgrading an existing installation.

Deploy a service to Restate Cloud or BYOC

Restate Cloud must be able to send invocations to your SDK endpoint. For services in a private Kubernetes cluster, you use the Restate Cloud Tunnel to set up a secure connection that does not require exposing a public ingress.
To try this end to end, connect a greeter service on a local kind cluster to Restate Cloud by following this guide.
First, choose a tunnel mode and then follow the corresponding instructions.

Choose a tunnel mode to connect to Restate Cloud

You can run the tunnel client in your application process or as a standalone deployment: We recommend the in-process tunnel client for TypeScript and Go because it removes the extra network hop. Use the standalone tunnel client for other SDKs or when you prefer to operate the client independently from your application.
Each application pod maintains an outbound connection to Restate Cloud. Invocations arrive directly at the SDK handler without a Kubernetes Service or standalone tunnel client pod in the invocation path. The operator injects the environment, region, signing key, and versioned tunnel name through RESTATE_INPROC_* environment variables.
The RestateCloudEnvironment runs the tunnel client for you, as a pod in your cluster. It forwards each invocation through a Kubernetes Service to your application pods.The tunnel client opens an outbound connection to Restate Cloud, so your service needs no public ingress and no inbound ports.
  1. Connect. The tunnel client resolves the tunnel servers for your region and dials out to them, authenticating with your API key. It holds one connection per tunnel server and redials on its own if a connection drops.
  2. Register. Each connection is keyed by your environment and tunnel name. The deployment URL you register encodes both, plus the address the tunnel client should forward to.
  3. Invoke. Restate Cloud sends discovery and invocation requests for that deployment to the tunnel server, which streams them down one of the connections registered under that tunnel name.
  4. Forward. The tunnel client forwards each request to your service’s endpoint inside your network, and responses stream back over the same connection.
Requests are signed with your environment’s request identity key, so your service only accepts requests that genuinely came from your environment.Run several tunnel clients with the same tunnel name for redundancy. The tunnel server load balances invocations across every connection registered under that name, so a client going away does not take the deployment offline.
Restate Cloud reaching a private service through a tunnel client that holds an outbound connection
Both modes use a RestateCloudEnvironment. The tunnel mode is selected for each RestateDeployment, so services using different modes can share the same Cloud environment resource.

Deploy a service and connect to Restate Cloud

Choose the tunnel mode you want to use and follow the corresponding instructions:
Use the in-process tunnel client to connect each application pod directly to Restate Cloud without exposing an inbound endpoint. This is the recommended path for TypeScript and Go services.
1

Install the Restate Operator

Install the Restate Operator via Helm:
To install the operator, you need permission to create namespaces and CRDs.
2

Create the Restate Cloud Secrets

Create an API key in Restate Cloud at Developers > API Keys > Create API Key. Save the key_ value in a file named token.The operator needs the key in the restate-operator namespace. The in-process tunnel client needs the same key in your service’s namespace because Kubernetes Secrets are namespace scoped:
3

Create a RestateCloudEnvironment

Create the environment manifest:
restate-cloud-environment.yaml
  • environmentId: copy it from the top left corner of the Restate Cloud UI. It starts with env_.
  • signingPublicKey: copy it from Developers > Security > HTTP endpoints. It starts with publickeyv1_.
  • region: use the identifier shown next to your environment ID, such as us or eu. BYOC environments can use a multi-label identifier such as <environment>.byoc.
For all available options, see the RestateCloudEnvironment specification as Pkl (recommended) or YAML.
A RestateCloudEnvironment deploys a standalone tunnel client pod by default. Services configured with tunnelMode: in-process connect directly from their application pods and do not use that pod in their invocation path.
4

Add the in-process tunnel client

Replace the normal SDK listener with the in-process tunnel client:
In TypeScript, connectTunnel(...) replaces restate.serve(...). In Go, tunnel.NewTunnel(srv).Start(...) replaces srv.Start(...).
In-process tunnel clients for the other Restate SDKs are coming. Check the SDK release notes for updates.
The in-process tunnel client is not supported with Knative mode.
5

Create the RestateDeployment

Create a RestateDeployment with tunnelMode: in-process and mount the API key Secret:
service-deployment.yaml
The operator injects the remaining RESTATE_INPROC_* configuration, registers the tunnel URL, and handles versioning.
6

Invoke your service

Go to the Restate Cloud UI’s Playground and send a request to your service.

Deploy a service to self-hosted Restate

Register your service with a self-hosted Restate environment. This example uses a RestateCluster resource named restate in the same Kubernetes cluster, so no Restate Cloud tunnel or Cloud API key is required.
To deploy a self-hosted Restate cluster with the operator, see Deploy Restate on Kubernetes. To try the complete setup on a local kind cluster, follow the operator guide.
1

Install the Restate Operator

Install the Restate Operator via Helm:
To install the operator, you need permission to create namespaces and CRDs.
2

Create the RestateDeployment

Create a RestateDeployment that references your RestateCluster named restate:
service-deployment.yaml
Once applied, the Restate Operator registers your service and handles versioning.
Read the RestateDeployment documentation, or view the full specification as Pkl or YAML.
3

Invoke your service

Go to the Restate UI’s Playground and send a request to your service.

Knative Deployment

The RestateDeployment CRD can use Knative Serving instead of ReplicaSets. The operator then manages the Knative Configurations, Routes, Restate registration, and service versions while Knative provides request-based autoscaling and scale-to-zero. Install Knative Serving before applying a Knative-mode RestateDeployment:
The example registers with a RestateCluster named restate. To register with Restate Cloud or BYOC, use cloud: my-cloud-environment instead. The optional knative.tag controls deployment identity:
  • Keep the same tag for an in-place update to the existing Restate deployment. Only do this when the change is compatible with invocations already assigned to that deployment.
  • Change the tag to create and register a new version while the previous version drains.
  • Omit the tag to use the pod template hash and create a new version for every template change.
The container port must be named h2c for HTTP/2 or http1 for HTTP/1.1. In-process Restate Cloud tunnels are not supported in Knative mode.
See the operator’s Knative Serving documentation and complete examples. To run on Knative without the operator, see the accordion under Direct Kubernetes deployments.

Horizontal scaling and load balancing

Restate allows scaling your services horizontally by running multiple replicas of your pods. To distribute traffic across these pods, you need to set up load balancing in between the Restate server and your service pods. This way, Restate gets a stable endpoint to send requests to, and the load balancer distributes the requests across the available pods.

Autoscale operator-managed revisions

In ReplicaSet mode, you can attach your own HorizontalPodAutoscaler to the RestateDeployment scale subresource to scale the latest revision. You can also configure spec.autoscaling so the operator creates an HPA for each older revision while its invocations drain:
The operator supplies the HPA’s scaleTargetRef. CPU and memory metrics require resource requests on your service containers. This setting applies only to draining revisions in ReplicaSet mode. Knative uses its own autoscaler. In a standard Kubernetes setup, you would typically use a Service of type ClusterIP or LoadBalancer to distribute traffic across your pods. Kubernetes Services distribute new connections across eligible pods. The exact behavior depends on your cluster’s networking implementation. If your services are running over HTTP/2 (the default), each Restate partition will generally have only one TCP connection to a single destination pod. However, because there are many partitions (by default 24, typically more in a larger cluster) this should get your pods a reasonably even distribution of traffic. For more advanced load balancing options, you can use an L7 load balancer like Istio or Cilium. Note that there is no need for routing to be sticky, because each invocation is self-contained and has all the context it needs to execute (the journal and any K/V state). No context is assumed to be sticky on the node. However, if you wish to do so, you can do sticky routing via the invocation ID in your request headers (x-restate-invocation-id).

Direct Kubernetes deployments

Use the Restate Operator when you want automatic registration, version management, and operator-managed scaling.
If you prefer not to use the Restate Operator, you can deploy your Restate services directly using standard Kubernetes Deployment and Service resources. A Kubernetes Deployment of more than one replica is generally appropriate, and a Kubernetes Service is used to provide a stable DNS name and IP for the pods. Here is an example manifest with a single pod in Kubernetes:
Once you have applied the manifest, register the service at http://<service>.<namespace>:9080. ⚠️ Note that this setup will not account for keeping around old code versions, so updating your code can break in-flight invocations. Check the versioning documentation for more information.
Restate services also run on a plain Knative Service, without the operator. There are no special container requirements beyond naming the port h2c:
Or as a manifest:
The service is reachable at http://<service-name>.<namespace>, but to handle versioning it is preferable to register the revision URL, such as http://<service-name>-0001.<namespace>, as part of your deployment workflow.Knative exposes the service through the Ingress by default. Restate does not require this, so you can pass --cluster-local to the creation command to disable it.
Learn more in this blog post and the Go example.