# How to Set Up Cron Jobs Using the Cronx Module in Gorig

> Learn to set up cron jobs in Gorig with the cronx module. Easily register scheduled tasks and manage your application's background processes efficiently.

- Repository: [Jom/gorig](https://github.com/jom-io/gorig)
- Tags: how-to-guide
- Published: 2026-03-04

---

**Use the `cronx` package in `jom-io/gorig` to register scheduled tasks via `AddCronTask`, `AddEveryTask`, or `AddOnceTask`, then start the scheduler with `Startup` and stop it gracefully with `Shutdown`.**

The `cronx` module provides Gorig’s official abstraction for background job scheduling, wrapping the robust `robfig/cron` library with framework-specific lifecycle management. When you set up cron jobs using the cronx module in Gorig, you gain built-in context propagation, panic recovery, timeout enforcement, and graceful shutdown handling that integrates seamlessly with the rest of the Gorig ecosystem.

## Understanding the Cronx Module Architecture

The `cronx` package is a thin but opinionated wrapper located in [`cronx/cron.go`](https://github.com/jom-io/gorig/blob/main/cronx/cron.go). It manages a singleton `cron.Cron` instance and exposes registration helpers that enforce Gorig’s standards for observability and safety.

Key architectural features include:

- **Context-aware execution** – Every task receives a `context.Context` derived from the job’s timeout or cancellation signal.
- **Panic recovery** – The wrapper in [`cronx/task.go`](https://github.com/jom-io/gorig/blob/main/cronx/task.go) automatically recovers from panics, logs the stack trace, and prevents the scheduler from crashing.
- **Timeout enforcement** – Optional timeout parameters create cancellable goroutines that respect deadlines.
- **Lifecycle hooks** – `Startup` and `Shutdown` methods follow Gorig’s standard service pattern, enabling clean initialization and termination.

## Registering Cron Jobs in Gorig

All task registration must occur before calling `Startup`. The module offers four primary registration functions, each defined in [`cronx/cron.go`](https://github.com/jom-io/gorig/blob/main/cronx/cron.go).

### Recurring Tasks with Cron Expressions

Use `AddCronTask` when you need standard cron syntax (`@every 1s`, `0 0 * * *`, etc.).

```go
// Runs every 5 minutes with a 30-second timeout
cronx.AddCronTask("*/5 * * * *", func(ctx context.Context) {
    // Your business logic here
    processData(ctx)
}, 30*time.Second)

```

### Interval-Based Execution

`AddEveryTask` provides a more readable API for fixed intervals without writing cron expressions.

```go
// Executes every 10 seconds
cronx.AddEveryTask(10*time.Second, func(ctx context.Context) {
    cleanupTempFiles(ctx)
}, 5*time.Second) // 5s timeout

```

### Delayed and One-Off Tasks

For non-recurring workloads, use `AddDelayTask` or `AddOnceTask`.

```go
// Run once after a 1-minute delay
cronx.AddDelayTask(1*time.Minute, func(ctx context.Context) {
    sendWelcomeEmail(ctx)
}, 10*time.Second)

// Run once at a specific time
futureTime := time.Now().Add(30 * time.Minute)
cronx.AddOnceTask(futureTime, func(ctx context.Context) {
    generateNightlyReport(ctx)
})

```

**Note:** The older `AddTask` function remains available for backward compatibility but is marked as deprecated in [`cronx/cron.go`](https://github.com/jom-io/gorig/blob/main/cronx/cron.go) because it lacks context support and panic recovery.

## Starting and Managing the Scheduler

### Initializing with Startup

The `Startup` function in [`cronx/cron.go`](https://github.com/jom-io/gorig/blob/main/cronx/cron.go) begins processing registered jobs. It accepts a service identifier and an optional HTTP port for health checks.

```go
// Start cron service without HTTP endpoint
if err := cronx.Startup("DATA_SYNC", ""); err != nil {
    log.Fatalf("cron startup failed: %v", err)
}

// Or with health endpoint on port 8081
if err := cronx.Startup("DATA_SYNC", "8081"); err != nil {
    log.Fatal(err)
}

```

### Graceful Shutdown Handling

To prevent data loss or corruption, always call `Shutdown` when the application receives a termination signal. This method stops the scheduler, waits for running jobs to complete (respecting the provided context), and cleans up resources.

```go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := cronx.Shutdown("DATA_SYNC", ctx); err != nil {
    log.Printf("cron shutdown error: %v", err)
}

```

## Production-Ready Cron Job Examples

### Basic Recurring Task

This example demonstrates a minimal setup that prints a timestamp every 5 seconds.

```go
package main

import (
    "context"
    "log"
    "time"

    "github.com/jom-io/gorig/cronx"
)

func main() {
    cronx.AddCronTask("@every 5s", func(ctx context.Context) {
        log.Println("tick at", time.Now())
    })

    if err := cronx.Startup("HEARTBEAT", ""); err != nil {
        log.Fatalf("failed to start cron: %v", err)
    }

    select {} // Block forever
}

```

### Task with Timeout and Panic Recovery

The following job simulates unstable work that occasionally panics. The `cronx` wrapper in [`cronx/task.go`](https://github.com/jom-io/gorig/blob/main/cronx/task.go) automatically recovers and logs the error without crashing the scheduler.

```go
cronx.AddCronTask("@every 10s", func(ctx context.Context) {
    // Simulate work that might panic
    if time.Now().Unix()%2 == 0 {
        panic("simulated failure")
    }
    log.Println("completed successfully")
}, 2*time.Second) // 2-second timeout

```

### Scheduled One-Off Task

Use `AddOnceTask` to schedule maintenance windows or delayed notifications.

```go
runAt := time.Now().Add(30 * time.Minute)
cronx.AddOnceTask(runAt, func(ctx context.Context) {
    log.Println("Generating nightly report at", time.Now())
    generateReport(ctx)
})

```

### Graceful Shutdown Implementation

Integrate with Go’s signal handling for production deployments.

```go
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := cronx.Shutdown("WORKER", ctx); err != nil {
    log.Printf("cron shutdown error: %v", err)
}

```

## Key Source Files in the Cronx Module

Understanding the internal structure helps when debugging or extending functionality.

| File | Purpose | Location |
|------|---------|----------|
| [`cronx/cron.go`](https://github.com/jom-io/gorig/blob/main/cronx/cron.go) | Public API surface including `AddCronTask`, `Startup`, and `Shutdown` | [`github.com/jom-io/gorig/cronx/cron.go`](https://github.com/jom-io/gorig/blob/main/github.com/jom-io/gorig/cronx/cron.go) |
| [`cronx/task.go`](https://github.com/jom-io/gorig/blob/main/cronx/task.go) | Internal `Task` struct definition, timeout enforcement, and panic recovery wrapper | [`github.com/jom-io/gorig/cronx/task.go`](https://github.com/jom-io/gorig/blob/main/github.com/jom-io/gorig/cronx/task.go) |
| [`cronx/logger.go`](https://github.com/jom-io/gorig/blob/main/cronx/logger.go) | Gorig-standard logger configuration for cron output | [`github.com/jom-io/gorig/cronx/logger.go`](https://github.com/jom-io/gorig/blob/main/github.com/jom-io/gorig/cronx/logger.go) |
| [`test/cron_test.go`](https://github.com/jom-io/gorig/blob/main/test/cron_test.go) | Unit tests demonstrating valid usage patterns | [`github.com/jom-io/gorig/test/cron_test.go`](https://github.com/jom-io/gorig/blob/main/github.com/jom-io/gorig/test/cron_test.go) |

## Summary

- The `cronx` module in `jom-io/gorig` wraps `robfig/cron` to provide context-aware, panic-safe job scheduling.
- Register tasks before startup using `AddCronTask` (cron expressions), `AddEveryTask` (intervals), `AddDelayTask` (delayed), or `AddOnceTask` (one-time).
- Start the scheduler with `Startup(serviceName, healthPort)` and stop it gracefully with `Shutdown(serviceName, ctx)`.
- All tasks receive a `context.Context` and support optional timeouts and automatic panic recovery via the wrapper in [`cronx/task.go`](https://github.com/jom-io/gorig/blob/main/cronx/task.go).

## Frequently Asked Questions

### What is the difference between AddCronTask and AddEveryTask?

`AddCronTask` accepts standard cron expressions like `@every 1s` or `0 0 * * *`, while `AddEveryTask` takes a `time.Duration` directly (e.g., `10*time.Second`) for simpler interval-based scheduling. Both functions support context propagation and timeout parameters, but `AddEveryTask` eliminates the need to parse cron syntax for simple recurring intervals.

### How does cronx handle panics in scheduled jobs?

The `cronx` module automatically recovers from panics via the wrapper logic defined in [`cronx/task.go`](https://github.com/jom-io/gorig/blob/main/cronx/task.go). When a task panics, the wrapper captures the error, logs it using the Gorig logger, and allows the scheduler to continue running subsequent jobs. This prevents a single faulty task from crashing the entire cron service.

### Can I run a task only once at a specific time?

Yes, use the `AddOnceTask` function to schedule a one-time execution. Pass a `time.Time` value indicating when the task should run, along with your handler function. The task will execute exactly once at the specified time and then automatically remove itself from the scheduler.

### What happens if I don't call Shutdown before the application exits?

If `Shutdown` is not called, the underlying `robfig/cron` scheduler may terminate abruptly, potentially interrupting running jobs and causing data loss or inconsistent state. Always call `Shutdown` with a context timeout during application termination (e.g., in signal handlers) to allow in-flight tasks to complete gracefully.