Documentation

Background jobs

Every generated project includes a durable Postgres job queue with retries and a worker you can run in-process or separately.

Included in every project

Generated projects ship with a background job queue stored in the project's own Postgres database. There is no option to select at generation time and nothing to install: no Redis, no Postgres extension, no extra service. The queue is part of the shared shiprust-core crate, the jobs table comes from the project's migrations, and the worker starts inside the web process by default.

Jobs are rows with a payload and a handler. Enqueue one from a request, inside the same transaction as the data it belongs to, and the worker runs it later, retrying with backoff if it fails. Queued jobs survive crashes, restarts and deploys.

What you get

  • A Job trait: a serde struct with a stable KIND, MAX_ATTEMPTS, TIMEOUT and an async run handler that receives your app state.
  • enqueue(&mut *tx, &job, options) with optional run_at delay and dedupe_key, usable with a pool, connection or transaction.
  • A worker with FOR UPDATE SKIP LOCKED claims, 60-second leases kept alive by heartbeats, exponential backoff, dead-letter rows, graceful shutdown that drains running jobs, and retention of finished rows.
  • cargo run -- jobs stats and cargo run -- jobs retry <id>.
  • JOBS_WORKER=true|false|only and a worker command, so the same image runs jobs in the web container or in a separate one.
  • An example job, a welcome email after signup verification, with an end-to-end test that runs the worker against a local mail double.

Handlers run at least once; the project guide explains how to keep them idempotent and when to use a job instead of the request handler, the bounded CPU helper or a Tokio interval.

Where to read more

The generated project's guide at app/content/docs/background-jobs.md (also served at /docs/background-jobs in your running app) covers defining jobs, enqueueing, guarantees, running the worker and operating the queue. The CapRover and Docker guides in your project describe the optional separate worker container.

This site runs on the same queue: the purchase confirmation email after a lifetime purchase is a background job. The queue supports both in-process and dedicated workers.

Read how we implement Rust background jobs with a Postgres queue for a code-level walkthrough of transactional enqueueing, leases, retries, and at-least-once delivery.