A Golang job scheduling library that lets you run Go functions at pre-determined intervals using cron expressions, fixed durations, daily, weekly, monthly, or one-time schedules with support for distributed deployments.
Options for controlling when jobs start and stop.
func WithStartAt(option StartAtOption) JobOptionControls first execution time. Options:
func WithStartImmediately() StartAtOption
func WithStartDateTime(start time.Time) StartAtOption
func WithStartDateTimePast(start time.Time) StartAtOptionRuns job immediately when scheduler starts (or when NewJob called if already running). After immediate run, follows normal schedule.
// Daily at 9 AM, but run immediately on startup
j, _ := s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0))),
gocron.NewTask(doMorningTask),
gocron.WithStartAt(gocron.WithStartImmediately()),
)Sets first execution to specific future time. Must be in future (relative to scheduler clock).
Errors:
ErrWithStartDateTimePast: time is in pastErrWithStartDateTimePastZero: time is zeroErrStartTimeLaterThanEndTime: start is after already-set stop time// Daily at 9 AM, starting tomorrow
tomorrow9AM := time.Now().Add(24*time.Hour).Truncate(24*time.Hour).Add(9*time.Hour)
j, _ := s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0))),
gocron.NewTask(doMorningTask),
gocron.WithStartAt(gocron.WithStartDateTime(tomorrow9AM)),
)Like WithStartDateTime but allows past times. Scheduler computes next run from start forward.
Use case: Backdate schedule to align with calendar boundary (e.g., weekly job from last Monday for proper alignment).
Errors:
ErrWithStartDateTimePastZero: time is zero// Weekly Monday at 9 AM, backdated to last Monday
lastMonday := time.Now().AddDate(0, 0, -7)
j, _ := s.NewJob(
gocron.WeeklyJob(
1,
gocron.NewWeekdays(time.Monday),
gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)),
),
gocron.NewTask(doWeeklyReport),
gocron.WithStartAt(gocron.WithStartDateTimePast(lastMonday)),
)
// Runs next Monday at 9 AMfunc WithStopAt(option StopAtOption) JobOptionSets time after which job no longer runs.
func WithStopDateTime(end time.Time) StopAtOptionParameters:
end: Must be in future and not before start time (if set)Errors:
ErrWithStopDateTimePast: end is in past or zeroErrStopTimeEarlierThanStartTime: end is before start// Run for 24 hours
end := time.Now().Add(24 * time.Hour)
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithStopAt(gocron.WithStopDateTime(end)),
)
// Run daily for one week
weekFromNow := time.Now().AddDate(0, 0, 7)
j, _ = s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0))),
gocron.NewTask(doTask),
gocron.WithStopAt(gocron.WithStopDateTime(weekFromNow)),
)func WithLimitedRuns(limit uint) JobOptionLimits job to run at most N times, then automatically removes it.
Parameters:
limit: Must be > 0Errors:
ErrWithLimitedRunsZero: limit is 0// Run exactly 3 times then stop
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithLimitedRuns(3),
)
// Runs at: 0, 60s, 120s, then removedUses:
Note: Skipped runs (due to singleton mode or concurrency limits) don't count. Only actual executions count.
func WithContext(ctx context.Context) JobOptionSets parent context for the job. gocron creates a child context and injects it as the first argument if task function accepts context.Context as first parameter.
Parameters:
ctx: Must not be nilErrors:
ErrWithContextNil: ctx is nilContext cancellation:
ctx, cancel := context.WithCancel(context.Background())
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func(ctx context.Context) {
select {
case <-ctx.Done():
log.Println("Context cancelled")
return
default:
doWork()
}
}),
gocron.WithContext(ctx),
)
// Later: cancel() stops job scheduling and cancels running job
cancel()Context injection: Automatic if first parameter is context.Context. Don't pass context via NewTask parameters.
// CORRECT: context auto-injected
task := gocron.NewTask(func(ctx context.Context, msg string) {
fmt.Println(msg)
}, "hello")
// INCORRECT: don't pass context manually
// task := gocron.NewTask(func(ctx context.Context) { ... }, myContext)Uses:
context.WithTimeout)j, _ := s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0))),
gocron.NewTask(func() {
log.Println("Running daily task")
}),
gocron.WithStartAt(gocron.WithStartImmediately()),
)
// Runs immediately, then daily at 9 AMlaunchTime := time.Date(2024, 3, 15, 10, 0, 0, 0, time.Local)
j, _ := s.NewJob(
gocron.DurationJob(time.Hour),
gocron.NewTask(func() {
checkStatus()
}),
gocron.WithStartAt(gocron.WithStartDateTime(launchTime)),
)
// First run at launch time, then hourlystart := time.Now()
end := start.Add(24 * time.Hour)
j, _ := s.NewJob(
gocron.DurationJob(5*time.Minute),
gocron.NewTask(func() {
monitorSystem()
}),
gocron.WithStartAt(gocron.WithStartDateTime(start)),
gocron.WithStopAt(gocron.WithStopDateTime(end)),
)
// Runs every 5 minutes for 24 hoursj, _ := s.NewJob(
gocron.DurationJob(10*time.Second),
gocron.NewTask(func() error {
return attemptOperation()
}),
gocron.WithLimitedRuns(5), // Try up to 5 times
gocron.WithEventListeners(
gocron.AfterJobRuns(func(jobID uuid.UUID, jobName string) {
log.Println("Operation succeeded")
}),
),
)ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return doWork()
}
}),
gocron.WithContext(ctx),
)
// Jobs timeout after 5 minutes totalcampaignStart := time.Date(2024, 3, 1, 0, 0, 0, 0, time.Local)
campaignEnd := time.Date(2024, 3, 31, 23, 59, 59, 0, time.Local)
j, _ := s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(
gocron.NewAtTime(9, 0, 0),
gocron.NewAtTime(15, 0, 0),
gocron.NewAtTime(20, 0, 0),
)),
gocron.NewTask(func() {
sendCampaignEmail()
}),
gocron.WithStartAt(gocron.WithStartDateTime(campaignStart)),
gocron.WithStopAt(gocron.WithStopDateTime(campaignEnd)),
)
// Sends emails 3x daily for entire monthAll timing options can be combined:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
start := time.Now().Add(1 * time.Hour)
end := time.Now().Add(25 * time.Hour)
j, _ := s.NewJob(
gocron.DurationJob(5*time.Minute),
gocron.NewTask(func(ctx context.Context) {
performTask()
}),
gocron.WithContext(ctx),
gocron.WithStartAt(gocron.WithStartDateTime(start)),
gocron.WithStopAt(gocron.WithStopDateTime(end)),
gocron.WithLimitedRuns(100),
)
// Starts in 1 hour, runs for 24 hours, max 100 timesInstall with Tessl CLI
npx tessl i tessl/golang-github-com-go-co-op-gocron-v2@2.19.1docs
api
examples
guides