Published 2026-07-31 · Reviewed 2026-07-31
A scheduler is not an owner
A practical way to design scheduled automation around missed runs, duplicates, evidence, approval and recovery instead of assuming a clock makes work reliable.
- automation
- home servers
- delivery governance
The clock did its job. The work still failed.
A scheduled task is easy to mistake for an operating process. Add a line that says “run at 07:00”, see it fire once, and the job feels automated. Then the machine is off at 07:00, yesterday’s run is still going, the network drops halfway through, or the task completes without producing the thing anybody actually needed.
The scheduler may have behaved exactly as designed. It only answered one question: when should an attempt begin? It did not decide whether a late attempt is still useful, whether two attempts may overlap, what counts as success, who sees a failure, or which outward-facing action needs approval.
That is why a scheduler is not an owner. Reliable scheduled work needs an operating contract around the clock.
Decide what a missed run means
A missed run is a product decision, not merely a technical error. Sometimes catching up is essential. A security scan skipped while a laptop was off may still be useful when it starts. Sometimes catching up creates nonsense. Sending Monday’s “today” briefing on Tuesday is worse than skipping it.
Different schedulers expose that choice in different ways. A systemd calendar timer with `Persistent=true` records when it last triggered and, when activated again, triggers its service if at least one run was missed while the timer was inactive, subject to any delay configured with `RandomizedDelaySec=`. Multiple calendar events missed during one sleep period become a single activation on resume. Kubernetes CronJob instead offers `startingDeadlineSeconds`: after the configured deadline, that occurrence is skipped and treated as failed while future occurrences remain scheduled.
The durable question is not which setting sounds safer. It is this:
Write those answers next to the schedule. Without them, the platform default quietly becomes policy.
- If this run is late, is its output still valid?
- Should missed occurrences coalesce into one catch-up run?
- At what age does catch-up become duplication or misinformation?
Assume overlap and duplication can happen
A schedule is not a guarantee of exactly-once execution. Kubernetes documents its CronJob creation as approximate: in some circumstances two Jobs may be created, or none. Its guidance is therefore to make the Job idempotent — safe to repeat without creating a second harmful effect.
Overlap is a separate choice. Kubernetes names three policies clearly: allow concurrent runs, forbid a new run while the previous one is active, or replace the active run with the new one. systemd timers have a different default shape: if the service they activate is already active when the timer elapses, it is left running rather than restarted.
Translate those mechanics into the work itself:
“Don’t run it twice” is not a control. A lock, unique run key, immutable output identifier or downstream duplicate check is a control.
- A read-only report can often run twice safely, provided only one final result is selected.
- A backup needs a lock or repository behaviour that prevents two writers corrupting the same target.
- A notification needs an idempotency key so a retry does not message someone twice.
- A publication task should prepare a candidate repeatedly if needed, but the public release should remain a separate approved action.
Define success beyond an exit code
An exit code proves that a process reached one of its own endings. It does not prove the intended outcome exists.
For a scheduled export, success might require a non-empty file, a checksum, a timestamp inside the expected window and a test read. For a report, it might require a source retrieval time, a visible “not checked” marker for unavailable inputs, and delivery to the review queue. For a deployment candidate, success might mean a fixed commit identifier, green checks and a review record — not that production changed.
Keep a small evidence record for every run:
1. Scheduled time and actual start time. 2. Unique run identifier. 3. Input version or retrieval time. 4. Output identifier and validation result. 5. Final disposition: succeeded, skipped, superseded, awaiting approval or failed. 6. Named owner and recovery action when it does not succeed.
Retention matters too. Kubernetes lets operators keep a chosen number of successful and failed Job records. Whatever tool you use, keep enough history to answer “what happened?” without keeping endless logs full of sensitive content. Metadata and verdicts usually age better than raw payloads.
A worked example: a scheduled article candidate
Consider a fictional learning site that wants a useful article every Wednesday morning.
A brittle design says: at 07:00, generate an article and publish it.
A bounded design separates the stages:
The scheduler still starts the process. It simply no longer holds authority it cannot exercise safely.
- The timer creates one run with a unique Wednesday key.
- If the machine starts late, the run is allowed until midday; after that it is skipped because the date-sensitive candidate is stale.
- A lock prevents overlapping authorship runs. Repeating the same run key updates the same candidate instead of creating another.
- The task records its sources, retrieval date, validation results and immutable content version.
- A reviewer checks claims, privacy, clarity and usefulness against that exact version.
- A person approves the exact reviewed candidate.
- A separate publisher performs the outward-facing action and verifies the public page.
- Failure at any stage leaves the candidate unpublished, with an owner and a recovery step.
Use timing controls for load, not false precision
Scheduled does not always mean exact to the second. systemd documents a default timer accuracy window of one minute, partly so local wake-ups can be coalesced. It also offers randomized delay to spread similarly timed work and reduce load spikes.
That is usually helpful. Ten home-server maintenance jobs do not become more reliable because they all fight for disk and network at exactly midnight. If the task genuinely has a deadline, model the deadline and verify completion. Do not treat a visually precise cron expression as proof of punctual delivery.
Limits and what we would do next
This pattern does not make unreliable code reliable. It does not replace backups, transactional storage, authorization or monitoring. Idempotency can also be difficult when the downstream system offers no duplicate protection. In that case, keep the irreversible step behind a human gate or redesign the integration before adding more retries.
What we would do next is pick one existing scheduled task and write six lines beside it: late-run policy, overlap policy, duplicate control, success evidence, failure owner and recovery action. If any line is blank, the clock is currently carrying a decision it cannot make. Fix the blank before adding another schedule.
Sources and limits
This article synthesises the sources below into a practical explanation. It is not a security standard, legal advice, or a guarantee that guidance current at review time still applies — check the review date above against your own situation.
- CronJob — Kubernetes. Documents approximate scheduling, possible duplicate or missed Jobs, idempotent workloads, start deadlines, concurrency policies and retained Job history.
- systemd.timer — systemd. Documents timer-to-service activation, active-service behaviour, catch-up semantics, timing accuracy and randomized delay controls.
More from Server Attic
The alert you always ignore
An alert nobody acts on is not monitoring. It is noise with a notification sound. A practical way to decide which home-server alerts deserve to interrupt you, using precision, recall, detection time and reset time.
What AdGuard Home can and cannot fix
What network-wide DNS filtering genuinely solves, the ceiling AdGuard's own documentation sets out, and how encrypted DNS and Encrypted Client Hello change the picture.
A backup is not a restore plan
A practical home-server guide to turning backup jobs into evidence you can actually recover from, using restore drills, integrity checks and clear limits.

Community comments
Comments are reviewed before publication. Keep discussion constructive: no harassment, hate, threats, doxxing, spam, illegal material, or attempts to evade moderation.
No approved comments yet.
Sign in to join the discussion.