Queue Tasks
Run asynchronous background tasks through VitNode's database-backed queue with retries and AdminCP monitoring.
Queue tasks handle asynchronous, one-off background work (e.g. sending bulk emails, processing media, or scheduling posts). Tasks are persisted in the core_queue table and drained periodically by VitNode's background worker.
Quick start
1. Define a Queue Task
import { buildQueueTask } from "@vitnode/core/api/lib/queue"
export interface NewsletterPayload {
postId: number
}
export const sendNewsletterTask = buildQueueTask<NewsletterPayload>({
name: "send-newsletter",
handler: async (c, payload) => {
await c.get("log").info(`Processing newsletter for post #${payload.postId}`)
},
})2. Register in an API Module
Attach the task to your module's queueTasks list:
import { buildModule } from "@vitnode/core/api/lib/module"
import { sendNewsletterTask } from "../../tasks/send-newsletter.task"
export const postsModule = buildModule({
name: "posts",
routes: [createPostRoute],
queueTasks: [sendNewsletterTask],
})3. Dispatch Tasks
Dispatch jobs from any Hono route or service:
// Dispatch immediately
await c.get("queue").dispatch({
name: "send-newsletter",
payload: { postId: 42 },
})
// Or schedule for future execution
await c.get("queue").dispatch({
name: "send-newsletter",
payload: { postId: 42 },
executeAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour from now
priority: 10, // Higher numbers run first
})dispatch returns { id } as soon as the task row is written to PostgreSQL.
Retries and Error Handling
VitNode tasks automatically retry on failure using exponential backoff:
export const riskyTask = buildQueueTask({
name: "sync-external-api",
maxAttempts: 5, // Default is 3
handler: async (c, payload) => {
// If an error is thrown, the task retries automatically
},
})| Attempt | Delay Before Next Retry |
|---|---|
| 1st failure | ~10 seconds |
| 2nd failure | ~20 seconds |
| Each next failure | Doubles, capped at 1 hour |
| Final failure | Marked as failed in core_queue |
Stale Tasks Are Reclaimed
A task still processing after 15 minutes (QUEUE_STALE_PROCESSING_MINUTES) belonged to a worker that died mid-run. The queue worker puts it back to pending, or marks it failed if it has used every attempt, so a crash never strands a task forever.
Keep handlers well under 15 minutes
A handler that runs longer would be reclaimed and run again while it is still going. Long work should save its progress and dispatch a follow-up task instead - the way notification fan-out does.
AdminCP Queue Monitor
Inspect and debug tasks at Core → Advanced → Queue (/admin/core/advanced/queue):
- Monitor pending, active, and failed jobs in real time.
- View failure error traces and retry failed tasks with one click.
buildQueueTask Options
Prop
Type