Skip to main content
Version: 2.0 prerelease

2.0 Prerelease Quickstart

2.0 prerelease

This page is for evaluating the Durable Workflow 2.0 release candidate. Stable 1.x remains the default public documentation line. Use this guide only when you are intentionally testing 2.0 prerelease packages and images.

This path uses published artifacts only:

SurfaceArtifact
Standalone serverdurableworkflow/server:0.2.363
CLIdw 0.1.77
Python SDKdurable-workflow==0.4.85
Laravel packagedurable-workflow/workflow:2.0.0-alpha.200@alpha
Waterlinedurable-workflow/waterline:2.0.0-alpha.84@alpha

Start A Local Server

Use this server for the Python and operator paths. It runs the published server image with SQLite, so no source checkout, MySQL, or Redis setup is required.

export DW_SERVER_IMAGE=durableworkflow/server:0.2.363
export DW_AUTH_TOKEN=dev-token

docker volume create durable-workflow-quickstart

docker run --rm \
-v durable-workflow-quickstart:/app/database \
-e DW_AUTH_DRIVER=token \
-e DW_AUTH_TOKEN="$DW_AUTH_TOKEN" \
"$DW_SERVER_IMAGE" server-bootstrap

docker rm -f durable-workflow-server >/dev/null 2>&1 || true
docker run -d --name durable-workflow-server \
-p 8080:8080 \
-v durable-workflow-quickstart:/app/database \
-e DW_AUTH_DRIVER=token \
-e DW_AUTH_TOKEN="$DW_AUTH_TOKEN" \
"$DW_SERVER_IMAGE"

until curl -sf http://localhost:8080/api/ready >/dev/null; do sleep 1; done
curl -H "Authorization: Bearer $DW_AUTH_TOKEN" \
http://localhost:8080/api/cluster/info

Python User

Create a clean project, install the published Python SDK, then run one workflow to completion against the local server.

mkdir durable-workflow-python-quickstart
cd durable-workflow-python-quickstart

python3 -m venv .venv
. .venv/bin/activate
pip install durable-workflow==0.4.85

Create greeter.py:

cat > greeter.py <<'PY'
import asyncio
import time

from durable_workflow import Client, Worker, activity, workflow


@activity.defn(name="quickstart.greet")
async def greet(name: str) -> dict:
return {"greeting": f"Hello, {name}!", "length": len(name)}


@workflow.defn(name="quickstart.greeter")
class GreeterWorkflow:
def run(self, ctx, name):
result = yield ctx.schedule_activity("quickstart.greet", [name])
return result


async def main():
workflow_id = f"quickstart-python-greeter-{int(time.time())}"

async with Client(
"http://localhost:8080",
token="dev-token",
namespace="default",
) as client:
handle = await client.start_workflow(
workflow_type="quickstart.greeter",
task_queue="quickstart-python",
workflow_id=workflow_id,
input=["Python"],
)

worker = Worker(
client,
task_queue="quickstart-python",
workflows=[GreeterWorkflow],
activities=[greet],
)

await worker.run_until(workflow_id=workflow_id, timeout=30.0)
result = await handle.result(timeout=10.0)

print(f"workflow_id={workflow_id}")
print("status=completed")
print(f"result={result}")


asyncio.run(main())
PY

python greeter.py

The successful output includes status=completed, the workflow id, and the activity result. Keep the printed workflow id for the operator check below.

Operator User

Install the published CLI, point it at the same local server, then inspect the completed Python workflow.

curl -fsSL https://durable-workflow.com/install.sh | VERSION=0.1.77 sh
export PATH="$HOME/.local/bin:$PATH"

dw env:set local \
--server=http://localhost:8080 \
--token=dev-token \
--namespace=default \
--make-default

dw doctor
dw server:health
dw server:info --output=json

Set QUICKSTART_WORKFLOW_ID to the id printed by python greeter.py, then capture its current run id and read the durable completed state:

export QUICKSTART_WORKFLOW_ID=quickstart-python-greeter-0000000000

dw workflow:describe "$QUICKSTART_WORKFLOW_ID" --output=json \
| tee quickstart-workflow.json
export QUICKSTART_RUN_ID="$(
python -c 'import json; print(json.load(open("quickstart-workflow.json"))["run_id"])'
)"

dw workflow:history "$QUICKSTART_WORKFLOW_ID" "$QUICKSTART_RUN_ID" --output=json
dw workflow:list --status=completed --output=json

An operator-only CLI session can verify server readiness, worker registration, task queues, and completed runs. It cannot complete a user workflow by itself: completion requires a worker process that implements the workflow type, such as the Python worker above or a Laravel queue worker below.

Laravel User

Create a fresh Laravel app, install the published prerelease packages, define one workflow and one activity, then let the queue worker complete the run.

