
A fluent, Laravel-inspired scheduler for Go that wraps gocron with expressive APIs for defining, filtering, and controlling scheduled jobs.
Features
- Fluent, chainable API for intervals, cron strings, and calendar helpers (daily/weekly/monthly).
- Overlap protection with optional distributed locking plus per-job tags and metadata.
- Filters (weekdays/weekends/time windows) and hooks (before/after/success/failure) keep jobs predictable.
- Command execution helper for running CLI tasks with background mode and env-aware tagging.
- Auto-generated, compile-tested examples ensure docs and behavior stay in sync.
Why scheduler?
Go has excellent low-level scheduling libraries, but defining real-world schedules often turns into a maze of cron strings, conditionals, and glue code.
scheduler provides a Laravel-style fluent API on top of gocron that lets you describe when, how, and under what conditions a job should run - without hiding what’s actually happening.
Everything remains explicit, testable, and inspectable, while staying pleasant to read and maintain.
Example
scheduler.NewJobBuilder(s).
Name("reports:generate").
Weekdays().
Between("09:00", "17:00").
WithoutOverlapping().
DailyAt("10:30").
Do(func() {
generateReports()
})List jobs as an ASCII table
package main
import (
"github.com/go-co-op/gocron/v2"
"github.com/goforj/scheduler"
)
func main() {
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
EveryMinute().
Name("cleanup").
Do(func() {})
scheduler.NewJobBuilder(s).PrintJobsList()
}Example output:
+--------------------------------------------------------------------------------------+
| Scheduler Jobs › (3)
+----------------+----------+----------------+---------+--------+----------------------+
| Name | Type | Schedule | Handler | Next | Tags |
+----------------+----------+----------------+---------+--------+----------------------+
| hello:world | command | cron 0 0 * * 0 | - | in 3d | env=dev, args="w" |
| hello:world | command | every 1h | - | in 1h | env=dev, args="hour" |
| cleanup | function | every 1m | cleanup | in 1m | env=dev |
+----------------+----------+----------------+---------+--------+----------------------+Runnable examples
Every function has a corresponding runnable example under ./examples.
These examples are generated directly from the documentation blocks of each function, ensuring the docs and code never drift. These are the same examples you see here in the README and GoDoc.
An automated test executes every example to verify it builds and runs successfully.
This guarantees all examples are valid, up-to-date, and remain functional as the API evolves.
API Index
Adapters
Lock
Lock invokes the underlying function.
client := redis.NewClient(&redis.Options{})
locker := scheduler.NewRedisLocker(client, 10*time.Minute)
lock, _ := locker.Lock(context.Background(), "job")
_ = lock.Unlock(context.Background())Run
Run executes the underlying function.
runner := scheduler.CommandRunnerFunc(func(ctx context.Context, exe string, args []string) error {
return nil
})
_ = runner.Run(context.Background(), "echo", []string{"hi"})Unlock
Unlock invokes the underlying function.
Commands
Command
Command executes the current binary with the given subcommand and variadic args.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
Cron("0 0 * * *").
Command("jobs:purge", "--force")Concurrency
WithoutOverlapping
WithoutOverlapping ensures the job does not run concurrently.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
WithoutOverlapping().
EveryFiveSeconds().
Do(func() { time.Sleep(7 * time.Second) })WithoutOverlappingWithLocker
WithoutOverlappingWithLocker ensures the job does not run concurrently across distributed systems using the provided locker.
locker := scheduler.LockerFunc(func(ctx context.Context, key string) (gocron.Lock, error) {
return scheduler.LockFunc(func(context.Context) error { return nil }), nil
})
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
WithoutOverlappingWithLocker(locker).
EveryMinute().
Do(func() {})Configuration
Timezone
Timezone sets a timezone string for the job (not currently applied to gocron Scheduler).
scheduler.NewJobBuilder(nil).
Timezone("America/New_York").
Daily()WithCommandRunner
WithCommandRunner overrides command execution (default: exec.CommandContext).
runner := scheduler.CommandRunnerFunc(func(_ context.Context, exe string, args []string) error {
fmt.Println(exe, args)
return nil
})
builder := scheduler.NewJobBuilder(nil).
WithCommandRunner(runner)
fmt.Printf("%T\n", builder)WithNowFunc
WithNowFunc overrides current time (default: time.Now). Useful for tests.
fixed := func() time.Time { return time.Unix(0, 0) }
scheduler.NewJobBuilder(nil).WithNowFunc(fixed)Construction
NewJobBuilder
NewJobBuilder creates a new JobBuilder with the provided scheduler.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EverySecond().Do(func() {})Diagnostics
CronExpr
CronExpr returns the cron expression string configured for this job.
builder := scheduler.NewJobBuilder(nil).Cron("0 9 * * *")
fmt.Println(builder.CronExpr())Error
Error returns the error if any occurred during job scheduling.
builder := scheduler.NewJobBuilder(nil).DailyAt("bad")
fmt.Println(builder.Error())Job
Job returns the last scheduled gocron.Job instance, if available.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
b := scheduler.NewJobBuilder(s).EverySecond().Do(func() {})
fmt.Println(b.Job() != nil)PrintJobsList
PrintJobsList renders and prints the scheduler job table to stdout.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
EverySecond().
Name("heartbeat").
Do(func() {})
scheduler.NewJobBuilder(s).PrintJobsList()Execution
RunInBackground
RunInBackground runs command/exec tasks in a goroutine.
scheduler.NewJobBuilder(nil).
RunInBackground().
Command("noop")Filters
Between
Between limits the job to run between the provided HH:MM times (inclusive).
scheduler.NewJobBuilder(nil).
Between("09:00", "17:00").
EveryMinute()Days
Days limits the job to a specific set of weekdays.
scheduler.NewJobBuilder(nil).
Days(time.Monday, time.Wednesday, time.Friday).
DailyAt("07:00")Environments
Environments restricts job registration to specific environment names (e.g. "production", "staging").
scheduler.NewJobBuilder(nil).Environments("production").Daily()Fridays
Fridays limits the job to Fridays.
scheduler.NewJobBuilder(nil).Fridays().DailyAt("09:00")Mondays
Mondays limits the job to Mondays.
scheduler.NewJobBuilder(nil).Mondays().DailyAt("09:00")Saturdays
Saturdays limits the job to Saturdays.
scheduler.NewJobBuilder(nil).Saturdays().DailyAt("09:00")Skip
Skip prevents scheduling the job if the provided condition returns true.
enabled := false
scheduler.NewJobBuilder(nil).
Skip(func() bool { return !enabled }).
Daily()Sundays
Sundays limits the job to Sundays.
scheduler.NewJobBuilder(nil).Sundays().DailyAt("09:00")Thursdays
Thursdays limits the job to Thursdays.
scheduler.NewJobBuilder(nil).Thursdays().DailyAt("09:00")Tuesdays
Tuesdays limits the job to Tuesdays.
scheduler.NewJobBuilder(nil).Tuesdays().DailyAt("09:00")UnlessBetween
UnlessBetween prevents the job from running between the provided HH:MM times.
scheduler.NewJobBuilder(nil).
UnlessBetween("22:00", "06:00").
EveryMinute()Wednesdays
Wednesdays limits the job to Wednesdays.
scheduler.NewJobBuilder(nil).Wednesdays().DailyAt("09:00")Weekdays
Weekdays limits the job to run only on weekdays (Mon-Fri).
scheduler.NewJobBuilder(nil).Weekdays().DailyAt("09:00")Weekends
Weekends limits the job to run only on weekends (Sat-Sun).
scheduler.NewJobBuilder(nil).Weekends().DailyAt("10:00")When
When only schedules the job if the provided condition returns true.
flag := true
scheduler.NewJobBuilder(nil).
When(func() bool { return flag }).
Daily()Hooks
After
After sets a hook to run after task execution.
scheduler.NewJobBuilder(nil).
After(func() { println("after") }).
Daily()Before
Before sets a hook to run before task execution.
scheduler.NewJobBuilder(nil).
Before(func() { println("before") }).
Daily()OnFailure
OnFailure sets a hook to run after failed task execution.
scheduler.NewJobBuilder(nil).
OnFailure(func() { println("failure") }).
Daily()OnSuccess
OnSuccess sets a hook to run after successful task execution.
scheduler.NewJobBuilder(nil).
OnSuccess(func() { println("success") }).
Daily()Locking
NewCacheLocker
NewCacheLocker creates a CacheLocker with a cache lock client and TTL. The ttl is a lease duration: when it expires, another worker may acquire the same lock key. For long-running jobs, choose ttl >= worst-case runtime plus a safety buffer. If your runtime can exceed ttl, prefer a renewing/heartbeat lock strategy.
Example: use an in-memory cache driver
client := cache.NewCache(cache.NewMemoryStore(context.Background()))
locker := scheduler.NewCacheLocker(client, 10*time.Minute)
_, _ = locker.Lock(context.Background(), "job")Example: use the Redis cache driver
redisStore := rediscache.New(rediscache.Config{
Addr: "127.0.0.1:6379",
})
redisClient := cache.NewCache(redisStore)
redisLocker := scheduler.NewCacheLocker(redisClient, 10*time.Minute)
_, _ = redisLocker.Lock(context.Background(), "job")NewRedisLocker
NewRedisLocker creates a RedisLocker with a client and TTL. The ttl is a lease duration: when it expires, another worker may acquire the same lock key. For long-running jobs, choose ttl >= worst-case runtime plus a safety buffer. If your runtime can exceed ttl, prefer a renewing/heartbeat lock strategy.
client := redis.NewClient(&redis.Options{}) // replace with your client
locker := scheduler.NewRedisLocker(client, 10*time.Minute)
_, _ = locker.Lock(context.Background(), "job")Metadata
JobMetadata
JobMetadata returns a copy of the tracked job metadata keyed by job ID.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
b := scheduler.NewJobBuilder(s).EverySecond().Do(func() {})
for id, meta := range b.JobMetadata() {
_ = id
_ = meta.Name
}Name
Name sets an explicit job name.
scheduler.NewJobBuilder(nil).
Name("cache:refresh").
HourlyAt(15)Scheduling
Cron
Cron sets the cron expression for the job.
builder := scheduler.NewJobBuilder(nil).Cron("15 3 * * *")
fmt.Println(builder.CronExpr())Daily
Daily schedules the job to run once per day at midnight.
scheduler.NewJobBuilder(nil).Daily()DailyAt
DailyAt schedules the job to run daily at a specific time (e.g., "13:00").
scheduler.NewJobBuilder(nil).DailyAt("12:30")DaysOfMonth
DaysOfMonth schedules the job to run on specific days of the month at a given time.
scheduler.NewJobBuilder(nil).DaysOfMonth([]int{5, 20}, "07:15")Do
Do schedules the job with the provided task function.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
Name("cleanup").
Cron("0 0 * * *").
Do(func() {})Every
Every schedules a job to run every X seconds, minutes, or hours.
scheduler.NewJobBuilder(nil).
Every(10).
Minutes()EveryFifteenMinutes
EveryFifteenMinutes schedules the job to run every 15 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryFifteenMinutes().Do(func() {})EveryFifteenSeconds
EveryFifteenSeconds schedules the job to run every 15 seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryFifteenSeconds().Do(func() {})EveryFiveMinutes
EveryFiveMinutes schedules the job to run every 5 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryFiveMinutes().Do(func() {})EveryFiveSeconds
EveryFiveSeconds schedules the job to run every 5 seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryFiveSeconds().Do(func() {})EveryFourHours
EveryFourHours schedules the job to run every four hours at the specified minute.
scheduler.NewJobBuilder(nil).EveryFourHours(25)EveryFourMinutes
EveryFourMinutes schedules the job to run every 4 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryFourMinutes().Do(func() {})EveryMinute
EveryMinute schedules the job to run every 1 minute.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryMinute().Do(func() {})EveryOddHour
EveryOddHour schedules the job to run every odd-numbered hour at the specified minute.
scheduler.NewJobBuilder(nil).EveryOddHour(10)EverySecond
EverySecond schedules the job to run every 1 second.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EverySecond().Do(func() {})EverySixHours
EverySixHours schedules the job to run every six hours at the specified minute.
scheduler.NewJobBuilder(nil).EverySixHours(30)EveryTenMinutes
EveryTenMinutes schedules the job to run every 10 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryTenMinutes().Do(func() {})EveryTenSeconds
EveryTenSeconds schedules the job to run every 10 seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryTenSeconds().Do(func() {})EveryThirtyMinutes
EveryThirtyMinutes schedules the job to run every 30 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryThirtyMinutes().Do(func() {})EveryThirtySeconds
EveryThirtySeconds schedules the job to run every 30 seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryThirtySeconds().Do(func() {})EveryThreeHours
EveryThreeHours schedules the job to run every three hours at the specified minute.
scheduler.NewJobBuilder(nil).EveryThreeHours(20)EveryThreeMinutes
EveryThreeMinutes schedules the job to run every 3 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryThreeMinutes().Do(func() {})EveryTwentySeconds
EveryTwentySeconds schedules the job to run every 20 seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryTwentySeconds().Do(func() {})EveryTwoHours
EveryTwoHours schedules the job to run every two hours at the specified minute.
scheduler.NewJobBuilder(nil).EveryTwoHours(15)EveryTwoMinutes
EveryTwoMinutes schedules the job to run every 2 minutes.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryTwoMinutes().Do(func() {})EveryTwoSeconds
EveryTwoSeconds schedules the job to run every 2 seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).EveryTwoSeconds().Do(func() {})Hourly
Hourly schedules the job to run every hour.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).Hourly().Do(func() {})HourlyAt
HourlyAt schedules the job to run every hour at the specified minute.
scheduler.NewJobBuilder(nil).HourlyAt(5)Hours
Hours schedules the job to run every X hours.
scheduler.NewJobBuilder(nil).Every(6).Hours()LastDayOfMonth
LastDayOfMonth schedules the job to run on the last day of each month at a specific time.
scheduler.NewJobBuilder(nil).LastDayOfMonth("23:30")Minutes
Minutes schedules the job to run every X minutes.
scheduler.NewJobBuilder(nil).Every(15).Minutes()Monthly
Monthly schedules the job to run on the first day of each month at midnight.
scheduler.NewJobBuilder(nil).Monthly()MonthlyOn
MonthlyOn schedules the job to run on a specific day of the month at a given time.
scheduler.NewJobBuilder(nil).MonthlyOn(15, "09:30")Quarterly
Quarterly schedules the job to run on the first day of each quarter at midnight.
scheduler.NewJobBuilder(nil).Quarterly()QuarterlyOn
QuarterlyOn schedules the job to run on a specific day of each quarter at a given time.
scheduler.NewJobBuilder(nil).QuarterlyOn(3, "12:00")Seconds
Seconds schedules the job to run every X seconds.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
scheduler.NewJobBuilder(s).
Every(3).
Seconds().
Do(func() {})TwiceDaily
TwiceDaily schedules the job to run daily at two specified hours (e.g., 1 and 13).
scheduler.NewJobBuilder(nil).TwiceDaily(1, 13)TwiceDailyAt
TwiceDailyAt schedules the job to run daily at two specified times (e.g., 1:15 and 13:15).
scheduler.NewJobBuilder(nil).TwiceDailyAt(1, 13, 15)TwiceMonthly
TwiceMonthly schedules the job to run on two specific days of the month at the given time.
scheduler.NewJobBuilder(nil).TwiceMonthly(1, 15, "10:00")Weekly
Weekly schedules the job to run once per week on Sunday at midnight.
scheduler.NewJobBuilder(nil).Weekly()WeeklyOn
WeeklyOn schedules the job to run weekly on a specific day of the week and time. Day uses 0 = Sunday through 6 = Saturday.
scheduler.NewJobBuilder(nil).WeeklyOn(1, "8:00")Yearly
Yearly schedules the job to run on January 1st every year at midnight.
scheduler.NewJobBuilder(nil).Yearly()YearlyOn
YearlyOn schedules the job to run every year on a specific month, day, and time.
scheduler.NewJobBuilder(nil).YearlyOn(12, 25, "06:45")State management
RetainState
RetainState allows the job to retain its state after execution.
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()
builder := scheduler.NewJobBuilder(s).EverySecond().RetainState()
builder.Do(func() {})
builder.Do(func() {})