What Types of Background Jobs Does Kaneo Run? Complete Scheduler Architecture Guide

Kaneo runs periodic background jobs—including trial reminders and health checks—using the croner library with a PostgreSQL-based leader-lock mechanism to ensure single-execution in clustered environments.

Kaneo is an open-source project management platform built with TypeScript and Node.js. The API layer manages scheduled maintenance tasks through a dedicated scheduler package located in apps/api/src/scheduler. All jobs follow a consistent pattern that prevents duplicate execution when running multiple server instances.

How Kaneo's Scheduler Works

The scheduler implementation lives in apps/api/src/scheduler/index.ts and uses the croner library to manage cron-based execution. Rather than running jobs directly, Kaneo wraps each handler with a leader-lock utility that coordinates across instances using a PostgreSQL table.

All job registrations follow this pattern in the scheduler index:

// apps/api/src/scheduler/index.ts
import { Cron } from "croner";
import { withCheckIn } from "./utils";
import { checkTrialReminders } from "./trial-reminders";

const jobs: Cron[] = [];

jobs.push(
  new Cron(
    "23 * * * *",
    withCheckIn("trial-reminders", checkTrialReminders)
  )
);

The withCheckIn wrapper automatically handles lease acquisition and release, ensuring that only one instance processes each job during any given time slot.

Types of Background Jobs in Kaneo

Kaneo currently implements two primary categories of background work: user-facing notification jobs and infrastructure maintenance tasks.

Trial Reminders

The trial-reminders job notifies users when their free trial subscription approaches expiration. According to the source code in apps/api/src/scheduler/index.ts, this job runs hourly at minute 23 (23 * * * *) via the checkTrialReminders handler.

This job queries the database for trials nearing expiration and dispatches email or SMS notifications through Kaneo's notification service. The implementation resides in apps/api/src/scheduler/trial-reminders.ts and integrates with the withCheckIn utility for safe execution.

Leader Lock Infrastructure

While technically a scheduling mechanism rather than a business job, the leader-lock system functions as a background coordination service that runs continuously. Implemented in apps/api/src/scheduler/leader-lock.ts, this infrastructure manages the job_lease table defined in apps/api/src/database/schema.ts.

The schema defines the lock storage as:

// apps/api/src/database/schema.ts
export const jobLeaseTable = pgTable("job_lease", {
  name: text("name").notNull(),
  owner: text("owner").notNull(),
  expires_at: timestamp("expires_at").notNull(),
});

When any cron job triggers, the system attempts to insert or update a row in this table. If another instance currently holds an unexpired lease for that job name, the current instance skips execution.

The Leader Lock Implementation Details

The leader-lock mechanism relies on two primary functions exported from apps/api/src/scheduler/leader-lock.ts: acquireJobLease and releaseJobLease.

When withCheckIn wraps a job handler, it performs the following sequence:

  1. Calls acquireJobLease with the job name and current instance identifier
  2. Checks if the returned lease is valid for this instance
  3. Executes the job logic only if the lease acquisition succeeds
  4. Calls releaseJobLease in a finally block to free the slot for the next execution window

This pattern guarantees that even with horizontal scaling across multiple containers or servers, resource-intensive tasks like trial reminder batch processing run exactly once per scheduled interval.

Creating Custom Background Jobs

Developers extending Kaneo can add new background jobs by following the established pattern in apps/api/src/scheduler/index.ts.

First, create a handler function in a new file:

// apps/api/src/scheduler/custom-cleanup.ts
export async function cleanupOldActivityLogs() {
  // Delete logs older than 90 days
  // Implementation details here
}

Then register the job with an appropriate cron schedule:

// apps/api/src/scheduler/index.ts
import { cleanupOldActivityLogs } from "./custom-cleanup";

jobs.push(
  new Cron(
    "0 2 * * *",  // Daily at 2:00 AM
    withCheckIn("activity-cleanup", cleanupOldActivityLogs)
  )
);

For direct lease management in complex scenarios, import the leader-lock functions directly:

import { acquireJobLease, releaseJobLease } from "./leader-lock";

async function specializedJob() {
  const lease = await acquireJobLease("special-job");
  if (!lease) return;  // Another instance is processing
  
  try {
    // Perform specialized work here
  } finally {
    await releaseJobLease("special-job");
  }
}

Summary

  • Kaneo uses the croner library in apps/api/src/scheduler/index.ts to schedule recurring tasks with cron expressions.
  • The trial-reminders job runs hourly to notify users of impending trial expirations.
  • A leader-lock mechanism in apps/api/src/scheduler/leader-lock.ts prevents duplicate execution using the job_lease PostgreSQL table.
  • The withCheckIn utility automatically handles lease acquisition and release for standard jobs.
  • Developers can extend the scheduler by wrapping new handlers with withCheckIn and registering them in the scheduler index file.

Frequently Asked Questions

How does Kaneo prevent the same background job from running on multiple servers simultaneously?

Kaneo implements a distributed locking mechanism using the job_lease table in PostgreSQL. Before executing any job, the scheduler attempts to acquire a lease for that specific job name. If another instance already holds an unexpired lease, the current instance skips execution. The lease automatically expires based on the expires_at timestamp defined in apps/api/src/database/schema.ts.

What scheduling library does Kaneo use for background jobs?

Kaneo uses croner, a lightweight JavaScript cron library. The implementation in apps/api/src/scheduler/index.ts creates new Cron instances with schedule expressions and handler functions wrapped by the withCheckIn utility.

How can I add a new periodic task to Kaneo's scheduler?

Create a handler function in apps/api/src/scheduler/, then import it into apps/api/src/scheduler/index.ts. Register the job using new Cron(scheduleExpression, withCheckIn("job-name", handler)). The withCheckIn wrapper automatically handles the leader-lock logic, ensuring safe execution in multi-instance deployments.

Where is the trial reminder logic implemented in the Kaneo codebase?

The trial reminder functionality is implemented in apps/api/src/scheduler/trial-reminders.ts and registered in apps/api/src/scheduler/index.ts with the cron schedule 23 * * * *. This configuration runs the check once per hour at minute 23 to identify and notify users with trials nearing expiration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →