gronx is a free, open source project & work management project written in Go and released under MIT. It has 516 GitHub stars, 32 forks and 8 open issues, and was last pushed 24 hours ago. On this registry it ranks #59 of 62 tracked projects in Project & Work Management, with 5 head-to-head comparisons available.

What is gronx?

gronx is a lightweight, dependency-free cron expression parser, task runner and job scheduler for Go (tested on v1.13+) that also ships as a standalone binary and crontab-like daemon for use outside a Go program.

What it is

gronx is a cron expression parser written in Go, ported from the PHP project adhocore/cron-expr, and published as a Go module on pkg.go.dev under github.com/adhocore/gronx. It parses cron expressions, reports whether an expression is valid, checks whether an expression is currently due, and calculates the next or previous run time from any arbitrary point in time. It runs with zero dependencies and is fast because it bails early as soon as a segment fails to match.

The concrete problem it solves is placing cron scheduling logic inside your own program rather than delegating it to the operating system. Instead of writing a crontab entry and hoping a host-level crond is running and correctly configured, a Go service can call gronx directly and decide for itself what happens when a schedule fires. For teams that want to go further, gronx includes a built-in crontab-like daemon that reads a task list file and executes tasks, which the README presents as a replacement for crond and, for the bold, for crontab entirely.

Key capabilities

  • Parse and validate cron expressions with gron.IsValid(expr), or validate without instantiation via gronx.IsValid("* * * * *").
  • Check dues with gron.IsDue(expr), which returns a boolean and an error, and accepts an explicit reference time.
  • Check many expressions against one reference time with BatchDue(), returning a []gronx.Expr{} array where each item carries a Due flag and any Err encountered.
  • Find the next run time with NextTick(expr, allowCurrent) or NextTickAfter(expr, refTime, allowCurrent), and the previous run time with PrevTick().
  • Support second-level time granularity in addition to the usual cron fields.
  • Run as a built-in crontab-like daemon backed by a crontab-like task list file, using the tasker command-line tool.
  • Install as a standalone binary through a Bash install script that supports custom installation directory, version selection, and platform or architecture overrides.

Who uses it and how

  • Go services that need in-process scheduling, using the library rather than an external scheduler process.
  • Applications that must know the next or previous run time of an expression, such as scheduling UIs, dashboards, and validation in configuration editors.
  • Teams that want to replace a host-level crond with a single binary plus a task list file.
  • CI/CD pipelines, which can run the installer non-interactively with --yes.
  • Operators managing many expressions, who can batch due checks against one shared reference time instead of looping.

Getting started

As a library, install with go get -u github.com/adhocore/gronx. For the standalone tasker tool, run the Bash installer curl -sS https://raw.githubusercontent.com/adhocore/gronx/refs/heads/main/install.sh | sh, adding -s -- --yes for non-interactive installs.

How it compares

No list of paid products it replaces was provided. The facts name crontab and crond as the things it can stand in for, and the PHP project adhocore/cron-expr as its source, so gronx sits as the Go-native alternative to system cron and as the Go counterpart to its PHP predecessor.

When to use it — and when not to

A self-hoster running the daemon must operate the binary and maintain a crontab-like task list file, and the available documentation does not describe persistence, retries, or logging. The README excerpt is sparse and truncated mid-sentence, and no release history is given, so anyone needing mature operational features should check the repository before committing. The licence is MIT, which is clear and permissive.

project readme (upstream, from github) — read inline

adhocore/gronx

Latest Version Software License Go Report Test Lint Codecov Support Tweet

gronx is Golang cron expression parser ported from adhocore/cron-expr with task runner and daemon that supports crontab like task list file. Use it programatically in Golang or as standalone binary instead of crond. If that's not enough, you can use gronx to find the next (NextTick()) or previous (PrevTick()) run time of an expression from any arbitrary point of time.

  • Zero dependency.
  • Very fast because it bails early in case a segment doesn't match.
  • Built in crontab like daemon.
  • Supports time granularity of Seconds.

Find gronx in pkg.go.dev.

Installation

As a Go library

go get -u github.com/adhocore/gronx

Using Bash install script

For installing the tasker command-line tool, you can use the bash installation script:

curl -sS https://raw.githubusercontent.com/adhocore/gronx/refs/heads/main/install.sh | sh

For non-interactive installation (useful in CI/CD environments):

curl -sS https://raw.githubusercontent.com/adhocore/gronx/refs/heads/main/install.sh | sh -s -- --yes

The script supports several options including custom installation directory, version selection, and platform/architecture overrides. Use --help to see all available options:

curl -sS https://raw.githubusercontent.com/adhocore/gronx/refs/heads/main/install.sh | sh -s -- --help

Usage

import (
	"time"

	"github.com/adhocore/gronx"
)

gron := gronx.New()
expr := "* * * * *"

// check if expr is even valid, returns bool
gron.IsValid(expr) // true

// check if expr is due for current time, returns bool and error
gron.IsDue(expr) // true|false, nil

// check if expr is due for given time
gron.IsDue(expr, time.Date(2021, time.April, 1, 1, 1, 0, 0, time.UTC)) // true|false, nil

Validity can be checked without instantiation:

import "github.com/adhocore/gronx"

gronx.IsValid("* * * * *") // true

Batch Due Check

If you have multiple cron expressions to check due on same reference time use BatchDue():

