gocron v2: a Go scheduler for functions, not shell commands
Easy and fluent Go cron scheduling. This is a fork from https://github.com/jasonlvhit/gocron
At a glance
- What is it?
- gocron v2 is an in-process Go job scheduler that runs Go functions on duration, cron, daily, weekly, monthly or one-time schedules. It fits services that need scheduling inside the binary, and it is the wrong tool when you need a separate daemon with a web UI.
- Who is it for?
- Adopt gocron v2 if your jobs are Go functions that belong inside an existing service and you want scheduling without a second process or a database-backed control plane. Do not adopt it if you need a standalone daemon with a built-in web UI, or if a single-instance cron on the host already covers the workload.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 29 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What gocron v2 solves, and who it is for
The package runs Go functions at predetermined intervals. That sentence is the whole product. A job is a Go function plus its parameters, and the scheduler decides when that function should next run. There is no shell, no crontab file, no separate daemon process, and no HTTP endpoint you have to expose to register work.
That shape matters for a specific kind of team. If you already run a Go service and want a cleanup task, a poller, a report generator or a cache refresh to live next to the code that uses it, gocron removes the usual glue: writing a wrapper binary, parsing a crontab, or shelling out to a system cron that cannot see your application state. Because the task is a Go function, it can close over a database handle, a logger or a config struct directly.
The README frames the package as "a job scheduling package which lets you run Go functions at pre-determined intervals", and the module path is versioned: github.com/go-co-op/gocron/v2. This is a fork from jasonlvhit/gocron, and the v2 line is the one the README documents. The repository also points to gocron-ui, described as a lightweight web dashboard to monitor, trigger and manage gocron jobs in real time, for readers who want a visual interface. That dashboard is a separate project, not part of the module you import.
Scheduler, job, executor: the three moving parts
The README names three concepts and they map cleanly onto the source layout, which contains scheduler.go, job.go, executor.go and distributed.go at the top level.
The scheduler keeps track of all jobs and sends each job to the executor when it is ready. The job encapsulates a task, meaning a Go function plus its arguments, and it provides the scheduler with the time the job should next run. The executor calls the task and handles the timing complications: singletons that should not overrun each other, and limits on how many jobs run at once.
That division explains where the interesting behaviour lives. Scheduling mode is not a property of the scheduler; it is a property of the job. You pass a job definition such as DurationJob, CronJob, DailyJob, WeeklyJob, MonthlyJob, DurationRandomJob or OneTimeJob as the first argument to NewJob, and a task as the second. The job then answers the question "when next?" for the scheduler.
Interval semantics are also per job. By default the next run is calculated from the scheduled start time, which produces fixed intervals regardless of how long execution takes. With WithIntervalFromCompletion the next run is calculated from completion instead, which keeps a consistent rest period between executions. The README recommends the completion-based mode for rate-limited APIs and resource-intensive jobs where execution time varies. That is a real design decision, not a cosmetic option: with the default, a job that takes longer than its interval will start its next run immediately after finishing, while the completion-based mode will space the runs out.
Installing gocron v2 and scheduling your first job
The README's Quick Start begins with a single go get against the v2 module path:
go get github.com/go-co-op/gocron/v2After that, the smallest useful program creates a scheduler, registers a job, starts the scheduler and blocks. The README's example uses a duration job that fires every ten seconds and a task function that takes a string and an int:
s, err := gocron.NewScheduler()
if err != nil {
// handle error
}
j, err := s.NewJob(
gocron.DurationJob(10*time.Second),
gocron.NewTask(
func(a string, b int) {
// do things
},
"hello",
1,
),
)
if err != nil {
// handle error
}
fmt.Println(j.ID())
s.Start()Two details are easy to miss. NewScheduler and NewJob both return an error, and the README's example discards neither silently; it shows the error handling branches even though the bodies are comments. Second, each job has a unique ID, which the example prints. That ID is what you would use later to look the job up or remove it.
Shutdown is explicit. The README blocks with a select on a timer, then calls s.Shutdown(), and notes a context-aware alternative, s.ShutdownWithContext(ctx). Nothing in the README suggests the scheduler stops on its own when your function returns, so a program that starts a scheduler and exits main will simply exit.
Concurrency limits, singletons and the queue-or-skip choice
Two independent limiting mechanisms are documented, and they can be enabled at the same time.
Singleton mode is per job. WithSingletonMode limits a job to a single concurrent execution, and the README says that execution either reschedules (skipping overlapping runs) or queues (waiting for the previous execution to finish). Those two behaviours are very different operationally. Rescheduling silently drops an occurrence when the previous one is still running, which is fine for a cache refresh and wrong for anything that must process every item. Queueing defers the run instead, which preserves the occurrence but can build a backlog if the job is consistently slower than its interval.
Limit mode is per scheduler. WithLimitConcurrentJobs caps the number of concurrent executions across the whole scheduler, again with a reschedule or queue choice. This is the knob that protects a shared resource such as a database connection pool when many jobs happen to fire at the same moment.
The README states plainly that a scheduler limit and a job limit can both be enabled. It does not spell out what happens when a queued job hits a scheduler limit that is also queueing. If you plan to combine them, that interaction is worth reading the godoc for before you rely on it.
Running gocron on more than one instance
The distributed story is deliberately split in two, and the README is honest that the pieces are not in this repository.
An elector, configured with WithDistributedElector, elects a single instance to run as the primary while the other instances check whether a new leader needs to be elected. A locker, configured with WithDistributedLocker, locks each run of a job to a single instance. The README notes that a locker can be set at the job level or the scheduler level, and that if both are defined, the job's locker takes precedence.
Those are interfaces, not implementations. The README links to a search for go-co-op electors and go-co-op lockers, and asks readers who do not see what they need to request a repo on Slack. So the practical cost of multi-instance gocron is that you must pick or write a backend for leader election or locking, and the godoc for Locker carries its own notes on design limitations. If your deployment is a single process, this entire section is irrelevant, and you should not add an elector just because the feature exists.
The repository does ship examples/elector/ as an example directory, which is the place to look for the intended wiring.
Where gocron v2 is the wrong tool
gocron runs inside your process. That is its advantage and its main constraint.
If the process dies, the schedule dies with it. There is no external daemon holding the timetable, so a crash-restart loop means missed runs, and a deployment that scales to zero overnight means nothing runs at all. A team that wants scheduling to survive application restarts independently of the application should be looking at a standalone scheduler instead.
The second limitation is the interface. Everything is Go code. There is no CLI to list jobs, no config file to define them, and no built-in web UI. The README points to gocron-ui as a separate dashboard, which means the visual option is another component to deploy and operate, not a flag you turn on.
Third, the distributed pieces are interfaces. Multi-instance coordination is possible, but the README tells you to find or request an implementation rather than shipping one. A team expecting leader election out of the box will be disappointed by the module contents.
Finally, if your jobs are shell commands, gocron is an awkward fit. You would be writing Go wrappers around exec calls to reproduce what system cron already does, and you would inherit the in-process lifetime problem for free.
gocron v2 versus robfig/cron v3
The closest comparison is robfig/cron, and the difference is visible in gocron's own go.mod: gocron depends on github.com/robfig/cron/v3 as a library. It uses that parser for cron expressions rather than writing its own.
So the question is not which one can parse a crontab. Both can. The question is what surrounds the parser.
robfig/cron is a cron parser and runner. You register entries and it fires them on the cron schedule. gocron adds a job abstraction with unique IDs, several schedule types beyond crontab (duration, random duration, daily, weekly, monthly, one time), per-job and per-scheduler concurrency limits with explicit skip-or-queue semantics, interval-from-completion timing, event listeners, and interfaces for distributed election and locking. It also adds a dependency graph: uuid, clockwork, robfig/cron and, for tests, testify and goleak.
If all you need is "run this function on a cron expression," robfig/cron is the smaller dependency and the more direct answer. gocron earns its extra surface when you need the concurrency semantics or the interval-from-completion mode, or when you want the schedule types that a crontab cannot express cleanly, such as a random duration between a minimum and a maximum.
Maintenance, licence and upgrade cost
The last push to the repository was on 2026-09-01, and the most recent release listed is v2.22.0 from 2026-07-09. The repository is not archived. Recent releases include v2.21.2 and v2.21.1 earlier in 2026, so the v2 line has seen patch releases alongside feature releases this year.
The licence is MIT, which permits use, modification and redistribution provided the copyright notice and permission notice are included. That is a permissive licence with no copyleft obligation on your own code. This is a description of the licence text, not legal advice; if your organisation has specific compliance requirements, read the LICENSE file in the repository.
The upgrade cost is concentrated in the major version. The module path carries /v2, and the repository includes migration_v1_to_v2.md at the top level, which tells you the maintainers considered the v1 to v2 move significant enough to document. Within v2, the API surface is built from options such as WithSingletonMode, WithIntervalFromCompletion, WithLimitConcurrentJobs, WithDistributedElector, WithDistributedLocker and WithEventListeners, which is the pattern that tends to keep additions backward compatible.
The go.mod declares go 1.22, so the minimum toolchain is stated rather than implied. If you are pinned to an older Go release, that line is the constraint to check first.
Editorial conclusion
Adopt gocron v2 if your jobs are Go functions that belong inside an existing service and you want scheduling without a second process or a database-backed control plane. Do not adopt it if you need a standalone daemon with a built-in web UI, or if a single-instance cron on the host already covers the workload. Before wiring it into production, verify two things against your own code: how your jobs behave under WithSingletonMode when a run overruns its interval, and whether your deployment actually needs a distributed elector or locker, since both require an implementation the repository does not ship.
Frequently asked questions
What is the purpose of a cron job?
A cron job runs a task automatically at a predetermined time or interval rather than in response to a user action. gocron applies that idea inside a Go program, running Go functions on schedules such as a fixed duration, a crontab expression, or a specific day and time.
Is cron free?
gocron is released under the MIT licence, which permits use, modification and redistribution as long as the copyright and permission notices are kept. That is a description of the licence, not legal advice for your organisation.
How do I run a job every 30 minutes with gocron?
Use a duration job with a 30 minute duration, or a cron job with a crontab expression. The README's Quick Start shows the duration form as gocron.DurationJob(10*time.Second) passed as the first argument to s.NewJob, so the same call with 30*time.Minute gives a half-hourly run.
What is an example of a job in gocron?
A job is a Go function plus its parameters. The README's Quick Start registers a task that takes a string and an int, passing "hello" and 1 as the arguments, and schedules it with a duration job.
gocron vs robfig cron: what is the difference?
gocron depends on github.com/robfig/cron/v3 for cron expression parsing, and adds a job abstraction with unique IDs, additional schedule types, per-job and per-scheduler concurrency limits, interval-from-completion timing, event listeners, and interfaces for distributed election and locking. robfig/cron is the smaller dependency when a cron expression and a function are all you need.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/go-co-op-gocron)