Documentation

Cron Jobs

Cron Jobs

Declare recurring background jobs in VitNode plugins and execute them via internal or external cron triggers.

A cron job in VitNode consists of a named handler and a standard 5-field cron expression. Cron definitions are automatically registered in the core_cron database table and manageable through the AdminCP.

Quick start

1. Define a Cron Job

Create a cron definition in your plugin:

plugins/blog/src/api/cron/cleanup.cron.ts
import { buildCron } from '@vitnode/core/api/lib/cron'

export const cleanupCron = buildCron({
  name: 'cleanup-drafts',
  description: 'Remove expired post drafts every midnight',
  schedule: '0 0 * * *', // Standard cron syntax
  handler: async (c) => {
    await c.get('log').info('Running midnight cleanup...')
  },
})

2. Register in an API Module

Attach the cron job to a module in src/api/modules/:

plugins/blog/src/api/modules/posts/posts.module.ts
import { buildModule } from '@/api/builders'
import { cleanupCron } from '../../cron/cleanup.cron'

export const postsModule = buildModule({
  name: 'posts',
  routes: [listPostsRoute],
  cronJobs: [cleanupCron], 
})

When your plugin is mounted in config.api.ts, the cron job is automatically registered.


Execution Adapters

VitNode does not hold persistent timers in the HTTP worker. Instead, jobs are triggered via an adapter:

Option A: In-Process Node CRON (Single Server)

Best for single-instance or local development. Configure in vitnode.api.config.ts:

apps/api/src/vitnode.api.config.ts
import { NodeCronAdapter } from '@vitnode/node-cron'
import { buildApiConfig } from '@vitnode/core/vitnode.config'

export const vitNodeApiConfig = buildApiConfig({
  cron: NodeCronAdapter(), 
})

Option B: External Webhook (Multi-Instance / Serverless)

Trigger runs via external HTTP schedulers (AWS EventBridge, GitHub Actions, or crontab):

curl -X POST https://your-domain.com/api/@vitnode/core/cron   -H "Authorization: Bearer $CRON_SECRET"

Manage cron jobs in the Debug Panel

Open Debug Panel → Cron & queue in the AdminCP (/admin/core/debug/cron), or type "Cron jobs" into the ⌘K admin search. Viewing it needs the cron → can_view staff permission.

  • The table lists every job with its Name, Plugin and Time: the schedule, a status badge, and the last and next run. It is sorted by next run, with a search box to find a job.
  • The play button on a row (Run Job Now) runs that job on demand. It needs the cron → can_run permission.

Health warnings

Cron fails quietly by nature: nobody notices a job that didn't run. So VitNode watches for it.

A job is overdue when its next scheduled run is more than 15 minutes in the past. Never-run jobs count from the moment they were registered.

The scheduler card at the top of the section sums it up:

What you seeWhat it means
Red Scheduler stoppedA job is overdue and nothing has run since. The scheduler is stopped or misconfigured, and the queue isn't draining either.
Amber N cron jobs are overdueThe scheduler ticks, but some jobs aren't finishing. A job that throws keeps its last successful run, so look for its error in the server logs.
Green All jobs on scheduleEverything ran on time. Go make a coffee.

Next to it you get the last run, the next run and how many jobs are registered. The table below lists every job with an Overdue, On schedule or Waiting for first run status.

The same check powers the Debug Panel Overview and the Cron Jobs and Queue Tasks cards at Core → System → Integrations.


buildCron Options

Prop

Type

Learn More