# Coldbox Scheduled Tasks

> Use this skill when creating ColdBox scheduled tasks, building Scheduler.cfc files, registering task frequencies, managing task life-cycles (before/after/onFailure/onSuccess), using server fixation for clustered apps, or configuring module schedulers.

- Skill: `coldbox/coldbox-scheduled-tasks` (Agent Skill)
- Install (CLI): `npx skillmds@latest add coldbox/coldbox-scheduled-tasks`
- Raw SKILL.md: https://api.skillmd.com/api/skills/coldbox/coldbox-scheduled-tasks/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: ColdBox (https://skillmd.com/u/coldbox)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/coldbox/coldbox-scheduled-tasks

---


# Scheduled Tasks

## When to Use This Skill

Use this skill when you need to run background tasks on a schedule — recurring jobs, one-off warm-up tasks, heartbeats, cache cleanup, data sync, or any server-side automation.

## Language Mode Reference

Examples use **BoxLang** / CFML script syntax (both supported identically for scheduler code).

## Core Concepts

- **Global Scheduler** — `config/Scheduler.cfc` with a `configure()` method; auto-discovered by ColdBox
- **Module Scheduler** — each module can have its own `Scheduler.cfc` inside the module root
- **WireBox ID** — the global scheduler is available as `appScheduler@coldbox`
- **Task DSL** — fluent API on `ColdBoxScheduledTask` object returned by `task( name )`
- **Built on AsyncManager** — backed by Java `ScheduledExecutorService`

## File Location & Structure

```
config/
  Scheduler.cfc       ← Global app scheduler
modules_app/
  myModule/
    Scheduler.cfc     ← Module-level scheduler
```

### Minimal Scheduler

```javascript
// config/Scheduler.cfc
component {

    function configure() {
        task( "Clear Old Sessions" )
            .call( () => getInstance( "SessionService" ).purgeExpired() )
            .everyDayAt( "02:00" );
    }
}
```

## Full Scheduler Template

```javascript
component {

    /**
     * Configure global scheduler settings and register tasks.
     * Available config methods:
     *   setTimezone( "America/Chicago" )
     *   setExecutor( asyncManager.newScheduledExecutor("myPool", 50) )
     *   setCacheName( "redis" )          ← for server fixation
     *   setServerFixation( true )        ← run once across all nodes
     */
    function configure() {

        // ---------- Global Settings ----------
        setTimezone( "UTC" );

        // ---------- Recurring Tasks ----------
        task( "Heartbeat" )
            .call( () => runEvent( "main.heartbeat" ) )
            .every( 5, "minutes" )
            .onFailure( ( task, exception ) => {
                getInstance( "AlertService" ).sendAlert( exception.message )
            } );

        task( "Daily Cleanup" )
            .call( () => getInstance( "CleanupService" ).run() )
            .everyDayAt( "03:00" )
            .onOneServer()          // clustered apps: run on ONE node only
            .withNoOverlaps();      // prevent task stacking if it runs long

        task( "Weekly Report" )
            .call( () => getInstance( "ReportService" ).generateWeekly() )
            .everyWeekOn( 1, "08:00" )  // Monday at 08:00
            .onEnvironment( [ "production" ] );

        // ---------- One-Off Startup Tasks ----------
        task( "Warm Up Cache" )
            .call( () => getInstance( "CacheWarmer" ).prime() )
            .delay( 30, "seconds" );    // fire once, 30s after app start

        task( "Notify Admin of Startup" )
            .call( () => getInstance( "AdminNotifier" ).appStarted() )
            .delay( 1, "minutes" );
    }

    // ---- Life-Cycle Hooks ----

    function onStartup() {
        // Called after all tasks are registered
        log.info( "Scheduler started" )
    }

    function onShutdown() {
        // Called before scheduler shuts down
        log.info( "Scheduler shutting down" )
    }

    function onAnyTaskError( required task, required exception ) {
        // Called whenever any task throws
        log.error( "Task failed: #task.getName()# — #exception.message#" )
    }

    function onAnyTaskSuccess( required task, result ) {
        // Called whenever any task completes without error
    }

    function beforeAnyTask( required task ) {
        // Called before every task execution
    }

    function afterAnyTask( required task, result ) {
        // Called after every task execution
    }
}
```

## Task DSL Reference

### Registering a Task

```javascript
task( "my-task-name" )          // unique name — returns ColdBoxScheduledTask
    .call( closure )            // required: what to execute
    .everyMinute()              // required: how often (or omit for one-off)
```

### Callable Targets

```javascript
// Lambda / arrow function
.call( () => getInstance( "MyService" ).doWork() )

// Closure
.call( function() {
    getInstance( "MyService" ).doWork()
} )

// CFC instance — calls run() by default
.call( getInstance( "MyTaskCFC" ) )

// CFC instance with custom method
.call( getInstance( "MyService" ), "cleanup" )

// ColdBox event
.call( () => runEvent( "scheduler.cleanCache" ) )
```

