Skip to content

PgWorkflows - Durable workflows built on PostgreSQL

PgWorkflows lets you build long-running, scalable, durable workflows with the architecture you already have. You don’t need to host additional software or adopt a separate ecosystem; Postgres handles the durability.

PgWorkflows supports the orchestration patterns you’d expect, including fan-in fan-out, durable sleep, and signals.

example.cs
[Workflow("trial-onboarding")]
public sealed class TrialOnboardingWorkflow
{
[WorkflowRun]
public async ValueTask<string> RunAsync(
IWorkflowContext ctx,
SignupInput input,
CancellationToken cancellationToken
)
{
// Fan-out: run independent activities in parallel.
var (workspace, _) = await ctx.WhenAll(
ctx.CallActivity((OnboardingActivities a) => a.ProvisionWorkspace(input.Company)),
ctx.CallActivity((EmailActivities a) => a.SendWelcome(input.Email)),
cancellationToken
);
// Durable timer: the run is parked in Postgres. It survives
// crashes and restarts, and no worker holds it in memory.
await ctx.Sleep(TimeSpan.FromDays(11), cancellationToken);
await ctx.Activity(
(EmailActivities a) => a.SendTrialEndingReminder(input.Email),
cancellationToken
);
// Human-in-the-loop: park again until an external signal arrives.
var decision = await ctx.WaitForSignal<UpgradeDecision>("upgrade", cancellationToken);
if (!decision.Upgraded)
{
await ctx.Activity(
(OnboardingActivities a) => a.DowngradeToFreeTier(workspace.Id),
cancellationToken
);
return $"{input.Company} stayed on the free tier.";
}
await ctx.Activity(
(BillingActivities a) => a.StartSubscription(workspace.Id, decision.Plan),
cancellationToken
);
return $"{input.Company} upgraded to {decision.Plan}.";
}
}
public sealed record SignupInput(string Company, string Email);
public sealed record UpgradeDecision(bool Upgraded, string Plan);

The following resources cover how PgWorkflows works and how to use it in your projects.

Get started

Follow the get started guide to create your first workflow.