Scheduler
The Scheduler defines recurring work.
Use it for work that should run on an interval, cron expression, or calendar schedule.
Scheduler API
This guide covers how a GoForj App registers and runs schedules. For standalone construction, the complete fluent API, adapters, and locking options, see the scheduler library page.
When to Use Scheduler
Use the scheduler when work should begin on an interval, cron expression, or calendar schedule. Give it a stable name and call a domain service or dispatch a named job.
Use events or request handlers for work triggered by those boundaries. When a scheduled workflow needs durability or retries, let the schedule dispatch a job. Add singleton process policy or distributed locking when overlapping scheduler processes are possible.
Default Recommendation
Use the scheduler to decide when recurring work starts.
| Need | Shape |
|---|---|
| Short, idempotent maintenance call | schedule calls a domain service method |
| Durable work with retries | schedule dispatches a named job |
| Operator-visible recurring behavior | stable schedule name plus metrics and inspects |
| High-throughput background processing | queue workers, not scheduler callbacks |
The scheduler should not become the place where business workflows accumulate.
Generate a Schedule
forj make:schedule reports:daily --every 24hIf --every is omitted, the generated starter interval is 1h.
Naming Schedules
Schedules should have stable names.
Use category:cadence for cadence-oriented schedules such as reports:daily, or category:action for maintenance actions such as sessions:cleanup. See Naming Conventions for the full naming map.
Start Scheduler
Run the scheduler directly:
forj schedulerFor an additional app, prefix the command with the app name:
forj admin schedulerRun it with other enabled local runtimes:
forj appFor an additional app, prefix the command with the app name:
forj admin appRecommended Shape
Schedules should call domain-owned services, jobs, or command work.
Good shape:
s.Every(30).Seconds().
Name("monitor:poll").
Do(s.InspectTask("monitor:poll", s.monitorCheckJob.RunScheduledPoll))Avoid growing scheduler runtime files into business-logic buckets.
Observability
Generated scheduler code can record job outcomes into metrics and inspects when those components are enabled.
Lighthouse can expose schedule metadata and operator controls through runtime-specific integration.
In focused tests, call the schedule's Interval and Handle methods directly. Assert the parsed interval, the stable Name, the delegated service or job call, and the returned error. Keep one App-level registration test when manually maintained schedules are added beside generated entries.
Production
In production, scheduler runtime usually needs clear singleton behavior or distributed locking when more than one process could run the same schedule.
Stable schedule names make scheduler behavior understandable, but they do not prevent overlap by themselves. Add overlap protection on the schedule when the work cannot run concurrently:
s.EveryFiveMinutes().
WithoutOverlapping().
Name("reports:daily").
Do(s.InspectTask("reports:daily", s.reports.GenerateDaily))Use WithoutOverlapping() for same-process overlap control. Use WithoutOverlappingWithLocker(...) with a shared locker when multiple scheduler processes could run the same schedule.
Do not scale scheduler processes the same way as stateless HTTP or queue workers unless the schedules and locking strategy support it. Generated scheduler registration does not add distributed locking automatically.
Supervise the built scheduler Runtime in deployment:
./bin/app schedulerExpected startup includes Scheduler started. Let one safe schedule become due and confirm its stable name and outcome in logs, metrics, or an Inspect. Send SIGTERM through the supervisor and confirm Shutting down scheduler and Scheduler shut down appear before the supervisor timeout. The Scheduler Processes runbook covers metrics ports, singleton policy, and failure response.
Common Mistakes
Common mistakes
- Do not hide important scheduled work behind anonymous callbacks.
- Do not put large business workflows in the scheduler registry.
- Do not run duplicate scheduler processes accidentally.
- Do not assume stable schedule names are a locking mechanism.
- Do not treat schedules as durable queues.
- Do not use unstable names for operator-facing schedules.
Next Steps
- Retries and Idempotency explains safe recurring work.
- Runtime Topology explains process boundaries.
- Environment Reference lists scheduler timeouts.
- Naming Conventions defines stable schedule names.
make:scheduleReference lists cadence flags, removal, and generated registration.- Scheduler covers standalone package details.