Version v0.1 · dotnet
Getting Started
Detailed setup with explicit C# configuration and the most common runtime options.
Getting Started
This guide expands on Quick Start and focuses on the most common production options.
All runtime configuration is set in C#.
Most teams can rely on defaults for many options. This example sets them explicitly so the behavior is easy to understand.
Recommended baseline setup
using DurableStack.Core.Options;
using DurableStack.Hosting.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
var connectionString = "Host=localhost;Port=5432;Database=durable_stack;Username=postgres;Password=postgres";
var workerName = $"orders-api-{Environment.MachineName}-{Environment.ProcessId}";
builder.Services.AddDurableStackPostgres(connectionString, options =>
{
options.JobActivation = DurableStackJobActivationMode.ScopedPerExecution; // Default: ScopedPerExecution
options.WorkerName = workerName; // Default: "{HOSTNAME-or-machine}-{processId}"
options.PollIntervalSeconds = 5; // Default: 5
options.BatchSize = 50; // Default: 50
options.MaxConcurrentRuns = 10; // Default: 5
options.LeaseDurationSeconds = 30; // Default: 30
options.RetryDelay = TimeSpan.FromSeconds(5); // Default: 00:00:05
options.RetryMaxDelay = TimeSpan.FromMinutes(60); // Default: 01:00:00
options.RetryJitterEnabled = true; // Default: false
options.RetryJitterRatio = 0.20; // Default: 0.2
options.Recurring.CatchUpPolicy = RecurringCatchUpPolicy.SkipMissed; // Default: SkipMissed
options.Recurring.RegistrationSync.ExistingJobBehavior = ExistingRecurringJobBehavior.KeepDatabase; // Default: KeepDatabase
options.Recurring.RegistrationSync.OrphanedJobBehavior = OrphanedRecurringJobBehavior.Disable; // Default: Disable
options.Retention.Enabled = true; // Default: true
options.Retention.RunRetentionSeconds = 86400; // Default: null (effective 86400 for durable DB providers)
options.Retention.SweepIntervalSeconds = 300; // Default: 300
options.Retention.DeleteBatchSize = 1000; // Default: 1000
options.Eventing.TenantId = null; // Default: null
options.Eventing.ClientSecret = null; // Default: null
});
Option groups that matter most
Worker and throughput
WorkerName(default:"{HOSTNAME-or-machine}-{processId}"): unique identity used for lease ownership and diagnostics.PollIntervalSeconds(default:5): how often workers check for available runs.BatchSize(default:50): maximum runs claimed per poll cycle.MaxConcurrentRuns(default:5): concurrency limit per worker instance.LeaseDurationSeconds(default:30): ownership timeout before another worker can recover a stalled run.
Retry behavior
RetryDelay(default:00:00:05): baseline delay between attempts.RetryMaxDelay(default:01:00:00): cap for backoff growth.RetryJitterEnabled(default:false) andRetryJitterRatio(default:0.2): spread retry pressure across time.
Recurring behavior
Recurring.CatchUpPolicy(default:SkipMissed): avoids large catch-up bursts after downtime.ExistingJobBehavior(default:KeepDatabase): keeps existing schedule definitions stable by default.OrphanedJobBehavior(default:Disable): safely disables schedules removed from code.
Retention
Retention.Enabled(default:true): enables cleanup for terminal runs.RunRetentionSeconds(default:null, effective86400for durable DB providers and3600for in-memory): how long completed/failed runs remain queryable.SweepIntervalSeconds(default:300) andDeleteBatchSize(default:1000): cleanup cadence and batch sizing.
Optional hosted observability
Eventing.TenantId(default:null) andEventing.ClientSecret(default:null): enable ingestion when both are set.
Defaults-first guidance
In most projects, you only need to set:
- provider selection and connection string
- optional
WorkerNameconvention - any non-default values that match your workload goals
Everything else can stay at defaults until you have data that justifies tuning.
What to tune first in production
- Increase
MaxConcurrentRunsandBatchSizecarefully as workload grows. - Keep
LeaseDurationSecondslonger than normal job execution for your slowest routine jobs. - Use retry jitter to reduce synchronized retries during external outages.
- Set retention windows based on audit/debug needs and database cost profile.
Full options reference
using DurableStack.Core.Options;
using DurableStack.Hosting.DependencyInjection;
builder.Services.AddDurableStack(options =>
{
options.StorageProvider = DurableStackStorageProvider.InMemory; // Backing store selection when using property-based config; default: InMemory.
options.Postgres.ConnectionString = "Host=localhost;Port=5432;Database=durable_stack;Username=postgres;Password=postgres"; // PostgreSQL connection string used when StorageProvider is Postgres; default: "".
options.SqlServer.ConnectionString = "Server=localhost;Database=durable_stack;Trusted_Connection=True;TrustServerCertificate=True"; // SQL Server connection string used when StorageProvider is SqlServer; default: "".
options.Sqlite.ConnectionString = "Data Source=durable_stack.db"; // SQLite connection string used when StorageProvider is Sqlite; default: "".
options.MySql.ConnectionString = "Server=localhost;Port=3306;Database=durable_stack;Uid=root;Pwd=password"; // MySQL connection string used when StorageProvider is MySql; default: "".
options.JobActivation = DurableStackJobActivationMode.ScopedPerExecution; // Job activation model (ScopedPerExecution or RootProvider); default: ScopedPerExecution.
options.WorkerName = DurableStackOptions.CreateDefaultWorkerName(); // Unique worker identity for leasing and diagnostics; default: "{HOSTNAME-or-machine}-{processId}".
options.DatabaseTablePrefix = null; // Optional prefix for durable database objects; default: null.
options.PollInterval = TimeSpan.FromSeconds(5); // Polling cadence for fetching available work; default: 00:00:05.
options.PollIntervalSeconds = 5; // Same as PollInterval but in seconds (<= 0 resets to default 5); default: 5.
options.BatchSize = 50; // Max runs leased per poll cycle; default: 50.
options.LeaseDuration = TimeSpan.FromSeconds(30); // Lease TTL before another worker can recover stalled runs; default: 00:00:30.
options.LeaseDurationSeconds = 30; // Same as LeaseDuration but in seconds (<= 0 resets to default 30); default: 30.
options.RetryDelay = TimeSpan.FromSeconds(5); // Base retry delay for failed attempts; default: 00:00:05.
options.RetryMaxDelay = TimeSpan.FromHours(1); // Retry backoff upper bound; default: 01:00:00.
options.RetryJitterEnabled = false; // Enables random jitter on retry delays; default: false.
options.RetryJitterRatio = 0.2; // Jitter ratio applied when RetryJitterEnabled is true; default: 0.2.
options.Eventing.TenantId = null; // Tenant ID for hosted ingestion auth; default: null.
options.Eventing.ClientSecret = null; // Client secret for hosted ingestion auth; default: null.
options.Eventing.IngestionApiBaseUrl = "https://api.durablestack.com"; // Base URL for event ingestion API; default: https://api.durablestack.com.
options.Eventing.IngestionPath = "/v1/events/batch"; // Relative ingestion endpoint path; default: /v1/events/batch.
options.Eventing.IngestionMaxBatchSize = 100; // Max events sent per ingestion request; default: 100.
options.Eventing.IngestionMaxRequestBodyBytes = 1_000_000; // Max request body size per ingestion post; default: 1,000,000.
options.Eventing.IngestionMaxRetryAttempts = 5; // Max retry attempts for failed ingestion posts; default: 5.
options.Eventing.IngestionFlushInterval = TimeSpan.FromSeconds(5); // Flush interval for telemetry publishing; default: 00:00:05.
options.Eventing.IngestionFlushIntervalSeconds = 5; // Same as IngestionFlushInterval in seconds (<= 0 resets to default 5); default: 5.
options.Eventing.IncludeErrorDetail = false; // Includes captured error detail text in events when true; default: false.
options.Eventing.MaxErrorDetailLength = 4096; // Max captured error-detail characters per event (<= 0 uses 4096); default: 4096.
options.Recurring.CatchUpPolicy = RecurringCatchUpPolicy.SkipMissed; // Recurring schedule behavior after downtime (SkipMissed or CatchUp); default: SkipMissed.
options.Recurring.RegistrationSync.ExistingJobBehavior = ExistingRecurringJobBehavior.KeepDatabase; // Existing recurring definitions: keep DB values or update from code; default: KeepDatabase.
options.Recurring.RegistrationSync.OrphanedJobBehavior = OrphanedRecurringJobBehavior.Disable; // Jobs removed from code: disable or ignore in DB; default: Disable.
options.Retention.Enabled = true; // Enables automatic cleanup of terminal runs; default: true.
options.Retention.RunRetentionSeconds = null; // Terminal run retention window in seconds; default: null (effective default = 3600 in-memory, 86400 durable stores).
options.Retention.SweepIntervalSeconds = 300; // Retention cleanup sweep interval in seconds (<= 0 uses 300); default: 300.
options.Retention.DeleteBatchSize = 1000; // Max rows removed per retention cleanup batch (<= 0 uses 1000); default: 1000.
options.JobRegistration.AutoDiscoverJobsFromAssembly = true; // Auto-registers public IDurableJob/IDurableJob<TArgs> from the app assembly; default: true.
});
Store helper methods
Use one of these helpers inside the same AddDurableStack callback when you prefer method-based store selection.
builder.Services.AddDurableStack(options =>
{
options.UseInMemory(); // Selects in-memory store; default if no store is selected.
// options.UsePostgres("Host=..."); // Selects PostgreSQL store and sets connection string; use only one Use* store method at a time (last call wins).
// options.UseSqlServer("Server=..."); // Selects SQL Server store and sets connection string; use only one Use* store method at a time (last call wins).
// options.UseSqlite("Data Source=..."); // Selects SQLite store and sets connection string; use only one Use* store method at a time (last call wins).
// options.UseMySql("Server=..."); // Selects MySQL store and sets connection string; use only one Use* store method at a time (last call wins).
});
Notes
- Setting both
PollIntervalandPollIntervalSecondsis redundant; use one style. - Setting both
LeaseDurationandLeaseDurationSecondsis redundant; use one style. - Setting both
Eventing.IngestionFlushIntervalandEventing.IngestionFlushIntervalSecondsis redundant; use one style. - Event ingestion is automatically enabled when both
Eventing.TenantIdandEventing.ClientSecretare set.