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.
Tasks define what to run. Wrap any Go function with NewTask.
type Task func() taskOpaque type returned by NewTask. Use with Scheduler.NewJob or Scheduler.Update.
func NewTask(function any, parameters ...any) TaskWraps a function and its parameters into a Task.
Parameters:
function: Any Go functionparameters: Arguments to pass to the function (must match function signature)Errors (returned by NewJob, not NewTask):
ErrNewJobTaskNil: task is nilErrNewJobTaskNotFunc: function is not of kind reflect.FuncErrNewJobWrongNumberOfParameters: parameter count doesn't match function signatureErrNewJobWrongTypeOfParameters: parameter types don't match function signaturetask := gocron.NewTask(func() {
fmt.Println("Simple task")
})
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
task,
)task := gocron.NewTask(func(msg string, count int) {
fmt.Printf("%s: %d\n", msg, count)
}, "Hello", 42)
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
task,
)task := gocron.NewTask(
func(name string, age int, active bool) {
fmt.Printf("%s (%d years old) is active: %v\n", name, age, active)
},
"Alice",
30,
true,
)func doWork(msg string) {
fmt.Println(msg)
}
task := gocron.NewTask(doWork, "Working...")
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
task,
)If your function's first parameter is context.Context, gocron automatically injects it. Don't pass context via parameters.
// CORRECT: context auto-injected
task := gocron.NewTask(func(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Context cancelled")
return
default:
doWork()
}
})
// INCORRECT: don't pass context manually
// task := gocron.NewTask(func(ctx context.Context) { ... }, myContext)Context must be first parameter:
task := gocron.NewTask(func(ctx context.Context, msg string, count int) {
for i := 0; i < count; i++ {
select {
case <-ctx.Done():
return
default:
fmt.Println(msg, i)
time.Sleep(time.Second)
}
}
}, "Processing", 10)Set parent context via WithContext:
ctx, cancel := context.WithCancel(context.Background())
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func(ctx context.Context) {
select {
case <-ctx.Done():
return
default:
doWork()
}
}),
gocron.WithContext(ctx),
)
// Later: cancel() cancels the context
cancel()See WithContext for details.
Task functions can return error as the last return value. Error is captured and triggers AfterJobRunsWithError listener.
task := gocron.NewTask(func() error {
if err := doWork(); err != nil {
return fmt.Errorf("work failed: %w", err)
}
return nil
})
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)
}),
),
)task := gocron.NewTask(func(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return doWork()
}
})Task functions can return multiple values. Last return value can be error.
// Return value is ignored, error is captured
task := gocron.NewTask(func() (string, error) {
result, err := doWorkWithResult()
return result, err
})Note: gocron doesn't use non-error return values. Only error is captured.
gocron automatically recovers from panics. Panic data is passed to AfterJobRunsWithPanic listener.
task := gocron.NewTask(func() {
panic("something went wrong")
})
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
task,
gocron.WithEventListeners(
gocron.AfterJobRunsWithPanic(func(jobID uuid.UUID, jobName string, recoverData any) {
log.Printf("Job panicked: %v", recoverData)
}),
),
)Note: Job is considered failed and rescheduled normally after panic.
Task functions can be closures that capture variables:
counter := 0
task := gocron.NewTask(func() {
counter++
fmt.Printf("Run count: %d\n", counter)
})Warning: Be careful with mutable shared state. Use proper synchronization if accessing from multiple jobs.
type Worker struct {
name string
}
func (w *Worker) DoWork() {
fmt.Printf("%s working\n", w.name)
}
worker := &Worker{name: "Alice"}
task := gocron.NewTask(worker.DoWork)
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
task,
)Pass dependencies as parameters or via closures:
// Via parameters
task := gocron.NewTask(func(db *sql.DB, logger *log.Logger) {
logger.Println("Running job")
doWorkWithDB(db)
}, database, logger)
// Via closure
task := gocron.NewTask(func() {
logger.Println("Running job")
doWorkWithDB(database)
})func createBackupTask(db *sql.DB, bucket string) gocron.Task {
return gocron.NewTask(func(ctx context.Context) error {
return backupDatabaseToBucket(ctx, db, bucket)
})
}
task := createBackupTask(database, "my-bucket")func process(items ...string) {
for _, item := range items {
fmt.Println(item)
}
}
task := gocron.NewTask(process, "a", "b", "c")func processItems[T any](items []T, processor func(T)) {
for _, item := range items {
processor(item)
}
}
task := gocron.NewTask(processItems[int], []int{1, 2, 3}, func(i int) {
fmt.Println(i * 2)
})Task validation happens at NewJob time, not NewTask time. This allows reusing tasks across jobs:
task := gocron.NewTask(func(msg string) {
fmt.Println(msg)
}, "hello")
// Reuse task
j1, _ := s.NewJob(gocron.DurationJob(time.Minute), task)
j2, _ := s.NewJob(gocron.CronJob("0 * * * *", false), task)// Missing parameter
task := gocron.NewTask(func(msg string) {
fmt.Println(msg)
})
j, err := s.NewJob(gocron.DurationJob(time.Minute), task)
// err: ErrNewJobWrongNumberOfParameters
// Wrong parameter type
task := gocron.NewTask(func(count int) {
fmt.Println(count)
}, "not an int")
j, err := s.NewJob(gocron.DurationJob(time.Minute), task)
// err: ErrNewJobWrongTypeOfParameterstask := gocron.NewTask(func(ctx context.Context, maxAge time.Duration) error {
return cleanupOldRecords(ctx, maxAge)
}, 24*time.Hour)
j, _ := s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(2, 0, 0))),
task,
)task := gocron.NewTask(func(ctx context.Context) error {
return healthCheck(ctx)
})
j, _ := s.NewJob(
gocron.DurationJob(30*time.Second),
task,
gocron.WithSingletonMode(gocron.LimitModeReschedule),
)task := gocron.NewTask(func(ctx context.Context, reportType string, recipients []string) error {
report, err := generateReport(ctx, reportType)
if err != nil {
return err
}
return emailReport(report, recipients)
}, "daily-sales", []string{"team@example.com"})
j, _ := s.NewJob(
gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0))),
task,
)Install with Tessl CLI
npx tessl i tessl/golang-github-com-go-co-op-gocron-v2docs
api
examples
guides