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.
How to create jobs with NewJob, job definitions, and tasks in gocron v2.
Creating a job requires three components:
j, err := s.NewJob(
jobDefinition, // When to run
task, // What to do
options... // How to run
)func DurationJob(duration time.Duration) JobDefinitionRuns every duration:
// Every 30 seconds
j, _ := s.NewJob(
gocron.DurationJob(30*time.Second),
gocron.NewTask(myFunc),
)
// Every 5 minutes
j, _ = s.NewJob(
gocron.DurationJob(5*time.Minute),
gocron.NewTask(myFunc),
)
// Every hour
j, _ = s.NewJob(
gocron.DurationJob(time.Hour),
gocron.NewTask(myFunc),
)Interval calculation: By default, intervals are measured from the scheduled start time, not completion time.
Use WithIntervalFromCompletion() to calculate from completion:
j, _ := s.NewJob(
gocron.DurationJob(5*time.Minute),
gocron.NewTask(myFunc),
gocron.WithIntervalFromCompletion(),
)See Concurrency: Interval Calculation for details.
func DurationRandomJob(min, max time.Duration) JobDefinitionRuns at a random interval between min and max:
// Between 1 and 5 minutes
j, _ := s.NewJob(
gocron.DurationRandomJob(time.Minute, 5*time.Minute),
gocron.NewTask(myFunc),
)Use cases:
Randomness: New interval is chosen after each execution.
func CronJob(cronExpression string, withSeconds bool) JobDefinitionRuns according to a cron expression:
// Every day at 9 AM
j, _ := s.NewJob(
gocron.CronJob("0 9 * * *", false),
gocron.NewTask(myFunc),
)
// Every minute
j, _ = s.NewJob(
gocron.CronJob("* * * * *", false),
gocron.NewTask(myFunc),
)
// With seconds (every 30 seconds)
j, _ = s.NewJob(
gocron.CronJob("30 * * * * *", true),
gocron.NewTask(myFunc),
)Cron format:
minute hour day month weekdaysecond minute hour day month weekdayExamples:
0 9 * * * — 9:00 AM every day0 9 * * 1 — 9:00 AM every Monday*/15 * * * * — Every 15 minutes0 9-17 * * 1-5 — Every hour from 9 AM to 5 PM, Monday-Fridayfunc DailyJob(interval uint, atTimes AtTimes) JobDefinitionRuns every interval days at specified times:
// Every day at 9 AM
j, _ := s.NewJob(
gocron.DailyJob(
1,
gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)),
),
gocron.NewTask(myFunc),
)
// Every day at 9 AM and 5 PM
j, _ = s.NewJob(
gocron.DailyJob(
1,
gocron.NewAtTimes(
gocron.NewAtTime(9, 0, 0),
gocron.NewAtTime(17, 0, 0),
),
),
gocron.NewTask(myFunc),
)
// Every 2 days at noon
j, _ = s.NewJob(
gocron.DailyJob(2, gocron.NewAtTimes(gocron.NewAtTime(12, 0, 0))),
gocron.NewTask(myFunc),
)See Advanced: Time-Based Scheduling for details and edge cases.
func WeeklyJob(interval uint, daysOfTheWeek Weekdays, atTimes AtTimes) JobDefinitionRuns every interval weeks on specified days:
// Every Monday at 9 AM
j, _ := s.NewJob(
gocron.WeeklyJob(
1,
gocron.NewWeekdays(time.Monday),
gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)),
),
gocron.NewTask(myFunc),
)
// Every Monday and Friday at 9 AM
j, _ = s.NewJob(
gocron.WeeklyJob(
1,
gocron.NewWeekdays(time.Monday, time.Friday),
gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)),
),
gocron.NewTask(myFunc),
)
// Every 2 weeks on Friday at 5 PM
j, _ = s.NewJob(
gocron.WeeklyJob(
2,
gocron.NewWeekdays(time.Friday),
gocron.NewAtTimes(gocron.NewAtTime(17, 0, 0)),
),
gocron.NewTask(myFunc),
)Week convention: Week starts on Sunday (Go's time.Weekday).
See Advanced: Time-Based Scheduling for details.
func MonthlyJob(interval uint, daysOfTheMonth DaysOfTheMonth, atTimes AtTimes) JobDefinitionRuns every interval months on specified days:
// On the 1st of every month at noon
j, _ := s.NewJob(
gocron.MonthlyJob(
1,
gocron.NewDaysOfTheMonth(1),
gocron.NewAtTimes(gocron.NewAtTime(12, 0, 0)),
),
gocron.NewTask(myFunc),
)
// On the last day of every month
j, _ = s.NewJob(
gocron.MonthlyJob(
1,
gocron.NewDaysOfTheMonth(-1),
gocron.NewAtTimes(gocron.NewAtTime(0, 0, 0)),
),
gocron.NewTask(myFunc),
)
// On the 1st and 15th at noon
j, _ = s.NewJob(
gocron.MonthlyJob(
1,
gocron.NewDaysOfTheMonth(1, 15),
gocron.NewAtTimes(gocron.NewAtTime(12, 0, 0)),
),
gocron.NewTask(myFunc),
)Day numbering:
See Advanced: Time-Based Scheduling for details and edge cases.
func OneTimeJob(startAt OneTimeJobStartAtOption) JobDefinitionRuns once, then automatically removed:
// Run immediately
j, _ := s.NewJob(
gocron.OneTimeJob(gocron.OneTimeJobStartImmediately()),
gocron.NewTask(myFunc),
)
// Run in 1 hour
futureTime := time.Now().Add(time.Hour)
j, _ = s.NewJob(
gocron.OneTimeJob(gocron.OneTimeJobStartDateTime(futureTime)),
gocron.NewTask(myFunc),
)
// Run at multiple times
j, _ = s.NewJob(
gocron.OneTimeJob(gocron.OneTimeJobStartDateTimes(
time.Now().Add(1*time.Hour),
time.Now().Add(2*time.Hour),
time.Now().Add(3*time.Hour),
)),
gocron.NewTask(myFunc),
)See Advanced: Time-Based Scheduling for details.
func NewTask(function any, parameters ...any) TaskWraps a function and its parameters:
// Simple function
task := gocron.NewTask(func() {
fmt.Println("Hello!")
})
// Function with parameters
task = gocron.NewTask(
func(name string, count int) {
fmt.Printf("Hello %s, count: %d\n", name, count)
},
"Alice",
42,
)
// Function with return value (return value ignored)
task = gocron.NewTask(func() error {
return doWork()
})Parameter passing: Parameters are passed after the function and must match the function signature.
If the first parameter is context.Context, gocron injects it:
task := gocron.NewTask(func(ctx context.Context) {
select {
case <-ctx.Done():
log.Println("Job cancelled")
return
default:
doWork()
}
})Context is cancelled when:
WithContext) is cancelledtask := gocron.NewTask(
func(ctx context.Context, name string, count int) {
select {
case <-ctx.Done():
return
default:
processData(name, count)
}
},
"Alice",
42,
)Context is always first if present; other parameters follow.
Return values are ignored by default. To capture errors, use event listeners:
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func() error {
return doWork()
}),
gocron.WithEventListeners(
gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
log.Printf("Job %s failed: %v", jobName, err)
}),
),
)_, err := s.NewJob(
gocron.DurationJob(time.Minute),
nil, // Task is nil
)
// Returns: ErrNewJobTaskNil_, err := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask("not a function"), // Not a function
)
// Returns: ErrNewJobTaskNotFunc_, err := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(
func(name string, count int) {},
"Alice", // Missing second parameter
),
)
// Returns: ErrNewJobWrongNumberOfParameters_, err := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(
func(name string, count int) {},
"Alice",
"not an int", // Wrong type
),
)
// Returns: ErrNewJobWrongTypeOfParametersCapture variables from outer scope:
userID := 123
task := gocron.NewTask(func() {
// userID is captured from outer scope
processUser(userID)
})Warning: Be careful with loop variables. Use parameter passing instead:
// Bad: all jobs use the same userID (last value)
for _, userID := range userIDs {
s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func() {
processUser(userID) // userID is captured, may change
}),
)
}
// Good: pass userID as parameter
for _, userID := range userIDs {
s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(
func(id int) {
processUser(id)
},
userID, // Passed as parameter
),
)
}Pass methods as tasks:
type Worker struct {
name string
}
func (w *Worker) DoWork() {
fmt.Printf("Worker %s doing work\n", w.name)
}
w := &Worker{name: "Alice"}
task := gocron.NewTask(w.DoWork)func Process[T any](item T) {
fmt.Printf("Processing %v\n", item)
}
// Pass type-specific function
task := gocron.NewTask(Process[string], "hello")Duration-based for regular intervals:
gocron.DurationJob(5*time.Minute)Cron for complex time-based schedules:
gocron.CronJob("0 9-17 * * 1-5", false) // Business hoursTime-based for readable schedules:
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)))task := gocron.NewTask(func(ctx context.Context) {
select {
case <-ctx.Done():
cleanup()
return
default:
doWork()
}
})// Good: explicit parameters
task := gocron.NewTask(
func(id int, name string) {
process(id, name)
},
userID,
userName,
)
// Bad: closure capture (harder to test, debug)
task := gocron.NewTask(func() {
process(userID, userName)
})task := gocron.NewTask(func() error {
if err := doWork(); err != nil {
return fmt.Errorf("work failed: %w", err)
}
return nil
})Use with event listeners to capture errors:
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
task,
gocron.WithEventListeners(
gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
log.Printf("Job failed: %v", err)
}),
),
)j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithName("my-cleanup-job"),
)Named jobs are easier to identify in logs and metrics.
Install with Tessl CLI
npx tessl i tessl/golang-github-com-go-co-op-gocron-v2@2.19.1docs
api
examples
guides