The cronlord binary is the whole product. This page catalogues every
subcommand, flag, and environment variable it honours, so you can
script against it or drop it into an ExecStart= line with confidence.
-c, --config PATH path to cronlord.toml (default: ./cronlord.toml)
-h, --help show help
-V, --version print version and exit
Unknown top-level flags are forwarded to the subcommand. Subcommand
flags like --schedule on job add don’t conflict.
| Variable | Default | Purpose |
|---|---|---|
CRONLORD_HOST |
127.0.0.1 |
Listen host |
CRONLORD_PORT |
7070 |
Listen port |
CRONLORD_DATA |
./var |
Data directory (db + logs live here) |
CRONLORD_DB |
$DATA/cronlord.db |
SQLite path override |
CRONLORD_LOG_DIR |
$DATA/logs |
Per-run log directory |
CRONLORD_ADMIN_TOKEN |
unset | Bearer token gating /api/* |
CRONLORD_LOG_TTL_DAYS |
30 |
Run log retention; 0 disables auto-rotation |
CRONLORD_BLOCK_PRIVATE_NETS |
unset | 1 refuses RFC1918, loopback, link-local, CGNAT, multicast HTTP targets |
CRONLORD_CLAUDE_CLI |
claude |
Override Claude Code CLI binary for kind = "claude" jobs |
Env > cronlord.toml > built-in default.
cronlord serve / cronlord serverStarts the scheduler + HTTP server + background reapers. This is the everyday command.
./cronlord server
CRONLORD_ADMIN_TOKEN=$(openssl rand -hex 32) ./cronlord server
What runs in the process:
$CRONLORD_HOST:$CRONLORD_PORT.CRONLORD_LOG_TTL_DAYS
once per day. Skipped when CRONLORD_LOG_TTL_DAYS=0.SIGINT / SIGTERM flushes the scheduler, closes the DB, and exits 0.
cronlord migrateApplies pending SQL migrations from db/migrations/. serve runs
migrations automatically on boot, so this is for manual maintenance
(e.g. running migrations against a snapshot before swapping binaries).
./cronlord migrate
# => ok
cronlord doctor [--json]Probes the install in under a second and reports what’s wrong. Pair
with docs/troubleshooting.md — anything doctor
flags has a fix in that guide.
./cronlord doctor
# [ ok ] binary cronlord 0.3.6 (crystal 1.19.1)
# [ ok ] db var/cronlord.db integrity_check=ok journal_mode=wal
# [warn] admin_token unset - OK while bound to 127.0.0.1, required before exposing publicly
# ...
# summary: 11 ok, 1 warn, 0 fail
Checks covered:
| Check | What it verifies |
|---|---|
binary |
Version + Crystal runtime |
config |
cronlord.toml present or defaults in use |
data_dir |
Exists and is writable |
db |
File exists, integrity_check=ok, reports journal_mode |
migrations |
All on-disk migrations applied; lists pending |
log_dir |
Size, retention policy, warns over 1 GiB |
stuck_runs |
Counts runs in running older than 24h |
workers |
Registered workers heartbeating within 5m |
timezone |
IANA tzdata available (America/New_York loads) |
admin_token |
Set for non-loopback binds, ≥32 chars |
private_nets_guard |
CRONLORD_BLOCK_PRIVATE_NETS state |
claude_cli |
claude on PATH when kind='claude' jobs exist |
Exit codes:
0 — all checks ok1 — at least one warning, no failures2 — one or more failures (use in CI: cronlord doctor || exit 1)--json emits the same report as a stable JSON object ({version, checks: [{name, status, detail}]}) suitable for monitoring pipelines.
cronlord job <subcommand>Non-interactive CRUD for jobs. Respects CRONLORD_ADMIN_TOKEN only at
the HTTP surface - the CLI talks to the DB directly.
job list./cronlord job list
Columns: ID, NAME, SCHEDULE, ON, COMMAND (truncated at 60).
job add./cronlord job add \
--schedule '*/5 * * * *' \
--command 'ping -c1 example.com' \
--name heartbeat
| Flag | Required | Notes |
|---|---|---|
--schedule |
yes | Cron expression or @hourly/@daily/@weekly/@monthly. Parsed before write. |
--command |
yes | Shell snippet (for --kind=shell), URL/JSON (http), or prompt (claude). |
--name |
no | Display name; defaults to the id. |
--id |
no | Stable id; defaults to UUID. Useful for idempotent upserts. |
--timeout |
no | Seconds until SIGTERM -> SIGKILL. 0 = unlimited. |
--kind |
no | shell (default), http, claude. |
Prints the job id on success. Exits 2 with a message on bad input.
job rm <id>./cronlord job rm 3a4f...
# => deleted
# or => not_found
Cascades to runs via ON DELETE CASCADE in the schema.
job run <id>Triggers a run immediately (trigger = "cli"), blocks until it
finishes, prints the run id. Useful for manual smoke tests or
invoking a job from another cron.
./cronlord job run nightly-backup
cronlord runs [--job ID] [--limit N]Show recent runs. Defaults to 20 rows newest first. Filter to one
job with --job=<id>.
./cronlord runs --limit 5
./cronlord runs --job heartbeat --limit 50
cronlord worker <subcommand>Manages the remote worker registry. All subcommands except run talk
to the local DB.
worker register <name> [--label L]...Creates a new worker row with a random plaintext secret. The
plaintext is printed once; there is no way to recover it. Copy it,
then derive the HMAC key on the worker host with
sha256(plaintext_secret).
./cronlord worker register runner-1 --label linux --label gpu
# id: b1d7abd0-...
# name: runner-1
# secret (shown once - copy it now):
# 47caaaeb19...
Labels are how job.labels restrict eligibility. A job with
labels = ["linux"] is only leased by workers advertising linux.
Empty job.labels means “any worker”.
worker list./cronlord worker list
# b1d7abd0-... runner-1 on 2026-04-19 11:42:01
# 5e02c1aa-... runner-2 off never
Columns: id, name, enabled, last_seen (unix -> local time).
worker rm <id>./cronlord worker rm b1d7abd0-...
# => deleted
Removes the worker row and detaches any in-flight leases (runs flip
back to queued via the lease reaper).
worker runRuns the reference worker polling loop. Expected environment (all required, flags override each one-to-one):
| Env / flag | Purpose |
|---|---|
CRONLORD_URL / --url |
Scheduler base URL, e.g. https://cron.example.com |
CRONLORD_WORKER_ID / --id |
Worker id from worker register |
CRONLORD_HMAC_KEY / --key |
sha256(plaintext_secret) |
CRONLORD_WORKER_NAME / --name |
Display name (logs only; default: hostname) |
CRONLORD_LEASE_SEC / --lease |
Lease window, default 60 |
CRONLORD_POLL_SEC / --poll |
Idle poll interval, default 5 |
export CRONLORD_URL=https://cron.example.com
export CRONLORD_WORKER_ID=b1d7...
export CRONLORD_HMAC_KEY=$(printf '%s' "$PLAIN_SECRET" | openssl dgst -sha256 | awk '{print $2}')
./cronlord worker run --name "$(hostname)"
SIGINT / SIGTERM drains the current run (if any) and exits 0.
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Domain failure (not found, unknown subcommand, …) |
2 |
Bad invocation (missing required flag, invalid cron) |