Skip to main content

Lightweight cron orchestration tool

Project description

cronctrl

cronctrl is a lightweight cron orchestration tool that uses a single YAML file as the source of truth for jobs. It provides a simple CLI to validate configs, run jobs, manage cron entries, and view logs/status.

Highlights

  • Single YAML config file describing all jobs
  • cronctrl validate for schema checks and basic cron validation
  • cronctrl run <job> to execute a job immediately with logging and state tracking
  • cronctrl apply to generate cron entries and logrotate config
  • cronctrl logs and cronctrl status for quick visibility

Requirements

  • Python 3.11+
  • cron and logrotate available on the system for apply mode
  • PyYAML (install via requirements or editable install)

Install

From the repo root:

python3.11 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
pip install -e .

Production install (system-wide):

pip install cronctrl

If your OS marks the system Python as externally managed (PEP 668), use pipx:

pipx install cronctrl

Quick start (configure + activate cron)

This is the minimal flow to get all jobs in your YAML file active via cron.

  1. Create your config YAML (start from the example):
sudo mkdir -p /etc/cronctrl
sudo cp examples/jobs.yaml /etc/cronctrl/jobs.yaml

Edit /etc/cronctrl/jobs.yaml and make sure:

  • log_dir and state_dir are absolute paths
  • each job has a valid schedule and exec
  1. Validate your config:
cronctrl --config /etc/cronctrl/jobs.yaml validate
  1. Activate all cron jobs from the YAML:
cronctrl --config /etc/cronctrl/jobs.yaml apply --mode etc-crond --user root

That single apply step installs cron entries for every enabled job in the config.

Flags:

  • --mode etc-crond writes to /etc/cron.d/cronctrl (system-wide cron)
  • --user root sets the OS user cron will run the jobs as in /etc/cron.d mode

Note: --config is a global option, so it must appear before the subcommand.

Configuration

Schema is defined in YAML_SCHEMA.md. Example (examples/jobs.yaml):

version: 1
timezone: "America/New_York"
user: "securepixel"

log_dir: "/var/log/cronctrl"
state_dir: "/var/lib/cronctrl"

defaults:
  retention_days: 14
  timeout_seconds: 3600
  shell: "/bin/bash"
  concurrency: "forbid"

jobs:
  heartbeat_minutely:
    schedule: "* * * * *"
    exec: "/bin/echo heartbeat"
    retention_days: 1
    description: "Minutely heartbeat for smoke testing"

  export_daily:
    schedule: "0 2 * * *"
    exec: "/opt/securepixel/jobs/export_daily.sh"
    retention_days: 30
    description: "Daily export job"

Notes

  • version must be 1
  • log_dir and state_dir must be absolute paths
  • jobs is required and must be a mapping
  • Job names must match ^[a-zA-Z0-9][a-zA-Z0-9_-]{1,63}$
  • schedule must be a basic 5-field cron string (MVP)
  • retention_days is required (or defaulted from defaults)
  • exec should be an absolute path for scripts/binaries (recommended); plain command names must exist on cron's PATH
  • Relative exec values run relative to cwd (if set) or / (cron default), so absolute paths are safer

CLI commands

cronctrl validate

Parses your YAML file, applies defaults, and validates required fields, job names, schedules, and retention settings. It prints any issues it finds and exits non-zero if the config is invalid, which makes it safe to run in CI or before an apply.

cronctrl --config /etc/cronctrl/jobs.yaml validate

cronctrl list

Prints a table of jobs from the config, including schedule, exec, retention, and disabled status. Use --enabled-only to filter out disabled jobs.

cronctrl --config /etc/cronctrl/jobs.yaml list
cronctrl --config /etc/cronctrl/jobs.yaml list --enabled-only

Run a single job immediately (cronctrl run <job>)

Executes one job right away using the exact command and settings from the YAML, capturing output into per-job and global logs and recording a state JSON summary for status reporting.

Behavior:

  • Acquires a lock (unless concurrency: allow)
  • Writes per-job and global logs
  • Records state JSON with timestamps, exit code, and duration
  • Returns the job exit code
  • Runs in cwd if set; otherwise cron typically runs with / as the working directory
cronctrl --config /etc/cronctrl/jobs.yaml run export_daily

cronctrl apply

