| title | Durable Workflow Python SDK | |
|---|---|---|
| description | Install the first-party Python SDK and run a durable workflow on self-hosted Server or a managed Cloud runtime. | |
| hide |
|
First-party Python SDK · 2.0 stable
Define workflows and activities, run an async worker, and start durable work from Python. Begin with self-hosted Server or connect an existing managed Cloud namespace.
Python 3.10+Async-firstFully typed
pip install durable-workflowUse a virtual environment and lock the resolved package version with the rest of your application dependencies.
A client asks the runtime to do durable work. A worker receives tasks and dispatches them to your workflow and activity code.
The workflow types, activity types, and task queue stay the same. Only the endpoint, credentials, and operating boundary change.
Run the published Server image locally, then execute the complete Python journey below.
Start locally → Managed runtime · limited accessConnect the same program to a provisioned namespace with separate client and worker credentials.
This source-free path runs one Python file containing an activity, workflow, worker, and client against the stable Server channel.
Docker keeps this first run local. Select the stable 2.x Server channel:
export DW_SERVER_IMAGE='durableworkflow/server:2'Then bootstrap and start that qualified image:
export DURABLE_WORKFLOW_RUNTIME_URL='http://127.0.0.1:8080'
export DURABLE_WORKFLOW_RUNTIME_NAMESPACE='default'
export DURABLE_WORKFLOW_TOKEN='local-python-example-token'
docker volume create durable-workflow-python
docker run --rm -v durable-workflow-python:/app/database \
-e DW_AUTH_DRIVER=token -e DW_AUTH_TOKEN="$DURABLE_WORKFLOW_TOKEN" \
"$DW_SERVER_IMAGE" server-bootstrap
docker rm -f durable-workflow-python-server >/dev/null 2>&1 || true
docker run -d --name durable-workflow-python-server -p 8080:8080 \
-v durable-workflow-python:/app/database \
-e DW_AUTH_DRIVER=token -e DW_AUTH_TOKEN="$DURABLE_WORKFLOW_TOKEN" \
"$DW_SERVER_IMAGE"
until curl -sf http://127.0.0.1:8080/api/ready >/dev/null; do sleep 1; doneThe named constants make the authoring contract visible: the decorator and start call share a workflow type, the workflow and decorator share an activity type, and the client and worker share one task queue. Values cross those boundaries with the supported Avro authoring codec.
import asyncio
import logging
import os
from uuid import uuid4
from durable_workflow import Client, Worker, activity, workflow
WORKFLOW_TYPE = "python.greeter"
ACTIVITY_TYPE = "python.greet"
TASK_QUEUE = "python-workers"
@activity.defn(name=ACTIVITY_TYPE)
def greet(name: str) -> str:
return f"Hello, {name}!"
@workflow.defn(name=WORKFLOW_TYPE)
class GreeterWorkflow:
def run(self, ctx, name):
return (yield ctx.schedule_activity(ACTIVITY_TYPE, [name]))
async def main() -> None:
logging.basicConfig(level=logging.INFO, format="%(message)s")
async with Client(
os.environ["DURABLE_WORKFLOW_RUNTIME_URL"],
token=os.getenv("DURABLE_WORKFLOW_TOKEN"),
control_token=os.getenv("DURABLE_WORKFLOW_CLIENT_TOKEN"),
worker_token=os.getenv("DURABLE_WORKFLOW_WORKER_TOKEN"),
namespace=os.environ["DURABLE_WORKFLOW_RUNTIME_NAMESPACE"],
) as client:
worker = Worker(
client,
task_queue=TASK_QUEUE,
workflows=[GreeterWorkflow],
activities=[greet],
)
handle = await client.start_workflow(
workflow_type=WORKFLOW_TYPE,
workflow_id=f"greeting-{uuid4().hex}",
task_queue=TASK_QUEUE,
input=["world"],
)
await worker.run_until(workflow_id=handle.workflow_id, timeout=30.0)
print(await handle.result(timeout=10.0))
asyncio.run(main())python greeter.pyThe SDK reports registration before it handles the workflow, followed by the completed result:
worker py-worker-… registered on python-workers
Hello, world!
Cloud provisioning returns a namespace-scoped runtime URL and namespace value.
Pass that complete runtime URL unchanged; do not invent or append an /api
suffix because the SDK adds its own routes.
- Control-plane API keyCreates and administers Cloud resources and runtime credentials. It is not passed to the Python SDK runtime client.
- Runtime client tokenStarts and controls workflows in one namespace. Pass it through
DURABLE_WORKFLOW_CLIENT_TOKEN, which maps tocontrol_token=. - Runtime worker tokenRegisters, polls, heartbeats, and completes work in that namespace. Pass it through
DURABLE_WORKFLOW_WORKER_TOKEN, which maps toworker_token=.
Replace the placeholders with values returned for your namespace, then run the
same greeter.py. Keep client and worker tokens in their respective processes
when you split the example for production.
export DURABLE_WORKFLOW_RUNTIME_URL='<provisioned-runtime-url>'
export DURABLE_WORKFLOW_RUNTIME_NAMESPACE='<provisioned-runtime-namespace>'
export DURABLE_WORKFLOW_CLIENT_TOKEN='<runtime-client-token>'
export DURABLE_WORKFLOW_WORKER_TOKEN='<runtime-worker-token>'
unset DURABLE_WORKFLOW_TOKEN
python greeter.pyStable 2.x SDK releases follow semantic versioning and negotiate runtime capabilities with Server at startup. Lock the resolved Python package version and Server image digest in production builds.