gron := gronx.New()
exprs := []string{"* * * * *", "0 */5 * * * *"}

// gives []gronx.Expr{} array, each item has Due flag and Err enountered.
dues := gron.BatchDue(exprs)

for _, expr := range dues {
    if expr.Err != nil {
        // Handle err
    } else if expr.Due {
        // Handle due
    }
}

// Or with given time
ref := time.Now()
gron.BatchDue(exprs, ref)

Next Tick

To find out when is the cron due next (in near future):

allowCurrent = true // includes current time as well
nextTime, err := gronx.NextTick(expr, allowCurrent) // gives time.Time, error

// OR, next tick after certain reference time
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // excludes the ref time
nextTime, err := gronx.NextTickAfter(expr, refTime, allowCurrent) // gives time.Time, error

Prev Tick

To find out when was the cron due previously (in near past):

allowCurrent = true // includes current time as well
prevTime, err := gronx.PrevTick(expr, allowCurrent) // gives time.Time, error

// OR, prev tick before certain reference time
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // excludes the ref time
nextTime, err := gronx.PrevTickBefore(expr, refTime, allowCurrent) // gives time.Time, error

The working of PrevTick*() and NextTick*() are mostly the same except the direction. They differ in lookback or lookahead.

Standalone Daemon

In a more practical level, you would use this tool to manage and invoke jobs in app itself and not mess around with crontab for each and every new tasks/jobs.

In crontab just put one entry with * * * * * which points to your Go entry point that uses this tool. Then in that entry point you would invoke different tasks if the corresponding Cron expr is due. Simple map structure would work for this.

Check the section below for more sophisticated way of managing tasks automatically using gronx daemon called tasker.


Go Tasker

Tasker is a task manager that can be programatically used in Golang applications. It runs as a daemon and invokes tasks scheduled with cron expression:

package main

import (
	"context"
	"time"

	"github.com/adhocore/gronx/pkg/tasker"
)

func main() {
	taskr := tasker.New(tasker.Option{
		Verbose: true,
		// optional: defaults to local
		Tz:      "Asia/Bangkok",
		// optional: defaults to stderr log stream
		Out:     "/full/path/to/output-file",
	})

	// add task to run every minute
	taskr.Task("* * * * *", func(ctx context.Context) (int, error) {
		// do something ...

		// then return exit code and error, for eg: if everything okay
		return 0, nil
	}).Task("*/5 * * * *", func(ctx context.Context) (int, error) { // every 5 minutes
		// you can also log the output to Out file as configured in Option above:
		taskr.Log.Printf("done something in %d s", 2)

		return 0, nil
	})

	// run task without overlap, set concurrent flag to false:
	concurrent := false
	taskr.Task("* * * * * *", , tasker.Taskify("sleep 2", tasker.Option{}), concurrent)

	// every 10 minute with arbitrary command
	taskr.Task("@10minutes", taskr.Taskify("command --option val -- args", tasker.Option{Shell: "/bin/sh -c"}))

	// ... add more tasks

	// optionally if you want tasker to stop after 2 hour, pass the duration with Until():
	taskr.Until(2 * time.Hour)

	// finally run the tasker, it ticks sharply on every minute and runs all the tasks due on that time!
	// it exits gracefully when ctrl+c is received making sure pending tasks are completed.
	taskr.Run()
}
Concurrency

By default the tasks can run concurrently i.e if previous run is still not finished but it is now due again, it will run again. If you want to run only one instance of a task at a time, set concurrent flag to false:

taskr := tasker.New(tasker.Option{})

concurrent := false
expr, task := "* * * * * *", tasker.Taskify("php -r 'sleep(2);'")
taskr.Task(expr, task, concurrent)

Task Daemon

It can also be used as standalone task daemon instead of programmatic usage for Golang application.

First, just install tasker command:

go install github.com/adhocore/gronx/cmd/tasker@latest

Or, you can install using mise:

mise use github:adhocore/gronx@latest

Or you can also download latest prebuilt binary from release for platform of your choice.

Then prepare a taskfile (example) in crontab format (or can even point to existing crontab).

user is not supported: it is just cron expr followed by the command.

Finally run the task daemon like so

tasker -file path/to/taskfile

You can pass more options to control the behavior of task daemon, see below.

Tasker command options:
-file string <required>
    The task file in crontab format
-out string
    The fullpath to file where output from tasks are sent to (defaults to stderr)
-shell string
    The shell to use for running tasks (default "/usr/bin/bash")
-tz string
    The timezone to use for tasks (default "Local")
-until int
    The timeout for task daemon in minutes
-verbose
    The verbose mode outputs as much as possible

Examples:

tasker -verbose -file path/to/taskfile -until 120 # run until next 120min (i.e 2hour) with all feedbacks echoed back
tasker -verbose -file path/to/taskfile -out path/to/output # with all feedbacks echoed to the output file
tasker -tz America/New_York -file path/to/taskfile -shell zsh # run all tasks using zsh shell based on NY timezone

File extension of taskfile for

readme truncated — read the full docs on github

Frequently asked questions

Is gronx free to use?

gronx is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does gronx do?

Lightweight, fast and dependency-free Cron expression parser (due checker, next/prev due date finder), task runner, job scheduler and/or daemon for Golang (test

What is gronx written in?

gronx is primarily written in Go. Its source is publicly available at https://github.com/adhocore/gronx, and it has 516 GitHub stars.