An open API service indexing awesome lists of open source software.

https://github.com/goforj/scheduler

Laravel-inspired job scheduler for Go with a fluent API, built on gocron.
https://github.com/goforj/scheduler

background-jobs builder-pattern cron developer-tools distributed-locking fluent-api go gocron goforj golang job-scheduler laravel-inspired redis scheduler task-scheduling

Last synced: 30 days ago
JSON representation

Laravel-inspired job scheduler for Go with a fluent API, built on gocron.

Awesome Lists containing this project

README

          


scheduler logo


A fluent, Laravel-inspired scheduler for Go that wraps gocron with expressive APIs for defining, filtering, and controlling scheduled jobs.


Go Reference
License: MIT
Go Test
Go version
Latest tag

Tests

Go Report Card

## 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

```go
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

```go
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`](./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

| Group | Functions |
|------:|-----------|
| **Adapters** | [Lock](#lock) [Run](#run) [Unlock](#unlock) |
| **Commands** | [Command](#command) |
| **Concurrency** | [WithoutOverlapping](#withoutoverlapping) [WithoutOverlappingWithLocker](#withoutoverlappingwithlocker) |
| **Configuration** | [Timezone](#timezone) [WithCommandRunner](#withcommandrunner) [WithNowFunc](#withnowfunc) |
| **Construction** | [NewJobBuilder](#newjobbuilder) |
| **Diagnostics** | [CronExpr](#cronexpr) [Error](#error) [Job](#job) [PrintJobsList](#printjobslist) |
| **Execution** | [RunInBackground](#runinbackground) |
| **Filters** | [Between](#between) [Days](#days) [Environments](#environments) [Fridays](#fridays) [Mondays](#mondays) [Saturdays](#saturdays) [Skip](#skip) [Sundays](#sundays) [Thursdays](#thursdays) [Tuesdays](#tuesdays) [UnlessBetween](#unlessbetween) [Wednesdays](#wednesdays) [Weekdays](#weekdays) [Weekends](#weekends) [When](#when) |
| **Hooks** | [After](#after) [Before](#before) [OnFailure](#onfailure) [OnSuccess](#onsuccess) |
| **Locking** | [NewRedisLocker](#newredislocker) |
| **Metadata** | [JobMetadata](#jobmetadata) [Name](#name) |
| **Scheduling** | [Cron](#cron) [Daily](#daily) [DailyAt](#dailyat) [DaysOfMonth](#daysofmonth) [Do](#do) [Every](#every) [EveryFifteenMinutes](#everyfifteenminutes) [EveryFifteenSeconds](#everyfifteenseconds) [EveryFiveMinutes](#everyfiveminutes) [EveryFiveSeconds](#everyfiveseconds) [EveryFourHours](#everyfourhours) [EveryFourMinutes](#everyfourminutes) [EveryMinute](#everyminute) [EveryOddHour](#everyoddhour) [EverySecond](#everysecond) [EverySixHours](#everysixhours) [EveryTenMinutes](#everytenminutes) [EveryTenSeconds](#everytenseconds) [EveryThirtyMinutes](#everythirtyminutes) [EveryThirtySeconds](#everythirtyseconds) [EveryThreeHours](#everythreehours) [EveryThreeMinutes](#everythreeminutes) [EveryTwentySeconds](#everytwentyseconds) [EveryTwoHours](#everytwohours) [EveryTwoMinutes](#everytwominutes) [EveryTwoSeconds](#everytwoseconds) [Hourly](#hourly) [HourlyAt](#hourlyat) [Hours](#hours) [LastDayOfMonth](#lastdayofmonth) [Minutes](#minutes) [Monthly](#monthly) [MonthlyOn](#monthlyon) [Quarterly](#quarterly) [QuarterlyOn](#quarterlyon) [Seconds](#seconds) [TwiceDaily](#twicedaily) [TwiceDailyAt](#twicedailyat) [TwiceMonthly](#twicemonthly) [Weekly](#weekly) [WeeklyOn](#weeklyon) [Yearly](#yearly) [YearlyOn](#yearlyon) |
| **State management** | [RetainState](#retainstate) |

## Adapters

### Lock

Lock invokes the underlying function.

```go
client := redis.NewClient(&redis.Options{})
locker := scheduler.NewRedisLocker(client, time.Minute)
lock, _ := locker.Lock(context.Background(), "job")
_ = lock.Unlock(context.Background())
```

### Run

Run executes the underlying function.

```go
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.

```go
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.

```go
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.

```go
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).

```go
scheduler.NewJobBuilder(nil).
Timezone("America/New_York").
Daily()
```

### WithCommandRunner

WithCommandRunner overrides command execution (default: exec.CommandContext).

```go
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.

```go
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.

```go
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.

```go
builder := scheduler.NewJobBuilder(nil).Cron("0 9 * * *")
fmt.Println(builder.CronExpr())
```