composer create-project laravel/laravel durable-workflow-laravel-quickstart
cd durable-workflow-laravel-quickstart

composer require \
durable-workflow/workflow:2.0.0-alpha.200@alpha \
durable-workflow/waterline:2.0.0-alpha.84@alpha
composer show durable-workflow/workflow
composer show durable-workflow/waterline

printf '\nQUEUE_CONNECTION=database\nWATERLINE_ALLOW_UNAUTHENTICATED=true\n' >> .env
php artisan key:generate
php artisan waterline:install
php artisan migrate

Create the workflow and activity:

mkdir -p app/Workflows/Quickstart

cat > app/Workflows/Quickstart/WelcomeActivity.php <<'PHP'
<?php

namespace App\Workflows\Quickstart;

use Workflow\V2\Activity;
use Workflow\V2\Attributes\Type;

#[Type('quickstart.laravel.welcome-activity')]
class WelcomeActivity extends Activity
{
public function handle(string $name): string
{
return "Hello, {$name}!";
}
}
PHP

cat > app/Workflows/Quickstart/WelcomeWorkflow.php <<'PHP'
<?php

namespace App\Workflows\Quickstart;

use Workflow\V2\Attributes\Type;
use Workflow\V2\Workflow;

use function Workflow\V2\activity;

#[Type('quickstart.laravel.welcome')]
class WelcomeWorkflow extends Workflow
{
public function handle(string $name): string
{
return activity(WelcomeActivity::class, $name);
}
}
PHP

Create an artisan command that starts the workflow and waits for a terminal state:

mkdir -p app/Console/Commands

cat > app/Console/Commands/RunQuickstartWorkflow.php <<'PHP'
<?php

namespace App\Console\Commands;

use App\Workflows\Quickstart\WelcomeWorkflow;
use Illuminate\Console\Command;
use Workflow\V2\WorkflowStub;

class RunQuickstartWorkflow extends Command
{
protected $signature = 'app:quickstart-workflow';

protected $description = 'Run the Durable Workflow quickstart workflow.';

public function handle(): int
{
$workflow = WorkflowStub::make(
WelcomeWorkflow::class,
'quickstart-laravel-'.now()->format('YmdHis')
);

$workflow->start('Laravel');
$deadline = now()->addMinutes(10);

while ($workflow->refresh()->running()) {
if (now()->greaterThan($deadline)) {
$this->error('status=timeout');
$this->line('workflow_id='.$workflow->workflowId());

return self::FAILURE;
}

usleep(100_000);
}

$this->line('workflow_id='.$workflow->workflowId());
$this->line('status='.$workflow->status());
$this->line('output='.$workflow->output());

return $workflow->completed() ? self::SUCCESS : self::FAILURE;
}
}
PHP

Run the worker and start the workflow, capturing the command output and elapsed time:

set -o pipefail

php artisan queue:work --tries=1 --timeout=60 \
> storage/logs/quickstart-worker.log 2>&1 &
export QUICKSTART_QUEUE_PID=$!
trap 'kill "$QUICKSTART_QUEUE_PID" 2>/dev/null || true' EXIT

SECONDS=0
php artisan app:quickstart-workflow 2>&1 | tee quickstart-laravel-output.log
printf 'elapsed_seconds=%s\n' "$SECONDS"

if [ -s storage/logs/quickstart-worker.log ]; then
sed -n '1,120p' storage/logs/quickstart-worker.log
fi

kill "$QUICKSTART_QUEUE_PID" 2>/dev/null || true
trap - EXIT

The successful output includes status=completed and output=Hello, Laravel!. To inspect the operator view, run php artisan serve and open /waterline.

Completion Criteria

Before tearing the quickstart down, verify each path reached an observable state from published artifacts:

PathObservable proof
Local server hostingcurl http://localhost:8080/api/ready succeeds, GET /api/cluster/info returns the standalone server topology, and the Python workflow below completes against that server.
Python userpython greeter.py prints status=completed, a quickstart-python-greeter-* workflow id, and the activity result.
Operator userdw workflow:describe "$QUICKSTART_WORKFLOW_ID" --output=json shows a run_id, dw workflow:history "$QUICKSTART_WORKFLOW_ID" "$QUICKSTART_RUN_ID" --output=json shows history for that run, and dw workflow:list --status=completed --output=json shows the completed Python workflow on the same server profile.
Laravel usercomposer show durable-workflow/workflow and composer show durable-workflow/waterline print the resolved published package versions, and php artisan app:quickstart-workflow prints status=completed, output=Hello, Laravel!, and elapsed_seconds=<10 minute value> while the Laravel queue worker is running.

Clean Up

docker rm -f durable-workflow-server
docker volume rm durable-workflow-quickstart

For deeper guides, continue to Python SDK, Server, CLI, and Waterline Operator API.