kron — Modern Cron Replacement
kron is a CLI-first, single-binary cron replacement written in Rust. It solves cron's observability black hole: every run is automatically captured with stdout, stderr, exit code, and duration. No silent failures.
When to Use This Skill
- User wants to schedule a recurring job or task
- User wants to check if a scheduled job ran successfully
- User wants to see output/logs from a past job run
- User mentions cron, crontab, job scheduling, or kron
- User wants to migrate from system crontab
Installation
cargo install kron
Single binary, zero runtime dependencies.
Core Commands
Add a job
# With a cron expression
kron add --name backup "0 2 * * *" pg_dump mydb -f /backups/mydb.sql
# With human-readable schedule
kron add --name backup "every day at 2am" pg_dump mydb -f /backups/mydb.sql
# Every minute (for testing)
kron add --name heartbeat "* * * * *" echo "alive"
# Capture current environment variables
kron add --capture-env --name deploy "0 3 * * *" ./deploy.sh
The --name flag is optional. The schedule comes first (quoted cron expression or human-readable), then the command to run.
List all jobs
kron list
Output:
ID NAME ENABLED SCHEDULE RUNS COMMAND
──────────────────────────────────────────────────────────────────────
a1b2c3d4 backup yes 0 2 * * * 5/5 pg_dump mydb -f /backups/mydb.sql
e5f6a7b8 heartbeat yes * * * * * 10/10 echo "alive"
Jobs can be referenced by ID (or prefix) or name in all commands.
Import from crontab
kron import # interactive selection
kron import --all # import all entries
Reads the current user's crontab, displays entries in a numbered list, and lets you select which to import (e.g. 1,3-5 or all). Handles @hourly/@daily/etc. aliases, skips comments, env vars, and @reboot entries. Derives job names from inline comments or command basenames.
Force-run a job now
kron run backup
Executes the job immediately (outside the schedule), captures output, and records the run.
Dry-run a job
kron test backup
Executes the job but does not record the run. Useful for testing commands.
Check job status
kron status
Output:
NAME STATUS EXIT CODE LAST RUN
-----------------------------------------------------------------
backup success 0 2026-03-15 02:00:01 (3s)
heartbeat success 0 2026-03-15 15:59:00 (0s)
All timestamps are displayed in local timezone.
View run history
kron history backup
kron history backup -n 20 # last 20 runs
kron history # shows all jobs (most recent first)
Output:
# STATUS EXIT CODE DURATION STARTED
-----------------------------------------------------------------
1 success 0 3s 2026-03-15 02:00:01
2 success 0 3s 2026-03-14 02:00:01
3 failed 1 1s 2026-03-13 02:00:00
View logs from a run
kron logs backup # most recent run
kron logs backup --run 3 # specific run
kron logs # most recent run across all jobs
Output:
=== Job: backup | Run #1 | 2026-03-15 02:00:01 ===
Status: success | Exit code: 0
Duration: 3s
--- stdout ---
DUMP DATABASE mydb
...
Remove a job
kron remove backup
Manage alerts
kron alert add-telegram --token <BOT_TOKEN> --chat-id <CHAT_ID>
kron alert add-slack --webhook-url <URL>
kron alert add-webhook --url <URL>
kron alert list
kron alert test # send test notification to all providers
kron alert remove <index>
Daemon management
kron daemon start # run scheduler in background
kron daemon start --foreground # explicit foreground mode
kron daemon stop # stop the running daemon
kron daemon install # install as systemd/launchd service
kron daemon uninstall # remove system service
kron daemon status # check service status
Self-update
kron update
Job Configuration (TOML)
Jobs are stored as TOML files in ~/.config/kron/jobs/<id>.toml:
[job]
id = "a1b2c3d4"
name = "backup"
command = "pg_dump mydb -f /backups/mydb.sql"
schedule = "0 2 * * *"
working_dir = "/app"
enabled = true
timeout = "30m"
[job.env]
DATABASE_URL = "postgres://localhost/mydb"
[job.alert]
on_failure = true
on_success = false
on_silence = "1h"
| Field | Required | Description |
|---|---|---|
id | Yes | 8-char hex ID (auto-generated) |
name | No | Human-friendly name (alphanumeric, hyphens, underscores, max 64) |
command | Yes | Shell command to execute (run via sh -c) |
schedule | Yes | Standard 5-field cron expression |
working_dir | No | Working directory for the command |
enabled | No | true (default) or false to disable without removing |
timeout | No | Max run duration: "30s", "5m", "1h" |
env | No | Environment variables as key-value pairs |
alert.on_failure | No | Alert on non-zero exit (default: true) |
alert.on_success | No | Alert on success (default: false) |
alert.on_silence | No | Dead-man switch: alert if job hasn't run in duration |
Cron Expression Reference
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *
Common patterns:
| Expression | Meaning |
|---|---|
* * * * * | Every minute |
0 * * * * | Every hour |
0 2 * * * | Daily at 2:00 AM |
0 0 * * 0 | Weekly on Sunday midnight |
*/5 * * * * | Every 5 minutes |
0 9-17 * * 1-5 | Hourly, weekdays 9 AM - 5 PM |
Data Storage
- Job definitions:
~/.config/kron/jobs/*.toml(single source of truth) - Run history:
~/.local/share/kron/kron.db(SQLite, WAL mode) - Alert config:
~/.config/kron/alerts.toml
Global Configuration
Optional global settings in ~/.config/kron/config.toml:
[retention]
max_runs_per_job = 100 # default: 100
max_age_days = 30 # default: 30
The daemon automatically prunes old runs based on this policy.
Built-in Safety Features
- Overlap prevention: A job won't start if a previous instance is still running
- Timeout: Jobs can be killed after a configurable duration (
kill_on_drop— no orphan processes) - Output capture: stdout and stderr always recorded (max 1 MiB each), never lost
- Graceful shutdown: SIGINT/SIGTERM cleanly stops the daemon
- SIGHUP reload:
kron add/kron removesignal the daemon for instant config pickup - Duplicate prevention: Daemon refuses to start if another instance is already running
- Automatic pruning: Old runs cleaned up based on retention policy (default: 100 runs/job, 30 days)
- File permissions: Job files 0600, jobs dir 0700, DB file 0600, alerts.toml 0600
- Next-fire scheduling: Zero idle wakeups — daemon sleeps until the next job is due
Common Workflows
Migrate from crontab
kron import # select entries interactively
kron daemon install # install as system service
crontab -r # remove old crontab (after verifying)
Debug a failing job
kron status # See which jobs are failing
kron history myjob # See recent run history
kron logs myjob # See the actual error output
kron test myjob # Dry-run to test without recording
kron run myjob # Re-run and record the result
Disable a job temporarily
Edit ~/.config/kron/jobs/<id>.toml and set enabled = false. The daemon picks up manual edits within 60 seconds, or send kill -HUP $(cat ~/.local/share/kron/daemon.pid) for instant reload.