Background jobs
Work that does not belong in a request runs on scheduled jobs. Job state is persisted in the database, so a restart does not lose scheduled work.
The dashboard is at Hangfire:DashboardPath (/hangfire by default) and is restricted to
administrators who have completed two-factor sign-in.
The jobs
Section titled “The jobs”All configured under the BackgroundJobs section. Each block has at least Enabled, Interval,
and MaxItems.
| Job | Does | Interval | Batch |
|---|---|---|---|
PermissionRebuild |
Materialises effective permissions from the rebuild queue. | 30 s | 200 |
ReminderDispatch |
Fires due document reminders. | 1 min | 200 |
EmailDelivery |
Drains the outbound email outbox. Holds mail while email is not configured. | 1 min | 100 |
SignatureExpiry |
Expires stale signature requests. | 5 min | 200 |
AntivirusBackfill |
Scans files still waiting for a verdict. Must stay on. | 5 min | 5000 |
FolderEmailIntake |
Polls a mailbox and files attachments. Disabled by default. | 5 min | - |
UploadSessionSweep |
Cleans up abandoned chunked uploads. | 15 min | 200 |
WorkflowEscalation |
Escalates overdue workflow tasks. | 15 min | 200 |
PreviewGeneration |
Renders missing previews and thumbnails, and regenerates unreadable ones. | 15 min | 200 |
OcrBackfill |
Extracts text from documents that have none. | 15 min | 100 |
StorageEncryptionBackfill |
Encrypts content stored before encryption at rest was enabled. | 15 min | 100 |
StorageSidecarBackfill |
Protects legacy storage metadata files. | 15 min | 100 |
RetentionCleanup |
Purges recycle-bin items past retention. | 1 h | 200 |
OrphanByteReclamation |
Deletes stored files nothing references any more. | 6 h | 200 |
StorageIntegrity |
Verifies stored files against their recorded hashes. | 12 h | 500 |
PrivacyRetention |
Clears aged IP addresses and user agents, and resumes interrupted erasures. | 1 day | 500 |
StorageReadRemediation |
One-time upgrade repairs; becomes a no-op once complete. | 2 min | 500 |
Three more jobs run on fixed schedules with no configuration block:
| Job | Does |
|---|---|
audit-seal |
Seals new audit rows into signed, chained ranges every 5 minutes. |
audit-integrity |
Verifies the audit chain: recent seals hourly, the full chain daily. |
workflow-assignment-reconciliation |
Reassigns workflow tasks and signature slots belonging to users who can no longer act on them. |
Job-specific settings
Section titled “Job-specific settings”| Job | Extra settings |
|---|---|
UploadSessionSweep |
SessionLifetime (1 day) - how long an abandoned session survives. |
OrphanByteReclamation |
MinimumAge (1 hour) - how long a blob must be unreferenced before deletion. |
WorkflowEscalation |
EscalationWindow (2 days) - how long a task may sit before escalation. |
PreviewGeneration |
MaxAttempts (3) - retries before giving up on a version. |
EmailDelivery |
MaxAttempts (3), BaseBackoff (1 min), LeaseTimeout (5 min), HoldingWindow (3 days) - mail queued longer than this while email was unavailable is suppressed rather than sent late. |
AntivirusBackfill |
TimeBudget (4 min) - must stay below the interval. VirusTotal lookups are paced by Antivirus:VirusTotalRequestsPerMinute. |
PrivacyRetention |
ResumeErasuresOlderThan (10 min) - when an interrupted erasure is picked up again. |
StorageReadRemediation |
TimeBudget (90 s), BatchSize (500). |
Reading the cadences
Section titled “Reading the cadences”The intervals are not arbitrary, and the pattern is worth understanding before changing any of them.
Fast (30 s - 1 min) for things a user is waiting on. Permission rebuild, reminders, email.
Medium (5 - 15 min) for things that should happen soon but that nobody is watching. Previews, text extraction, escalation, session cleanup. A new document without a thumbnail for a few minutes is acceptable; the alternative is doing that work inside the upload request.
Slow (1 - 12 h) for housekeeping. Retention, byte reclamation, integrity verification. These scan and are deliberately infrequent.
Cadence is a real cost
Section titled “Cadence is a real cost”The configuration file itself records why PermissionRebuild runs every 30 seconds rather than
every 5: access-control writes drain the rebuild queue inline, in the same transaction, so
this job is a safety net for rows that outlive their request, not the primary path. At a
5-second cadence the job runner’s own bookkeeping became the dominant cost of an otherwise idle
system.
That is the general lesson. A job that ticks frequently and usually finds nothing to do is not free - it costs a scheduler wake-up, a connection, and a query, on every tick, forever.
Two-stage deletion
Section titled “Two-stage deletion”Deleting a document does not free disk space, and the reason is two jobs with different jobs to do:
RetentionCleanuppurges the document record once it is past retention.OrphanByteReclamationlater deletes the underlying stored file, if nothing else references it and it is older thanMinimumAge.
Because identical content is stored once, a stored file may still be referenced by other documents. Space is therefore freed some hours after a purge, not at it. See Governance and retention.
Enabling and disabling
Section titled “Enabling and disabling”Every job can be turned off with its Enabled flag. A job checks the flag when each run starts, so
runs already queued, retrying or interrupted do nothing once it is disabled. Some are worth knowing
about:
FolderEmailIntakeis disabled by default and needs both its own flag andEmailIntake:Enabledto be turned on.- Disabling
PermissionRebuildis usually safe in the short term, because access-control writes drain the queue inline. It is a safety net, not the primary path - but leaving it off indefinitely means nothing catches the rows that outlive their request.
Leave these on:
StorageIntegrity- your only automatic detection of corrupted or missing stored files.AntivirusBackfill- the only recovery path for a file whose scan never ran. Unscanned files are never served.PrivacyRetention- clears network identifiers on schedule and finishes interrupted erasures.
Monitoring
Section titled “Monitoring”The dashboard shows scheduled, running, succeeded, and failed jobs, with retry history.
Hangfire:RetryAttempts (3 by default) controls automatic retries.
A job failing consistently is worth investigating rather than waiting out - a preview job failing every pass usually means the converter path is wrong, not that the documents are unusual.
Related
Section titled “Related”- Configuration keys - the full
BackgroundJobssection. - Performance and tuning - job load in context.
- Deployment - the startup check.
- Troubleshooting - symptoms that trace back to a job not running.