If you've ever redeployed a Hangfire-backed app and gone looking for yesterday's failed jobs, only to find the dashboard doesn't remember them, you're not misconfiguring anything. That's how it's designed to work — the Hangfire dashboard is a live view of current storage state, not an audit log.
Everything the Hangfire dashboard displays — enqueued, processing, succeeded, failed — comes straight out of your Hangfire storage (SQL Server, PostgreSQL, Redis, whatever you've configured) at the moment you load the page. It isn't a separate log that accumulates over time; it's a window onto whatever rows currently exist. Once a row is gone from storage, the dashboard has nothing left to show for it.
Hangfire ships with a background sweep that runs continuously and removes succeeded and failed job records once they age past a retention window — by default on the order of a day, though the exact behavior can vary by storage provider and version, and is configurable. This runs regardless of whether you've deployed anything; it's just Hangfire keeping its own storage from growing without bound. It's a sensible default for keeping storage lean — it just means "job history" was never meant to be permanent in the first place.
If your Hangfire storage backend isn't durable across process restarts (for example, an in-memory provider used for local development or a quick proof of concept), a redeploy or app pool recycle doesn't just prune old jobs on schedule — it wipes all of them at once, succeeded, failed, and pending alike, the instant the process comes back up empty.
For a single small project, this rarely bites you — you notice a failure, you fix it, you move on. For an agency running Hangfire across a dozen client projects, it plays out differently: a client asks why their nightly import failed three weeks ago, and there's no way to answer, because the record that would have explained it aged out long before anyone thought to go looking. The gap isn't visible until exactly the moment you need it not to be.
Two options, and they aren't mutually exclusive:
QueueHawk.Agent streams every job state change out over HTTPS as it happens, so history lives independently of whatever retention window or storage backend Hangfire itself is using — 7 to 365 days depending on plan, and it survives redeploys, restarts, and storage migrations alike, because it was never sitting in the same database Hangfire sweeps.See also: how QueueHawk compares to the built-in Hangfire dashboard more broadly, and what changes once you're running this across more than one client project.