Scheduled work is easy to underestimate. A cron entry can be one line long while the job behind it takes hours, touches a large amount of data, and competes with several other jobs for the same application and database.
That is the problem behind my mautic-cron-scripts project. The useful part is not a clever command. It is turning a pile of periodic tasks into something that can be operated: predictable enough to schedule, visible enough to debug, and constrained enough that one slow run does not create a backlog of overlapping work.
Start with the work, not the schedule
“Run every five minutes” is not a useful definition if a normal execution takes an hour. The schedule needs to reflect the work being done: segment updates, email processing, cleanup, and other application maintenance have different runtimes and different failure modes.
I prefer to begin by measuring each task. Record when it starts, when it finishes, whether it succeeded, and enough output to explain a failure. Once those measurements exist, the schedule can be chosen from evidence instead of habit.
Long-running jobs need boundaries
A job that can run indefinitely is difficult to reason about. Batch limits, explicit time windows, and sensible cleanup policies give the process a boundary. They also make it possible to resume work without treating every interruption as a disaster.
Overlap is another boundary problem. Two copies of a heavy update can make both copies slower, increase database pressure, and leave operators unsure which result is authoritative. A lock or an equivalent single-run guard is a small addition that prevents a surprisingly large class of incidents.
Make the logs answer operational questions
Application logs often tell you what happened inside the application. A cron wrapper should answer the questions around it:
- Which task ran?
- When did it start and finish?
- Which environment was it running against?
- Did it exit successfully?
- How much work did it attempt?
That context turns “the queue is behind” into a useful investigation. It also makes a handoff possible; another person does not need to reconstruct the entire scheduling system from scattered crontabs.
Be deliberate about where a job runs
In a multi-server setup, a scheduled task should have an explicit home. A primary-host check, environment-specific configuration, or scheduler with a clear ownership model prevents the same work from running everywhere just because the code was deployed everywhere.
This is especially important for jobs that send messages or mutate shared state. “It ran” is not sufficient. It ran on the intended host, against the intended environment, with the intended credentials and limits.
Deployment is part of the job
Small operational tools deserve a repeatable installation path. The scripts should have a known location, controlled ownership, writable log directories, a cron deployment mechanism, and log rotation. Without those pieces, a script can work perfectly in a terminal and still fail as a service.
The broader lesson is portable: scheduled automation is production software when it moves data, sends email, or changes an application. Give it configuration, logs, failure handling, and a way to verify that the deployed version is the version you meant to run.
Current as of September 2026. The examples here are intentionally application- and environment-agnostic; the implementation is available in mautic-cron-scripts.
Leave a Reply