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 validatefor schema checks and basic cron validationcronctrl run <job>to execute a job immediately with logging and state trackingcronctrl applyto generate cron entries and logrotate configcronctrl logsandcronctrl statusfor 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.
- 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_dirandstate_dirare absolute paths- each job has a valid
scheduleandexec
- Validate your config:
cronctrl --config /etc/cronctrl/jobs.yaml validate
- 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-crondwrites to/etc/cron.d/cronctrl(system-wide cron)--user rootsets the OS user cron will run the jobs as in/etc/cron.dmode
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
versionmust be1log_dirandstate_dirmust be absolute pathsjobsis required and must be a mapping- Job names must match
^[a-zA-Z0-9][a-zA-Z0-9_-]{1,63}$ schedulemust be a basic 5-field cron string (MVP)retention_daysis required (or defaulted fromdefaults)execshould be an absolute path for scripts/binaries (recommended); plain command names must exist on cron's PATH- Relative
execvalues run relative tocwd(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
cwdif 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/cronctrluser-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-runprints 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-logsremoves per-job logs andall.logfromlog_dir--purge-stateremoves per-job state JSON and lock files fromstate_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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
302514bb64fd29098727de17b251ecf0359535d42e465bbec3c3f1fced73ab3c
|
|
| MD5 |
cb20c0e7fc92626604c44db727367957
|
|
| BLAKE2b-256 |
201f36127ead37fa9f63cfb7e3e8a24608df9777712ee3d02bcd847bf1fd0007
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d916608b07917b87c5a3da825993c4f828836e863fa0afad76bb02746dca52a3
|
|
| MD5 |
1d9b51a478ab6782c65a43ec9570ffba
|
|
| BLAKE2b-256 |
efb622caaca56ffdc306fb4d4eb1dec8dd3a310f5aef0c9dfc3bff7a4e17ea89
|