Quick start
Go from an empty project to a background job that a worker picks up and runs.
This page builds one small console app that is both a producer and a worker. It dispatches a job, and its own worker runs it. The same job and handler work unchanged when you later split them into a web app and a separate worker service.
You need the .NET 10 SDK and one store. Docker is the quickest way to get PostgreSQL or Redis; SQLite needs nothing.
Create a project and start a store
Text Text Define a job and its handler
Create
Jobs.cs. A job is a record that implementsIQueueJoband has a[JobName]. The name is what gets stored, so you can rename or move the C# type later without breaking jobs already in the store.Text The handler is created from dependency injection, in a fresh scope for each run, so constructor injection works as usual.
IJobContexttells it which job and which execution it is on.Create the schema
Workers never create the schema themselves. Run the migration once, before the app starts. In a real deployment this is a separate step with its own credentials; here it runs at the top of
Program.csto keep things short.Delete the template's
Worker.csand replaceProgram.cswith:Text You can run the same migration from the command line instead:
runwick migrate --provider postgres --connection-env MY_CONN. See Installation.Register Runwick and a worker
Continue
Program.csbelow the migration. Only theUse...line depends on the store.Text Application("quickstart")names your app inside the store, so several apps can share one database.AddJobsFromAssemblyContainingregisters every[JobName]type in that assembly.AddWorkermakes this process a worker. Poolmainreads queuedefaultand runs at most 4 jobs at once. Jobs with no[Queue]go todefault.
Leave out
AddWorkerin an app that only dispatches.Dispatch a job and watch it run
Add this after
StartAsync:Text Run it:
Text You should see the
Dispatched jobline, then the handler's log line a moment later. Press Ctrl+C to stop. The worker stops taking new jobs and gives running ones time to finish.In a web app, inject
IJobDispatcherinto an endpoint or a service instead of resolving it by hand:Text
What just happened
DispatchAsync returned only after the store confirmed the job was written. The worker found the job, took a lease on it, ran your handler in its own dependency-injection scope, and recorded the result as succeeded.
If the handler had thrown, the job would have waited a few seconds and run again. By default a job runs at most 3 times, so 2 automatic retries, before it ends up failed.
If dispatch reports an unknown outcome
A network failure can leave DispatchAsync unable to tell whether the write happened. The job may or may not exist. Dispatching and Delivery guarantees explain how to handle it safely.
Check it with the sample host
The repository ships a sample host for PostgreSQL that migrates, dispatches a batch of jobs, runs them on an in-process worker, and prints how many succeeded:
It prints 10 succeeded and exits with code 0 when every job ran.
Add the dashboard (optional)
The dashboard runs inside an ASP.NET Core app and needs your own sign-in, such as cookie authentication registered with AddAuthentication().AddCookie() and app.UseAuthentication(). There is no anonymous mode.
This example gives every signed-in user the Operator role; map roles from your own claims in a real app. Sign in, open /runwick, and you can see the job, its attempts and its status history. Dashboard covers permissions and setup in full.
Next steps
Jobs and handlers
Queues, priorities, execution limits and the fluent alternative to attributes.
Retries and timeouts
Change the retry budget and delay, and set an execution timeout.
Workers
Pools, concurrency, shutdown and running workers in a separate process.
Delivery guarantees
Why handlers must be safe to repeat, and how to make them so.