Installs the cron schedule and log retention configuration derived from your YAML. In other words, it generates the cron lines that invoke cronctrl run <job> on the schedule you defined and writes them to either /etc/cron.d/cronctrl (system-wide mode) or your user crontab (user mode). It also generates /etc/logrotate.d/cronctrl so job logs are rotated based on retention_days. cronctrl adds a PATH line (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin) so cron can find cronctrl when installed system-wide.

Modes:

  • etc-crond (default): writes /etc/cron.d/cronctrl
  • user-crontab: writes a managed block to the current user crontab
cronctrl --config /etc/cronctrl/jobs.yaml apply --mode etc-crond --user root
cronctrl --config /etc/cronctrl/jobs.yaml apply --mode user-crontab

Options:

  • --dry-run prints cron + logrotate output without writing
  • --remove-missing (user-crontab only): remove entries that no longer exist in YAML
cronctrl --config /etc/cronctrl/jobs.yaml apply --mode user-crontab --dry-run

cronctrl remove

Removes cronctrl-managed artifacts to stop scheduled runs. It deletes the cronctrl cron entries (either /etc/cron.d/cronctrl or the managed block in your user crontab) and removes /etc/logrotate.d/cronctrl. It does not delete logs or state unless you opt in with flags.

cronctrl --config /etc/cronctrl/jobs.yaml remove --mode etc-crond
cronctrl --config /etc/cronctrl/jobs.yaml remove --mode user-crontab

Options:

  • --purge-logs removes per-job logs and all.log from log_dir
  • --purge-state removes per-job state JSON and lock files from state_dir

cronctrl logs

Streams log output so you can quickly see what jobs are doing. By default it reads the global log (all.log), but you can target a single job with --job, control how many lines with --lines, and follow with --follow.

cronctrl --config /etc/cronctrl/jobs.yaml logs
cronctrl --config /etc/cronctrl/jobs.yaml logs --job export_daily
cronctrl --config /etc/cronctrl/jobs.yaml logs --follow --lines 200

cronctrl status

Summarizes the last run for each job using the state JSON files written by cronctrl run. This shows last start/finish timestamps, exit code, duration, and message, and can be filtered to a single job.

cronctrl --config /etc/cronctrl/jobs.yaml status
cronctrl --config /etc/cronctrl/jobs.yaml status --job export_daily

Logs and state

  • Per-job log: <log_dir>/<job>.log
  • Global log: <log_dir>/all.log
  • State JSON: <state_dir>/state/<job>.json

Example state:

{
  "job": "export_daily",
  "last_started_at": "2026-01-24T10:00:00Z",
  "last_finished_at": "2026-01-24T10:02:33Z",
  "last_exit_code": 0,
  "last_duration_seconds": 153,
  "last_command": "/opt/securepixel/jobs/export_daily.sh",
  "last_log_file": "/var/log/cronctrl/export_daily.log",
  "message": "ok"
}

Testing

pytest

Release

cronctrl uses a GitHub Actions release workflow that triggers on version tags (e.g., v0.2.0). When you push a tag, the workflow builds the sdist and wheel, attaches them to a GitHub Release, and (if configured) publishes to PyPI.

Typical flow:

git checkout main
git pull origin main
git tag v0.2.0
git push origin v0.2.0

Verify the published package:

pip install --upgrade cronctrl
cronctrl --version

Roadmap

  • Phase 6 polish: additional dry-run behaviors, schedule validation improvements
  • Phase 7: packaging for pip install (publishable build, ensure cron PATH compatibility)
  • Improve UX and output formatting

License

TBD

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cronctrl-0.2.2.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cronctrl-0.2.2-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file cronctrl-0.2.2.tar.gz.

File metadata

  • Download URL: cronctrl-0.2.2.tar.gz
  • Upload date:
  • Size: 19.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cronctrl-0.2.2.tar.gz
Algorithm Hash digest
SHA256 302514bb64fd29098727de17b251ecf0359535d42e465bbec3c3f1fced73ab3c
MD5 cb20c0e7fc92626604c44db727367957
BLAKE2b-256 201f36127ead37fa9f63cfb7e3e8a24608df9777712ee3d02bcd847bf1fd0007

See more details on using hashes here.

File details

Details for the file cronctrl-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: cronctrl-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cronctrl-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d916608b07917b87c5a3da825993c4f828836e863fa0afad76bb02746dca52a3
MD5 1d9b51a478ab6782c65a43ec9570ffba
BLAKE2b-256 efb622caaca56ffdc306fb4d4eb1dec8dd3a310f5aef0c9dfc3bff7a4e17ea89

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page