cronaute — a cron daemon for containers: one static binary, one user, no spool, and a read-only dashboard. A reimplementation of go-crond in Crystal, cut down to what a container actually needs.
The omissions are the design: no multi-user (no user column, no spool, no setuid), no reaping (it runs under tini as PID 1), no editing from the UI (any method but GET is 405), no persistence (state is in memory, history is in the JSON log). The crontab is edited out of band — mounted volume or gitops — and reloaded on change, atomically: a broken file leaves the daemon running exactly what it was running.
Standard five fields, optional leading seconds field, Vixie shorthands, @every with Go-style durations, and run-parts directories on named periods. Schedules run on the wall clock in TZ, falling back to UTC rather than the host zone so the same image schedules identically everywhere; DST edge cases live in a pinned cron_parser fork.
Two decisions I’d happily argue about:
/healthand/liveare separate routes because they trigger opposite reactions. Readiness goes red on a failed crontab reload and takes the instance out of service — restarting wouldn’t fix the file and would destroy the working schedule still running jobs. Liveness goes red on a tick loop that hasn’t turned in 30 s, which a restart does fix. Docker has a single health state, so the image’sHEALTHCHECKasks/live.- An occurrence that comes due with no free slot is dropped, not queued. Queuing piles one waiting fiber per occurrence behind a job that may never return its slot, then releases them in a burst if it ever does.
Prometheus metrics on /metrics, one JSON object per line on stdout, static amd64/arm64 binaries.
Source: GitHub - jbox-web/cronaute · GitHub (MIT).