Configuration

Every default in one place, for jobs, workers, producers, stores, retention, the scheduler, the outbox relay and the fixed limits.

On this page

All configuration goes through AddRunwick(runwick => ...) and the store, outbox and management extensions. Times are as written. Runwick checks every value at startup and refuses a configuration it cannot run safely. It never quietly adjusts a value for you.

Per job

Set per job with an attribute or the fluent builder, or for all jobs with runwick.JobDefaults(...). Later layers win: library default, then JobDefaults, then attribute, then fluent.

SettingDefaultAttribute / fluent
Queuedefault[Queue("emails")] / .ToQueue("emails")
Prioritymedium (low, medium, high)[Priority("high")] / .WithPriority("high")
Maximum executions (counts real starts, so 3 means two automatic retries)3[MaxExecutions(5)] / .WithMaxExecutions(5)
Execution timeoutnone.WithExecutionTimeout(...) / .WithoutExecutionTimeout()
Retry delay (exponential, half random)5 s first, never more than 60 s.WithRetryDelay(initial, max)
Re-queue after a worker took a job but never started itup to 5 times.WithMaxUnstartedAbandonments(n)
Delay between those re-queues1 s growing to 30 s.WithUnstartedRecoveryDelay(initial, max)
Uniqueness, rate limits, concurrency limitsoff until you declare them.UniqueWhileActive(...), .RollingWindow(...), .MaxConcurrent(...) and others

With the defaults, the first retry waits between 2.5 and 5 seconds, the second between 5 and 10. See Retries and timeouts.

Text
runwick.JobDefaults(d => d.WithMaxExecutions(5).WithRetryDelay(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(2)));
runwick.AddJob<BuildReport>()
    .ToQueue("reports")
    .WithPriority("low")
    .WithExecutionTimeout(TimeSpan.FromMinutes(2));

Worker: WorkerOptions

Set with runwick.AddWorker(w => w.Configure(o => ...)). Most apps never touch these. They are tied together; see Startup validation.

OptionDefaultWhat it does
LeaseDuration60 sHow long a claim on a job lasts before it must be renewed
LeaseSafetyMargin10 sThe worker treats the lease as expired this much early
RenewalInterval / RenewalJitter15 s / 3 sRenew every 12 to 18 s
LeaseWatchdogInterval1 sHow often the safety watchdog checks leases (at most 1 s)
SchedulerAllowance2 sAllowance for timer lag
ControlRequestTimeout5 sLongest wait for one claim, renew or finish call
ControlRetryInitialBackoff / ControlRetryMaxBackoff100 ms / 1 sBackoff between retries of those calls
MinimumInvocationMargin15 sLease time that must be left to start a handler
ClaimResolutionBudget / BeginResolutionBudget15 s / 15 sHow long to resolve an uncertain claim or start
CompletionResolutionBudget30 sHow long to resolve an uncertain finish
CancellationGrace10 sTime a cancelled handler gets to exit
CleanupBudget10 sTime for the attempt's scope to dispose
OwnershipCommandLifetime30 sDeadline of each claim, renew or finish command
RecoveryScanInterval / RecoveryScanJitter5 s / 1 sScan for jobs of crashed workers every 4 to 6 s
ShutdownBudget25 sTotal time for Runwick's shutdown
ShutdownDrain / ShutdownCancellation / ShutdownFinalization15 s / 5 s / 5 sThe three shutdown stages; their sum must fit in ShutdownBudget
RecordResourceUsagetrueStore CPU time, allocations and working set per attempt

Keep the host's shutdown timeout (HostOptions.ShutdownTimeout, 30 s by default in .NET) above ShutdownBudget. Runwick does not change it for you.

Pools

SettingDefaultNotes
Pool sizefixed 4.FixedConcurrency(n) with n at least 1, or .AutoConcurrency(min, max); not both
Queue weight1.Queue(name, weight), 1 to 1,000
Lane selectionWeighted.LaneSelection(...): Weighted, StrictWithAging or Strict
Lane weights high : medium : low6 : 3 : 1.LaneWeights(h, m, l); a waiting lane ages after 60 s
Most jobs one worker holds at once16w.MaxOutstandingAttempts(n); the sum of pool maximums must fit

Producer: ProducerOptions

Set with runwick.Producer(o => ...).

OptionDefaultNotes
MaxConcurrentCalls8Dispatch calls in flight at once; at least 1. On PostgreSQL, raising it adds connections.
MaxWaitingOperations64Callers waiting for a slot; more fail with local_capacity_exceeded without sending anything
AdmissionWait1 sLongest wait for a slot
CommandLifetime5 minHow long a prepared dispatch stays valid; also the maximum

Stores

OptionDefaultAllowed
PostgresOptions.Schemarunwickonly runwick
SqliteOptions.WriterBusyTimeout2 s100 ms to 30 s
SqliteOptions.ReaderThreads21 to 8
RedisOptions.ProfileRecheckInterval30 s5 s to 5 min

See PostgreSQL, SQLite and Redis.

Retention

Cleanup runs in worker processes. Its budgets are set with w.Retention(r => ...):

