Fast, reliable background jobs in Go.

Atomic, transaction-safe, robust job queueing for Go applications. Backed by PostgreSQL and built to scale.

Transactional enqueueing simplifies everything.

River jobs never run before your transaction completes, and are never lost. If your API's transaction succeeds, your job will be enqueued—period.

Compared to working with external job queues like Redis, this means a simpler development model that lets you build more reliable applications more quickly. For Go applications built on Postgres, this also means no added services to manage.

func createUser( /* ... */) (*User, error) {
tx, err := dbPool.Begin(ctx)
if err != nil {
return nil, err
defer tx.Rollback(ctx)
// Create the new user record:
user, err := insertUser(ctx, tx, email)
if err != nil {
return nil, err
// Enqueue a job to send the user an onboarding email:
_, err = riverClient.InsertTx(ctx, tx,
SendOnboardingEmailArgs{UserID: user.ID},
if err != nil {
return nil, err
// Atomically commit both the user record and the job:
if err := tx.Commit(ctx); err != nil {
return nil, err
return user, nil

Batteries included

The features you need.

River is fully-featured so you can focus on building your app instead of chasing down bugs and distributed systems edge cases.

Transactional enqueueing
Simplifies development and prevents bugs.
Error handling and reporting
Connects to your bug tracking service to catch issues.
Automatic retries
Erroring jobs are automatically retried with a customizable exponential backoff.
Multiple isolated queues
Each named queue is processed independently and won't block other queues.
Unique jobs
Ensure that a job is enqueued only once in a given time period.
Jobs are worked according to assigned priority. Important jobs are processed first.
Recurring Jobs
Set up a schedule for recurring jobs, including advanced cron scheduling and time zone handling.
Configurable job timeouts
Handle jobs that are lightning fast or very slow. Ensure they don't run forever.
Graceful shutdown
Waits for active jobs to complete before exiting.
No added service dependencies
River runs in your app's primary Postgres database, which means there are no new operational dependencies.

Thoughtfully designed for modern Go.

River is designed by experienced Go developers to fit the conventions of the Go ecosystem. It leverages generics to provide strongly typed, structured arguments to workers.

// Each worker has a corresponding args type:
type SendEmailArgs struct {
UserID int64
func (args SendEmailArgs) Kind() string { return "SendEmail" }
type SendEmailWorker struct {
// Work receives a typed *Job[T] to process:
func (w *SendEmailWorker) Work(ctx context.Context, j *Job[SendEmailArgs]) error {
user, err := getUser(ctx, j.Args.UserID)
if err != nil {
return err
return sendOnboardingEmail(

Stay updated.

We're hard at work on more advanced features including a self-hosted web interface. Sign up to get updates on our progress.