Skip to main content

robfig/cron - Go cron library guide

At 04:30, every day.

Next runs · UTC

Calendar

September 2026 · UTC

0 runs

About this tool

github.com/robfig/cron/v3 is the dominant cron library in the Go ecosystem - used by Kubernetes, Caddy, GitLab Runner and many more. The default parser is strict standard cron (5-field, no L/W); the parser is configurable via bitmask options to enable seconds, year and descriptors.
For last-day-of-month patterns in robfig/cron see the dedicated guide.

Basic usage

go
package main


import (
    "log"
    "time"


    "github.com/robfig/cron/v3"
)


func main() {
    c := cron.New(cron.WithLocation(time.UTC))


    c.AddFunc("0 9 * * 1-5", func() {
        log.Println("Weekday 09:00 UTC tick")
    })


    c.AddFunc("@hourly", func() {
        log.Println("Hourly tick")
    })


    c.Start()
    select {} // block forever
}

Parser options for seconds & descriptors

go
parser := cron.NewParser(
    cron.SecondOptional | cron.Minute | cron.Hour |
    cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)


c := cron.New(cron.WithParser(parser))


// 6-field with seconds (every 30 seconds):
c.AddFunc("*/30 * * * * *", everyThirtySeconds)


// Standard 5-field still works:
c.AddFunc("*/5 * * * *", everyFiveMinutes)


// Descriptor:
c.AddFunc("@daily", dailyTask)

Implementing the Job interface

go
type ReportJob struct {
    db *sql.DB
}


func (j *ReportJob) Run() {
    rows, _ := j.db.Query(...)
    // ...
}


c.AddJob("0 9 * * 1-5", &ReportJob{db: myDB})

AddJob accepts any type implementing cron.Job (a single Run() method). Useful when the job needs dependencies - pass them via the struct fields.

robfig/cron pitfalls

  • Local timezone by default - pass cron.WithLocation(time.UTC) for UTC.
  • Wraparound for `*/N` - same as Unix cron; */7 in the hour field doesn't divide 24 evenly.
  • No `L` / `W` / `#` - use runtime checks or switch to github.com/adhocore/gronx / github.com/gorhill/cronexpr.
  • Goroutine-per-job - long-running jobs don't block other ticks. If you need single-instance semantics, gate with a mutex or use cron.SkipIfStillRunning middleware.

Examples

  • 0 18 * * *
    Every day at 18:00
  • 0 */5 * * *
    Every 5 hours
  • 0 18 * * 1-5
    Weekdays at 18:00
  • 0 0 1 * *
    Once a month, on the 1st at midnight

Cheatsheet

Full cheatsheet
FieldReq.RangeWildcards
minuteyes0-59, - * /
houryes0-23, - * /
day of monthyes1-31, - * /
monthyes1-12 or JAN-DEC, - * /
day of weekyes0-6 or names; some dialects use 0-7 or 1-7 — see Dialects, - * /

Frequently asked questions

Syntax

What cron syntax does robfig/cron use?

By default the v3 parser accepts standard 5-field cron. Use cron.NewParser with cron.SecondOptional | cron.Minute | … to enable the seconds field. Aliases like @hourly, @daily, @weekly, @monthly, @yearly are supported via cron.Descriptor.

Does robfig/cron support L, W, # modifiers?

No. robfig/cron v3 does not support L, W, or #; use application logic for those rules. It does accept ? as an alternative to * in day-of-month and day-of-week fields.

What are robfig/cron's @-aliases?

@yearly (or @annually), @monthly, @weekly, @daily (or @midnight), @hourly are equivalent to the standard cron expressions for those cadences. Enable them with cron.Descriptor in your parser options.

How many fields does robfig/cron v3 accept by default?

robfig/cron v3 accepts five fields by default: minute, hour, day of month, month, and day of week. A leading seconds field requires an explicit parser option.

How do I enable seconds in robfig/cron v3?

Create the scheduler with cron.New(cron.WithSeconds()). The resulting parser expects six fields, with seconds first; */30 * * * * * then runs every 30 seconds. Expression: */30 * * * * *

Open in editor

Timing

Can robfig/cron use durations instead of field expressions?

Yes. @every 1h30m schedules a job at a fixed duration interval. It is useful when elapsed time matters more than alignment to a wall-clock minute.

Days and months

What does ? mean in a robfig/cron day field?

In robfig/cron, ? is accepted as a wildcard in the day-of-month or day-of-week field. It does not add Quartz's broader day-field rules.

How does robfig/cron combine restricted day-of-month and day-of-week fields?

robfig/cron uses OR semantics when both day fields are restricted. A date matches when either the day-of-month condition or the day-of-week condition is true.

Timezones and DST

How do I set the timezone in robfig/cron?

Pass cron.WithLocation(time.UTC) (or another *time.Location) to cron.New(...). By default it uses the host's local timezone, which is rarely what you want in a containerised deployment.

Can one robfig/cron entry select its own timezone?

Yes. Prefix the specification with CRON_TZ=Area/Location to select an IANA timezone for that entry, or use WithLocation to set the scheduler-wide location.

What happens to robfig/cron jobs during DST changes?

Calendar schedules follow the configured location. A time inside a spring-forward gap may not run, and a repeated fall-back wall-clock time requires idempotent job handling.

Platforms

Does robfig/cron keep schedules after a Go process exits?

No. robfig/cron runs in process; restart code must rebuild the scheduler and register every job again. Persist schedule definitions separately if users can edit them.

Debugging

How can robfig/cron prevent overlapping runs?

Wrap the job with cron.SkipIfStillRunning(logger) to skip a tick while the prior run is active, or cron.DelayIfStillRunning(logger) to serialize delayed runs.

How should I handle an invalid robfig/cron specification?

Check the error returned by AddFunc or AddJob. Invalid syntax is rejected during registration, so do not discard that error in production code.

Crontap

Can Crontap run a robfig/cron schedule?

Yes. You can run a compatible HTTP job on Crontap, but Go callbacks must first be exposed through an endpoint and nonstandard forms such as @every need translation.

Write the cron here. Run it on Crontap.

Point Crontap at any URL. Pick any cron. Done.

WordPress, Shopify, Railway, Cloud Run, Vercel, HubSpot, Ghost, your own box. If it answers HTTP, Crontap can drive it on a clock you can read, in the timezone that actually matters, and page you when something breaks.

1.9k+ teams · Free forever tier · No credit card