RetentionOptionsDefaultAllowed
Enabledtrue
BatchRows1001 to 1,000
BatchDetailBytes1 MiB64 KiB to 16 MiB
BatchTarget500 ms50 ms to 2 s
BatchPause1 s0 to 60 s
IdlePoll60 s1 s to 1 h

How long records are kept is a stored policy per application, edited through the management API. The defaults:

RecordKept for
Detail of succeeded jobs7 days
Detail of failed jobs30 days
Job history90 days
Audit90 days
Scaling events7 days
Schedule occurrences30 days
Worker presence24 hours

Cleanup is bounded work, not a promise to keep up with any load. Deleting rows does not erase backups or copies. See Retention.

Scheduler: SchedulerOptions

Set with runwick.AddScheduler(o => ...). The scheduler needs the scheduler capability raised once per application, or startup fails with scheduler_not_active.

OptionDefault
PollInterval1 s while work is due
IdlePollInterval5 s when idle (15 s on PostgreSQL while its listener is healthy)
JobsPerSecond / Burst100 / 32 per scheduler process

Per schedule: misfire policy RunOnce with 60 s grace (up to 24 h); catch-up at most 1,000 jobs per episode and 7 days back. See Schedules.

Outbox relay: OutboxRelayOptions

OptionDefaultAllowed
Enabledtrue
MaxInFlight41 to 64
LeaseDuration / RenewalInterval60 s / 15 slease at least 5 s and at least 3 renewals
ProviderCallTimeout5 sshorter than RenewalInterval
IdlePoll1 spositive
RetryInitial / RetryMax1 s / 60 s
TransportLifetime30 sat most 30 s
Acceptance horizon (per message, OutboxStageOptions)7 daysup to 30 days

See Outbox.

Telemetry: RunwickTelemetryOptions

Set with runwick.Telemetry(o => ...).

OptionDefaultAllowed
RecordExceptionDetailsfalsePuts a cleaned, bounded exception message on spans and logs
MaxSeriesPerInstrument1,000100 to 10,000
CollectApplicationMetricsfalseMakes this process the application-wide metrics collector
ApplicationSamplePeriod15 s5 s to 5 min
CollectorNamedefault

Management and dashboard

Management needs authentication schemes and a grant mapping, or it does not start. There is no anonymous mode. The dashboard title defaults to Runwick. See Management API and Dashboard.

Fixed limits

These cannot be configured.

LimitValue
Serialized payload1 MiB of JSON, 32 levels deep
Application, queue and pool names1 to 128 characters, [A-Za-z0-9][A-Za-z0-9._-]*
Job names1 to 200 characters, same characters
Uniqueness and resource keys1 to 512 bytes of UTF-8
Business metadata16 KiB and 32 entries
Batch dispatch100 items or 2 MiB
Rate limit1 to 100,000 per window; window 1 s to 24 h; at most 8 rate scopes per job

Startup validation

When the host starts, Runwick checks the whole configuration and throws RunwickConfigurationException listing every problem at once. Each entry has a code, the setting it is about, and the rule it broke. Timing problems have the code invalid_timing:

Text
Runwick configuration is invalid:
  [invalid_timing] LeaseDuration: V-04: RenewalIntervalMax + 2·ControlRequestTimeout + ControlRetryMaxBackoff + SchedulerAllowance (31000 ms) must be < LeaseDuration − LeaseSafetyMargin (20000 ms).

The worker timings must satisfy these rules. The defaults pass all of them:

RuleWith defaults
Every duration is positive and a whole number of milliseconds (RenewalJitter and RecoveryScanJitter may be 0)pass
LeaseSafetyMargin is less than LeaseDuration10 s < 60 s
RenewalInterval − RenewalJitter is above zero12 s > 0
(RenewalInterval + RenewalJitter) + 2 × ControlRequestTimeout + ControlRetryMaxBackoff + SchedulerAllowance is less than LeaseDuration − LeaseSafetyMargin31 s < 50 s
MinimumInvocationMargin is at least 2 × ControlRequestTimeout + ControlRetryMaxBackoff + SchedulerAllowance15 s ≥ 13 s
LeaseWatchdogInterval is at most 1 s1 s
ControlRetryInitialBackoff is at most ControlRetryMaxBackoff100 ms ≤ 1 s
ControlRequestTimeout is less than OwnershipCommandLifetime5 s < 30 s
ShutdownDrain + ShutdownCancellation + ShutdownFinalization is at most ShutdownBudget25 s ≤ 25 s
Each resolution budget is at least ControlRequestTimeout15, 15, 30 s ≥ 5 s
RecoveryScanJitter is less than RecoveryScanInterval1 s < 5 s

Scale timings together

Setting LeaseDuration to 30 s on its own fails: 31 s of renewal path does not fit in 20 s of usable lease. Raising ControlRequestTimeout to 10 s fails too, because the start margin then needs 23 s. If you change one timing, scale the related ones with it, for example a 120 s lease with a 20 s safety margin and a 30 s renewal interval.

Other startup errors you may see: invalid_pool (for example a pool that subscribes a queue no job or Queue(...) call registers), pool_without_queue, pool_budget_exceeded (pool maximums add up to more than the worker's limit), invalid_policy for store options, and missing_store when no store is configured. Troubleshooting lists the common ones.