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.
Get started with gocron v2 in minutes. This guide walks you through creating your first scheduled job.
go get github.com/go-co-op/gocron/v2package main
import (
"fmt"
"time"
"github.com/go-co-op/gocron/v2"
)
func main() {
// Create a new scheduler
s, err := gocron.NewScheduler()
if err != nil {
panic(err)
}
defer s.Shutdown()
// Add a job that runs every minute
j, err := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func() {
fmt.Println("Job running at", time.Now())
}),
)
if err != nil {
panic(err)
}
fmt.Println("Job scheduled with ID:", j.ID())
// Start the scheduler
s.Start()
// Block forever (or until signal)
select {}
}Output:
Job scheduled with ID: 550e8400-e29b-41d4-a716-446655440000
Job running at 2024-01-15 14:30:00
Job running at 2024-01-15 14:31:00
Job running at 2024-01-15 14:32:00
...func NewScheduler(options ...SchedulerOption) (Scheduler, error)Creates a new scheduler instance:
s, err := gocron.NewScheduler()
if err != nil {
log.Fatal(err)
}s.Start()Starts executing jobs in the background. This is non-blocking.
defer s.Shutdown()Always use defer to ensure graceful cleanup when your program exits.
Run every N duration:
// Every 5 seconds
j, _ := s.NewJob(
gocron.DurationJob(5*time.Second),
gocron.NewTask(myFunc),
)
// Every 10 minutes
j, _ = s.NewJob(
gocron.DurationJob(10*time.Minute),
gocron.NewTask(myFunc),
)
// Every hour
j, _ = s.NewJob(
gocron.DurationJob(time.Hour),
gocron.NewTask(myFunc),
)Use cron expressions for time-based schedules:
// Every day at 9 AM (cron format)
j, _ := s.NewJob(
gocron.CronJob("0 9 * * *", false),
gocron.NewTask(myFunc),
)
// Every Monday at 2 PM
j, _ = s.NewJob(
gocron.CronJob("0 14 * * 1", false),
gocron.NewTask(myFunc),
)Cron format: minute hour day month weekday
* = any value0 9 * * * = 9:00 AM every day0 14 * * 1 = 2:00 PM every MondayRun at specific times each day:
// Every day at 9 AM
j, _ := s.NewJob(
gocron.DailyJob(
1,
gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)),
),
gocron.NewTask(myFunc),
)See Advanced: Time-Based Scheduling for WeeklyJob, MonthlyJob, and OneTimeJob.
task := gocron.NewTask(func() {
fmt.Println("Hello from job!")
})task := gocron.NewTask(
func(name string, count int) {
fmt.Printf("Hello %s, count: %d\n", name, count)
},
"Alice",
42,
)Parameters are passed after the function.
If the first parameter is context.Context, gocron injects it:
task := gocron.NewTask(func(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Job cancelled")
return
default:
doWork()
}
})Context is cancelled when:
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithName("my-cleanup-job"),
)Names make jobs easier to identify in logs and metrics.
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithTags("cleanup", "database"),
)Use tags to group and manage related jobs.
Run once immediately, then follow normal schedule:
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithStartAt(gocron.WithStartImmediately()),
)package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/go-co-op/gocron/v2"
)
func main() {
// Create scheduler
s, err := gocron.NewScheduler()
if err != nil {
log.Fatal(err)
}
defer func() {
if err := s.Shutdown(); err != nil {
log.Printf("Shutdown error: %v", err)
}
}()
// Add cleanup job (every 5 minutes)
_, err = s.NewJob(
gocron.DurationJob(5*time.Minute),
gocron.NewTask(cleanupOldFiles),
gocron.WithName("cleanup-files"),
gocron.WithTags("maintenance"),
)
if err != nil {
log.Fatal(err)
}
// Add report job (daily at 9 AM)
_, err = s.NewJob(
gocron.DailyJob(
1,
gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0)),
),
gocron.NewTask(generateDailyReport),
gocron.WithName("daily-report"),
gocron.WithTags("reporting"),
)
if err != nil {
log.Fatal(err)
}
// Start scheduler
s.Start()
fmt.Println("Scheduler started. Press Ctrl+C to exit.")
// Wait for interrupt signal
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
<-sigCh
fmt.Println("\nShutting down gracefully...")
}
func cleanupOldFiles() {
fmt.Println("Cleaning up old files...")
// Your cleanup logic here
}
func generateDailyReport() {
fmt.Println("Generating daily report...")
// Your report logic here
}Now that you have a working scheduler, explore these topics:
Problem: Jobs are created but not executing.
Solution: Make sure you called s.Start():
s.Start() // Don't forget this!
select {}Problem: Program exits before jobs run.
Solution: Add a blocking statement:
s.Start()
select {} // Block foreverOr wait for a signal:
s.Start()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
<-sigChProblem: Errors during shutdown.
Solution: Always use defer and check errors:
defer func() {
if err := s.Shutdown(); err != nil {
log.Printf("Shutdown error: %v", err)
}
}()Install with Tessl CLI
npx tessl i tessl/golang-github-com-go-co-op-gocron-v2docs
api
examples
guides