### Error

Error returns the error if any occurred during job scheduling.

```go
builder := scheduler.NewJobBuilder(nil).DailyAt("bad")
fmt.Println(builder.Error())
```

### Job

Job returns the last scheduled gocron.Job instance, if available.

```go
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.

```go
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.

```go
scheduler.NewJobBuilder(nil).
RunInBackground().
Command("noop")
```

## Filters

### Between

Between limits the job to run between the provided HH:MM times (inclusive).

```go
scheduler.NewJobBuilder(nil).
Between("09:00", "17:00").
EveryMinute()
```

### Days

Days limits the job to a specific set of weekdays.

```go
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").

```go
scheduler.NewJobBuilder(nil).Environments("production").Daily()
```

### Fridays

Fridays limits the job to Fridays.

```go
scheduler.NewJobBuilder(nil).Fridays().DailyAt("09:00")
```

### Mondays

Mondays limits the job to Mondays.

```go
scheduler.NewJobBuilder(nil).Mondays().DailyAt("09:00")
```

### Saturdays

Saturdays limits the job to Saturdays.

```go
scheduler.NewJobBuilder(nil).Saturdays().DailyAt("09:00")
```

### Skip

Skip prevents scheduling the job if the provided condition returns true.

```go
enabled := false
scheduler.NewJobBuilder(nil).
Skip(func() bool { return !enabled }).
Daily()
```

### Sundays

Sundays limits the job to Sundays.

```go
scheduler.NewJobBuilder(nil).Sundays().DailyAt("09:00")
```

### Thursdays

Thursdays limits the job to Thursdays.

```go
scheduler.NewJobBuilder(nil).Thursdays().DailyAt("09:00")
```

### Tuesdays

Tuesdays limits the job to Tuesdays.

```go
scheduler.NewJobBuilder(nil).Tuesdays().DailyAt("09:00")
```

### UnlessBetween

UnlessBetween prevents the job from running between the provided HH:MM times.

```go
scheduler.NewJobBuilder(nil).
UnlessBetween("22:00", "06:00").
EveryMinute()
```

### Wednesdays

Wednesdays limits the job to Wednesdays.

```go
scheduler.NewJobBuilder(nil).Wednesdays().DailyAt("09:00")
```

### Weekdays

Weekdays limits the job to run only on weekdays (Mon-Fri).

```go
scheduler.NewJobBuilder(nil).Weekdays().DailyAt("09:00")
```

### Weekends

Weekends limits the job to run only on weekends (Sat-Sun).

```go
scheduler.NewJobBuilder(nil).Weekends().DailyAt("10:00")
```

### When

When only schedules the job if the provided condition returns true.

```go
flag := true
scheduler.NewJobBuilder(nil).
When(func() bool { return flag }).
Daily()
```

## Hooks

### After

After sets a hook to run after task execution.

```go
scheduler.NewJobBuilder(nil).
After(func() { println("after") }).
Daily()
```

### Before

Before sets a hook to run before task execution.

```go
scheduler.NewJobBuilder(nil).
Before(func() { println("before") }).
Daily()
```

### OnFailure

OnFailure sets a hook to run after failed task execution.

```go
scheduler.NewJobBuilder(nil).
OnFailure(func() { println("failure") }).
Daily()
```

### OnSuccess

OnSuccess sets a hook to run after successful task execution.

```go
scheduler.NewJobBuilder(nil).
OnSuccess(func() { println("success") }).
Daily()
```

## Locking

### NewRedisLocker

NewRedisLocker creates a RedisLocker with a client and TTL.

```go
client := redis.NewClient(&redis.Options{}) // replace with your client
locker := scheduler.NewRedisLocker(client, time.Minute)
_, _ = locker.Lock(context.Background(), "job")
```

## Metadata

### JobMetadata

JobMetadata returns a copy of the tracked job metadata keyed by job ID.

```go
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.

```go
scheduler.NewJobBuilder(nil).
Name("cache:refresh").
HourlyAt(15)
```

## Scheduling

### Cron

Cron sets the cron expression for the job.

```go
builder := scheduler.NewJobBuilder(nil).Cron("15 3 * * *")
fmt.Println(builder.CronExpr())
```

### Daily

Daily schedules the job to run once per day at midnight.

```go
scheduler.NewJobBuilder(nil).Daily()
```

### DailyAt

DailyAt schedules the job to run daily at a specific time (e.g., "13:00").

```go
scheduler.NewJobBuilder(nil).DailyAt("12:30")
```

### DaysOfMonth

DaysOfMonth schedules the job to run on specific days of the month at a given time.

```go
scheduler.NewJobBuilder(nil).DaysOfMonth([]int{5, 20}, "07:15")
```

### Do

Do schedules the job with the provided task function.

