skip to content

Four hundred servers each run the same systemd timer with OnCalendar=*-*-* 03:00:00, and every night they hit the same internal package mirror at once. Which [Timer] directives spread that load, and which one does not do what people expect?

level: seniorimportance: nice to knowfreq 38%

answer

  1. exact wall-clock time is a sync primitive
  2. one directive randomizes, one only batches
  3. per-host offset can be stable
  4. derived from the machine ID
  5. the recovery path stampedes too

basics

~20 s

RandomizedDelaySec= is the spreader: it delays each trigger by a random amount between zero and the given span. AccuracySec= does not spread load across a fleet — it only widens the window in which systemd may fire a timer so it can batch wakeups on that one host.

solid answer

~50 s

Add `RandomizedDelaySec=` to the `[Timer]` section — `RandomizedDelaySec=45min` turns a synchronized 03:00 stampede into arrivals scattered across 03:00–03:45. Each host draws its own delay, so the fleet self-spreads without you templating a different minute per machine. If you also want a host's offset to be stable night to night rather than re-drawn each time, set `FixedRandomDelay=yes`, which derives the offset from the machine ID so each server keeps its own slot — useful when you want reproducible timing or per-host cache locality. The directive people reach for by mistake is `AccuracySec=`. It defaults to one minute and controls how loosely systemd is allowed to schedule the elapse so it can coalesce timer wakeups and let the CPU stay idle longer; it is a power-saving knob on a single host, not a fleet-spreading one, and every host is still aiming at the same window. Verify the result with `systemctl list-timers`, which shows each host's computed next elapse.

code

ini · 11 lines
ini
[Unit]
Description=Nightly mirror sync

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=45min
FixedRandomDelay=yes
Persistent=true

[Install]
WantedBy=timers.target

go deeper

for a junior

Know that an identical OnCalendar= time on many hosts makes them all fire together, and that RandomizedDelaySec= is the directive that scatters them.

for a middle

Explain how the random delay is drawn per elapse, what FixedRandomDelay=yes changes, and why AccuracySec= is a wakeup-coalescing knob rather than a spreading one.

for a senior

Size the window from job duration and the target's tolerable concurrency, and cover the recovery path where persistent timers all come due at once after a fleet-wide restart.

for a principal

Decide the standard for scheduled work across the fleet: randomized versus templated placement, whether client spreading is masking a capacity gap at the target, and who owns that ceiling.

## The failure mode A schedule written as an exact wall-clock time is a synchronization primitive. Four hundred hosts with NTP-corrected clocks and `OnCalendar=*-*-* 03:00:00` will start their jobs within milliseconds of one another. Whatever they touch — a package mirror, an artifact store, an object-storage bucket, a database read replica — sees a spike four hundred times its steady state, and the classic symptoms follow: connection pool exhaustion at the target, timeouts, retries that amplify the spike, and a job that succeeds when you test it on one host and fails at 03:00 on the fleet. ## The directive that actually spreads ```ini [Timer] OnCalendar=*-*-* 03:00:00 RandomizedDelaySec=45min ``` `RandomizedDelaySec=` takes a time span and delays each elapse by a random amount between zero and that span. The choice is made per host, so no coordination is needed: the fleet's arrivals become roughly uniform across the window. Sizing it is straightforward arithmetic — take how long one run takes and how much concurrency the target can absorb, and pick a window wide enough that expected concurrency stays under that ceiling. Four hundred hosts, a two-minute job, and a target comfortable with twenty concurrent clients wants a window of at least about forty minutes. ## Making the offset stable By default the delay is re-drawn on each elapse, so a given host lands at a different minute every night. `FixedRandomDelay=yes` changes that: the offset is derived deterministically from the machine ID and the unit, so each host keeps the same slot indefinitely. Reach for it when you want the timing to be reproducible — for correlating a nightly job with a graph, for staggering hosts against a cache that warms per shard, or simply so that "host 47 runs at 03:19" stays true and debuggable. ## The knob that does not do this `AccuracySec=` is regularly proposed as the answer and is not. It specifies the accuracy with which the timer shall elapse — the window, starting at the nominal time, inside which systemd may actually fire it — and it defaults to one minute. Its purpose is power management: by allowing slack, systemd can coalesce several timers into one wakeup and let the CPU sleep longer, which matters on laptops and dense virtualized hosts. Two consequences follow: 1. The default one-minute slack does not meaningfully de-synchronize a fleet — everyone is still aiming at the same minute. 2. Its intent is the *opposite* of spreading. Coalescing pulls firings together so they share a wakeup, rather than pushing them apart. If you genuinely need a timer to fire close to its nominal instant, `AccuracySec=1us` narrows the window, at the cost of denying systemd the batching. That is a legitimate use — it is just not a load-distribution tool. ## Alternatives, and when they are better - **Templated schedules.** Generating a distinct `OnCalendar=` minute per host from configuration management gives you exact, auditable placement, at the cost of managing the mapping and re-balancing when hosts come and go. Randomized delay is usually the better default; templating wins when the distribution has to be provably even or aligned to something else. - **Monotonic timers.** `OnBootSec=`/`OnUnitActiveSec=` inherently stagger, because hosts boot at different moments. Good when the requirement is really an interval; useless when the job must land in a nightly window. - **Fix the target.** Spreading the clients hides a server that cannot absorb burst. If the mirror falls over at four hundred concurrent pulls, the delay window buys time but the capacity question is still open. ## The interaction with catch-up runs A fleet recovering from a power event brings up many hosts within the same minute, and any timer with `Persistent=true` that is owed a run wants to fire immediately. That is the same stampede in a different costume, and `RandomizedDelaySec=` is the same mitigation — worth stating explicitly in an interview, because it shows you have thought past the steady state to the recovery path. ## Verifying ```bash systemctl list-timers mirror-sync.timer ``` Run it on a handful of hosts and compare the computed next elapse; with a randomized delay in place the times should visibly differ. On the target side, the honest confirmation is the arrival-rate graph flattening from a spike into a plateau.

  • What does AccuracySec= actually control, and when would you lower it?
    It sets the window, beginning at the nominal elapse time, within which systemd may fire the timer, defaulting to one minute. The slack lets systemd coalesce timer wakeups so the CPU sleeps longer. Lower it — down to `AccuracySec=1us` — only when a job genuinely must run near its exact instant, accepting the extra wakeups that come with it.
  • Why might you prefer FixedRandomDelay=yes over a freshly drawn delay each night?
    Because the host then occupies the same slot every run. That makes timing reproducible when correlating with graphs or logs, keeps per-host cache and shard locality stable across nights, and stops a host from occasionally drawing a very early delay several nights in a row. The tradeoff is that the distribution is fixed rather than re-randomized.
  • After a data-centre power event, hundreds of hosts boot together with Persistent=true timers owed a run. What do you expect, and what mitigates it?
    Every owed job fires within seconds of the timers starting, layering a fleet-wide burst on top of boot itself. `RandomizedDelaySec=` spreads those catch-up triggers over its window just as it spreads scheduled ones, so the recovery path stops being a second outage. Sizing that window for the recovery case, not just the nightly one, is the judgment call.

saying these in an interview costs you the question

  • Proposes AccuracySec= as the load-spreading mechanism
  • Thinks the one-minute default accuracy already de-synchronizes a fleet
  • Adds a sleep $((RANDOM % 3600)) inside the job script instead
  • Ignores that persistent catch-up runs stampede on recovery
  • Assumes randomized delay removes the need for target capacity

context