Skip to content

Durable workflows (Restate)

Restate is a durable execution runtime: workflow steps journaled on the server survive pod restarts, so side effects inside ctx.run are not replayed after a crash.

On Komodo, Restate runs as a per-app StatefulSet next to your workload. You opt in through the devfile; the data-apps Helm chart provisions Restate, network policies, Istio authorization, and automatic deployment registration so the runtime can call back into your app.

Two App Builder entry points:

  • restate=true on scaffold_app (typically with the fastapi template): adds restate-sdk, metadata.komodo.restate, an example restate_workflow.py, and patches main.py with a combined ASGI router (FastAPI for normal HTTP, Restate for protocol paths).
  • restate-agent template: a standalone Restate restate.app(...) served by Uvicorn on the container port (default 9080). The whole process is the Restate deployment endpoint; there is no FastAPI split.

Add (or keep) a restate block under metadata.komodo. The scaffold defaults match what App Builder injects:

metadata:
komodo:
restate:
enabled: true
storage: "10Gi"
version: "1.3"
dashboard:
enabled: true
  • enabled: turns on the per-app Restate StatefulSet and related chart resources.
  • storage: PVC size for Restate’s embedded RocksDB.
  • version: Restate server image tag (for example 1.3, aligned with docker.io/restatedev/restate).
  • dashboard.enabled: when combined with app exposure, exposes the Restate admin UI on a dedicated hostname (see your environment’s routing).

The platform deploys one Restate instance per app, colocated with the primary service (the primaryService in the devfile). Non-primary services do not get their own Restate StatefulSet; they receive RESTATE_INGRESS_URL and RESTATE_ADMIN_URL pointing at the primary’s Restate Service. If a sibling defines its own Restate handlers, it must register that service’s in-cluster URL with the shared Restate admin API (see Multi-container and registration).

When Restate is enabled, the chart injects:

VariablePurpose
RESTATE_INGRESS_URLIn-cluster base URL for invoking handlers (for example POST …/Workflow/{key}/start).
RESTATE_ADMIN_URLIn-cluster base URL for the admin API (for example POST …/deployments for registration).
RESTATE_DASHBOARD_URLPresent when dashboard and exposure are enabled: HTTPS URL for the admin UI in a browser.

Restate is the supported exception to “browser and containers only use public HTTPS”: pod-to-Restate traffic uses these in-cluster URLs. When calling Restate from Python with httpx, use trust_env=False and http2=False so traffic does not go through an HTTP proxy sidecar and does not attempt HTTP/2 against servers that speak HTTP/1.1 only.

Paths your app must expose (Restate → app)

Section titled “Paths your app must expose (Restate → app)”

After registration, the Restate runtime calls your app’s listen port (the same port as your HTTP server and Kubernetes Service) on paths expected by the Restate service protocol and the Python SDK. Your ASGI stack must forward these to restate.app(...), not to FastAPI, or Restate will never see discovery or invocations.

Required paths for the FastAPI + Restate scaffold (as implemented in komodo-internal-tools):

  • /discover — discovery during deployment registration. The scaffold matches this path exactly (path == "/discover"). Use this path literally (no extra path prefix on the app port unless your framework strips a global prefix before ASGI routing).
  • /invoke/ — any path starting with /invoke/ is routed to the Restate handler (service invocation protocol).
  • /{ServiceName}/ — HTTP-style paths for each registered service or virtual object (for example /Workflow/... for the default VirtualObject("Workflow")). For each new restate.VirtualObject("MySvc") or service, add that service to the scaffold’s _restate_services list in main.py so the ASGI router includes the /MySvc/ prefix. If you omit a service from that list, named paths fall through to FastAPI and return 404 or the wrong handler.

restate-agent template: Uvicorn serves restate.app(services=[...]) directly, so all protocol and service paths are handled by the SDK on the container port without a separate FastAPI router.

Health checks: keep /health (or your configured probe path) on your web framework for Kubernetes and for the chart’s postStart registration script, which polls local HTTP before calling Restate admin. Do not send liveness or readiness probes to Restate-only paths on the user container; the Restate pod has its own admin /health on port 9070.

NetworkPolicies allow Restate → app on the TCP app port as a whole; there is no separate Kubernetes route per path. Path handling is entirely application routing.

The data-apps chart runs a postStart hook on the user container that:

  1. Optionally waits for the Istio sidecar, then for your app to respond on http://127.0.0.1:<port><health-path>.

  2. Waits for Restate admin GET {RESTATE_ADMIN_URL}/health.

  3. POST {RESTATE_ADMIN_URL}/deployments with JSON body:

    {"uri":"http://<release-service>.<namespace>.svc.cluster.local:<app-port>","use_http_11":true,"force":true}

    The URI is the app’s Kubernetes ClusterIP Service DNS name, not the public apps hostname. Restate uses it for in-cluster callbacks to /discover and /invoke/....

Image requirements: the script tries wget first, then curl. Install at least one in the image or registration fails silently from kubelet’s perspective; diagnostics append to /tmp/restate-registration.log inside the container (kubectl exec and cat that file).

Helm values under restate.registration control wait attempts and delays (for example localHttpPath, waitForLocalHttp, restateAdminHealthWaitAttempts). See deploy/charts/data-apps/values.yaml in the data-apps Helm chart (compute-services) for defaults.

  • Sibling services automatically receive RESTATE_INGRESS_URL and RESTATE_ADMIN_URL; you do not add them manually to the devfile for that.
  • A sibling that only invokes workflows via RESTATE_INGRESS_URL needs httpx (or similar), not necessarily restate-sdk or ASGI patching.
  • A sibling that defines Restate handlers must combine ASGI routing like the primary, install restate-sdk, and register its own stable Service DNS and port with POST {RESTATE_ADMIN_URL}/deployments from application startup (with retries), because only the primary’s postStart runs the chart registration. Use the same JSON shape as the chart (uri, use_http_11, force).

Restate_StatefulSet

User_deployment

GET_/discover_POST_/invoke_*

registration_POST_/deployments

Uvicorn_ASGI

FastAPI

restate_SDK_handler

Restate_runtime