A watchdog for cron-style jobs. Teams register the jobs they care about, each job gets a unique check-in URL, and Cronmon emails the responsible person or team if a job doesn't check in within its schedule plus a grace period.
Cronmon sits between your scheduled tasks and the people who need to know when they break. Every job has a schedule (either a cron expression or a simple interval like "daily") and a grace period. When the job runs, it pings its check-in URL; if the ping doesn't arrive in time, Cronmon raises an alert and keeps re-alerting on each missed window until the job checks in again.
It's intended for internal IT use: nightly backups, config snapshots, tape rotation reminders, and the small army of quiet cron jobs that nobody notices until they stop running. Authentication is handled by SSO (Keycloak via Socialite), so there's no public signup.
Append a curl to whatever your cron entry runs. The full URL is
shown on each job's detail page in Cronmon.
0 2 * * * /usr/local/bin/nightly-backup && curl -fsS \
https://cronmon.example.ac.uk/check-in/00000000-0000-0000-0000-000000000000 \
> /dev/nullA few things worth knowing:
- The token in the URL is per-job. Treat it like a webhook URL: unguessable, but not a high-stakes secret.
- Use
-fsSso curl fails the cron command on a non-2xx response. - The response body is empty; the 200 status is the acknowledgement.
A check-in can carry a few figures about the run, such as how much a backup copied. Add them as a query string:
curl -fsS "https://cronmon.example.ac.uk/check-in/<token>?files=1234&bytes=5678901"- Any key names are allowed. Values that look like numbers are stored as numbers (so leading zeros are dropped), which keeps them easy to graph in Metabase or similar.
- Limits: up to 20 keys, key names up to 50 characters, values up
to 255 characters, and no array values like
files[]=1. Breaking a limit gets a422with a one-line reason and the check-in is not recorded, so with-fsSthe cron command fails visibly. - Metadata shows as extra columns in the job's recent check-ins, in the job's Download CSV export (full history, raw values), and in the API's check-in history.
- The query string ends up in proxy and web server access logs, so never put passwords, tokens or personal information in it.
Cronmon exposes the current estate figures at /metrics in
Prometheus text format, for scraping into Grafana — monitored,
alerting, silenced and never-checked-in job counts per team, plus a
shared team="Personal" bucket for user-owned jobs.
The endpoint is guarded by a single static bearer token rather than
SSO. Set CRONMON_METRICS_TOKEN in the environment to enable it;
until a token is set the endpoint returns 503, and a missing or
wrong token gets 403. Point Prometheus at it with the token as the
scrape credential:
scrape_configs:
- job_name: cronmon
scheme: https
metrics_path: /metrics
authorization:
type: Bearer
credentials: "your-CRONMON_METRICS_TOKEN"
static_configs:
- targets: ["cronmon.example.ac.uk"]A ready-to-paste version with your deployment's own scheme and host
is on the in-app API examples page (/api/help).
- PHP 8.4
- Lando for the local development environment
- A Flux UI Pro licence (Cronmon uses Flux Pro components, so the licence is needed to install Composer dependencies)
- Composer credentials for the Flux Pro repository in
auth.json
cp .env.example .env
lando start
lando mfs # drop, migrate and seed the dev databaseThe .env.example file is pre-configured for Lando, so the
defaults should work without further changes. lando mfs runs
the TestDataSeeder, which creates a small set of users, teams
and jobs to make the UI usable straight away.
Sign in as admin2x / secret (admin) or user2x / secret (standard user). MailHog is exposed on the Lando proxy so you can read alert emails locally.
lando artisan ... # any artisan command
lando composer ... # composer inside the appserver
lando npm ... # npm inside the node container
lando mfs # drop + migrate + seedFor anyone who'd like to run Cronmon themselves without our internal
Docker Swarm setup, there's a compose.yml in the repo root that
brings up the app, scheduler, queue worker, a one-shot migrations
service, MariaDB and Redis. Reverse-proxy / TLS termination is left
to you — the app is published on a configurable host port.
cp compose.dotenv.example .env # then edit — see notes inside
docker compose build # needs Flux Pro credentials
docker compose up -d
docker compose exec app php artisan cronmon:add-user --adminA few things to know:
- Flux Pro licence required to build the image. Cronmon's UI is
built on Flux Pro, Caleb Porzio's paid
component library that helps fund Livewire and Flux. Set
FLUX_USERNAMEandFLUX_LICENSE_KEYin.envbeforedocker compose build. There's no canonical prebuilt image — if you're using this app, please chuck a few quid Caleb's way. APP_KEYneeds generating once. Rundocker compose run --rm app php artisan key:generate --showand paste the result into.env, thenup -d.- Mail in demo mode. If you just want to see the app work without
wiring up real SMTP, the
mailpitservice incompose.ymlis commented out — uncomment it, setMAIL_HOST=mailpit/MAIL_PORT=1025in.env, and alert emails will land in Mailpit's web UI on port 8025. - SSO is the only sign-in path. Cronmon is built around Keycloak
via Socialite. The
KEYCLOAK_*variables incompose.dotenv.exampleneed to point at your own realm. - Persistence. MariaDB and Redis use named Docker volumes
(
cronmon_mariadb,cronmon_redis); back those up if the data matters to you.
On a brand-new install nobody can sign in via SSO yet — there are no users in the database for the allowlist to match against. Create the first admin from the command line:
php artisan cronmon:add-user # interactive walkthrough
php artisan cronmon:add-user kmc2y [email protected] McAuthor Kit --adminPositional arguments are <username> <email> <surname> <forenames>.
The interactive mode asks for the email first and prefills surname
and forenames from it when the address follows the
forename.surname[.N]@... pattern, so most of the time you just
confirm the defaults. After that the user can sign in via SSO and
the rest of the admin UI takes over.
The test suite uses Pest and runs against an in-memory SQLite
database via RefreshDatabase, so no migration step is needed.
lando test # full suite
lando test --filter=HomePageTest # one file or testOutside of Lando, php artisan test --compact works too if you
have PHP installed locally.
CI runs the suite against both MySQL and PostgreSQL on every push
(see .github/workflows/). Production runs on PostgreSQL but a
sibling system uses MySQL, and keeping both happy catches the
occasional dialect difference early.
Pull requests are welcome. The short version:
- Fork or clone the repository.
- Follow the Getting started steps above.
- Write a test for your change, make it pass, then run the full
suite with
lando test. - Run
vendor/bin/pint --dirtyto format any PHP files you've touched. - Open a pull request describing what changed and why.
Project-specific conventions live in CLAUDE.md and the .ai/
directory. Worth a skim before making larger changes.
Cronmon is released under the MIT licence.