### Frequency Methods

| Method | Description |
|--------|-------------|
| `every( period, timeUnit )` | Custom interval (ms default) |
| `spacedDelay( delay, timeUnit )` | Fixed delay between completions (no overlap) |
| `everyMinute()` | Every minute |
| `everyHour()` | Every hour |
| `everyHourAt( minutes )` | Hourly at a specific minute mark |
| `everyDay()` | Every day at midnight |
| `everyDayAt( "HH:mm" )` | Daily at a specific time |
| `everyWeek()` | Every Sunday at midnight |
| `everyWeekOn( day, time )` | Weekly on a specific day (1=Mon … 7=Sun) |
| `everyMonth()` | First day of every month at midnight |
| `everyMonthOn( day, time )` | Monthly on specific day and time |
| `onFirstBusinessDayOfTheMonth( time )` | First Monday of the month |
| `onLastBusinessDayOfTheMonth( time )` | Last business day of the month |
| `everyYear()` | First day of the year at midnight |
| `everyYearOn( month, day, time )` | Yearly on a specific date |
| `onWeekends( time )` | Saturday and Sunday |
| `onWeekdays( time )` | Monday–Friday |
| `onMondays( time )` … `onSundays( time )` | Specific day of week |

> Time units: `nanoseconds`, `microseconds`, `milliseconds` (default), `seconds`, `minutes`, `hours`, `days`

### Overlap Prevention

```javascript
// Prevent stacking: next run waits for previous to finish
.withNoOverlaps()

// Spaced delay alternative (explicit)
.spacedDelay( 30, "seconds" )
```

### Startup Delay

```javascript
// Delay first execution only
.delay( 5, "minutes" )
```

### Life-Cycle Methods Per Task

```javascript
task( "example" )
    .call( () => doWork() )
    .everyMinute()
    .before( ( task ) => {
        // runs before each execution
    } )
    .after( ( task, results ) => {
        // runs after each execution
    } )
    .onFailure( ( task, exception ) => {
        // handle errors
    } )
    .onSuccess( ( task, results ) => {
        // handle success
    } )
```

### Constraints

```javascript
// Run only when a condition is true at runtime
.when( () => getSetting( "tasksEnabled" ) )

// Restrict to specific environments
.onEnvironment( "production" )
.onEnvironment( [ "staging", "production" ] )

// Date range
.startOn( "2025-01-01", "00:00" )
.endOn( "2025-12-31", "23:59" )

// Time-of-day window
.between( "09:00", "17:00" )
.startOnTime( "09:00" )
.endOnTime( "17:00" )

// Specific timezone for this task
.setTimezone( "America/New_York" )
```

### Server Fixation (Clustering)

```javascript
// Run on only ONE node in a cluster — requires distributed cache
task( "nightly-report" )
    .call( () => reportService.build() )
    .everyDayAt( "01:00" )
    .onOneServer()
    .setCacheName( "redis" )   // change the fixation cache (default: "template")
```

### Disable / Enable

```javascript
// Permanently disabled at registration
.disable()

// Re-enable at runtime
myTask.enable()

// Prefix with "x" to disable (like xdescribe in TestBox)
xtask( "Disabled Task" )
    .call( () => doWork() )
    .everyMinute()
```

## Module Scheduler

Create `Scheduler.cfc` in your module root. Module schedulers have extra auto-injected variables: `moduleMapping`, `modulePath`, `moduleSettings`.

```javascript
// modules_app/myModule/Scheduler.cfc
component {

    function configure() {
        task( "Module Sync" )
            .call( () => getInstance( "SyncService@myModule" ).sync() )
            .everyHour()
    }
}
```

Register it in `ModuleConfig.cfc` — ColdBox discovers scheduler files automatically when they exist in the module root.

## Scheduler Properties Available in configure()

| Property | Description |
|----------|-------------|
| `asyncManager` | AsyncManager reference |
| `cachebox` | CacheBox reference |
| `controller` | ColdBox controller |
| `log` | Pre-configured logger |
| `wirebox` | WireBox reference |
| `appMapping` | Application mapping path |

Helper methods available: `getInstance()`, `announce()`, `runEvent()`, `runRoute()`, `getSetting()`, `getCache()`, `getModuleSettings()`, `view()`, `layout()`

## Key Rules

- Always create a unique task name — duplicate names cause conflicts.
- Call `.call()` before any frequency method.
- Use `.withNoOverlaps()` on tasks that may run longer than their interval.
- Use `.onOneServer()` for tasks that must run once across a cluster — requires a distributed cache.
- Omit any frequency method to make a task **one-off** (run once at startup, optionally after a `delay()`).
- Use `xtask()` prefix to temporarily disable a task without removing it (like TestBox's `xdescribe`).