```go
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.

```go
scheduler.NewJobBuilder(nil).
Every(10).
Minutes()
```

### EveryFifteenMinutes

EveryFifteenMinutes schedules the job to run every 15 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryFifteenMinutes().Do(func() {})
```

### EveryFifteenSeconds

EveryFifteenSeconds schedules the job to run every 15 seconds.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryFifteenSeconds().Do(func() {})
```

### EveryFiveMinutes

EveryFiveMinutes schedules the job to run every 5 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryFiveMinutes().Do(func() {})
```

### EveryFiveSeconds

EveryFiveSeconds schedules the job to run every 5 seconds.

```go
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.

```go
scheduler.NewJobBuilder(nil).EveryFourHours(25)
```

### EveryFourMinutes

EveryFourMinutes schedules the job to run every 4 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryFourMinutes().Do(func() {})
```

### EveryMinute

EveryMinute schedules the job to run every 1 minute.

```go
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.

```go
scheduler.NewJobBuilder(nil).EveryOddHour(10)
```

### EverySecond

EverySecond schedules the job to run every 1 second.

```go
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.

```go
scheduler.NewJobBuilder(nil).EverySixHours(30)
```

### EveryTenMinutes

EveryTenMinutes schedules the job to run every 10 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryTenMinutes().Do(func() {})
```

### EveryTenSeconds

EveryTenSeconds schedules the job to run every 10 seconds.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryTenSeconds().Do(func() {})
```

### EveryThirtyMinutes

EveryThirtyMinutes schedules the job to run every 30 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryThirtyMinutes().Do(func() {})
```

### EveryThirtySeconds

EveryThirtySeconds schedules the job to run every 30 seconds.

```go
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.

```go
scheduler.NewJobBuilder(nil).EveryThreeHours(20)
```

### EveryThreeMinutes

EveryThreeMinutes schedules the job to run every 3 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryThreeMinutes().Do(func() {})
```

### EveryTwentySeconds

EveryTwentySeconds schedules the job to run every 20 seconds.

```go
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.

```go
scheduler.NewJobBuilder(nil).EveryTwoHours(15)
```

### EveryTwoMinutes

EveryTwoMinutes schedules the job to run every 2 minutes.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryTwoMinutes().Do(func() {})
```

### EveryTwoSeconds

EveryTwoSeconds schedules the job to run every 2 seconds.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

scheduler.NewJobBuilder(s).EveryTwoSeconds().Do(func() {})
```

### Hourly

Hourly schedules the job to run every hour.

```go
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.

```go
scheduler.NewJobBuilder(nil).HourlyAt(5)
```

### Hours

Hours schedules the job to run every X hours.

```go
scheduler.NewJobBuilder(nil).Every(6).Hours()
```

### LastDayOfMonth

LastDayOfMonth schedules the job to run on the last day of each month at a specific time.

```go
scheduler.NewJobBuilder(nil).LastDayOfMonth("23:30")
```

### Minutes

Minutes schedules the job to run every X minutes.

```go
scheduler.NewJobBuilder(nil).Every(15).Minutes()
```

### Monthly

Monthly schedules the job to run on the first day of each month at midnight.

```go
scheduler.NewJobBuilder(nil).Monthly()
```

### MonthlyOn

MonthlyOn schedules the job to run on a specific day of the month at a given time.

```go
scheduler.NewJobBuilder(nil).MonthlyOn(15, "09:30")
```

### Quarterly

Quarterly schedules the job to run on the first day of each quarter at midnight.

```go
scheduler.NewJobBuilder(nil).Quarterly()
```

### QuarterlyOn

QuarterlyOn schedules the job to run on a specific day of each quarter at a given time.

```go
scheduler.NewJobBuilder(nil).QuarterlyOn(3, "12:00")
```

### Seconds

Seconds schedules the job to run every X seconds.

```go
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).

```go
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).

```go
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.

```go
scheduler.NewJobBuilder(nil).TwiceMonthly(1, 15, "10:00")
```

### Weekly

Weekly schedules the job to run once per week on Sunday at midnight.

```go
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.

```go
scheduler.NewJobBuilder(nil).WeeklyOn(1, "8:00")
```

### Yearly

Yearly schedules the job to run on January 1st every year at midnight.

```go
scheduler.NewJobBuilder(nil).Yearly()
```

### YearlyOn

YearlyOn schedules the job to run every year on a specific month, day, and time.

```go
scheduler.NewJobBuilder(nil).YearlyOn(12, 25, "06:45")
```

## State management

### RetainState

RetainState allows the job to retain its state after execution.

```go
s, _ := gocron.NewScheduler()
s.Start()
defer s.Shutdown()

builder := scheduler.NewJobBuilder(s).EverySecond().RetainState()
builder.Do(func() {})
builder.Do(func() {})
```