Skip to content
Trackr beta
esc
Type to search. Use ↑ ↓ to move, ↵ to open.
Self-hosting

Background jobs

Emails, webhooks, push and cleanup run outside the request path, through a Postgres-backed queue drained by the Go worker.

Updated 1 Sept 2026 beta 1 min read

How the queue works #

The web app writes a row into the jobs table. The worker claims rows with SELECT … FOR UPDATE SKIP LOCKED, runs the handler, and records the outcome. A Postgres LISTEN/NOTIFY trigger wakes the worker the moment a job is inserted, so latency is milliseconds, and a poll interval catches anything missed.

Lifecycle: queued → running → succeeded | failed (retry with backoff) | cancelled. Each running job heartbeats; a reaper re-queues jobs whose worker died.

Job types #

TypePurpose
mail.sendTransactional email.
webhook.deliverOne webhook delivery attempt.
push.sendAPNs push to registered devices.
notify.digestDaily notification digest per user.
prune.jobs prune.invitations prune.notifications prune.webhook_deliveriesRetention.

Schedules #

The scheduler reads the schedules table every few seconds and enqueues one job per due row, then advances next_run_at by the row's interval. It only enqueues; the worker does the work. Schedules are plain rows: pause, retune or add one with SQL, or toggle them under Admin → System → Schedules.

Monitoring #

Admin → System → Jobs shows recent jobs with their state, queue-health counts, and actions to send a test email, stop a running job, or retry a failed one. Both Go services log structured lines to stdout; point your log shipper at the containers.

Edit this page on GitHub