Systemd timers (cronjob replacement) don't run when the system goes to sleep

This is Fedora 39. I used to run a cronjob that archives several podcasts for offline listening. I changed it to use systemd timers, but I can’t get it to reliably run when my system goes to sleep:

  • it does not wake up at specified time, even though WakeSystem=true

  • it doesn’t run the services when I manually wake the system from the screenlock, by keyboard action to wake up the screen and re-authenticate.

This problem just got a little more urgent because the current DNF5 plan removes dnf-automatic: instead they recommend running DNF updates off a systemd timer. Since updating is usually scheduled off-hours, it’s important for the overall Fedora security posture to reliably run them.

As you can see the timer is enabled but never triggers:

systemctl status --user everymonday.timer
● everymonday.timer - Monday evening timer for podcasts
     Loaded: loaded (~/.config/systemd/user/everymonday.timer; enabled; preset: disabled)
     Active: active (running) since Thu 2024-03-21 00:13:04 EDT; 2 weeks 0 days ago
    Trigger: n/a
   Triggers: ● getpodcasts.target

Mar 21 00:13:04 fedora systemd[2190]: Started everymonday.timer - Monday evening timer for podcasts.

The timer is supposed to trigger several services

[Unit]
Description=Monday evening timer for podcasts

[Timer]
OnBootSec=0min
OnCalendar=Mon 22:30
Unit=getpodcasts.target
WakeSystem=true

[Install]
WantedBy=basic.target  

so I created getpodcasts.target which runs multiple services

[Unit]
Description=get all podcasts target, triggered my everymonday.timer
Wants=getpodcast1.service getpodcast2.service
1 Like

WakeSystem= only works for the system manager, as documented in man systemd.timer.

OnCalendar= says:

When a system is temporarily put to sleep (i.e. system suspend or hibernation) the realtime clock does not pause. When a calendar timer elapses while the system is sleeping it will not be acted on immediately, but once the system is later resumed it will catch up and process all timers that triggered while the system was sleeping. Note that if a calendar timer elapsed more than once while the system was continuously sleeping the timer will only result in a single service activation. If WakeSystem= (see below) is enabled a calendar time event elapsing while the system is suspended will cause the system to wake up (under the condition the system’s hardware supports time-triggered wake-up functionality).

I think the (ineffective) use of WakeSystem= might be breaking that behavior.

This should be timers.target.

1 Like

I took out the WakeSystem= line and changed the WantedBy= to timers.target, and the timer is still not triggering.

$ systemctl --user status everymonday.timer
● everymonday.timer - Monday evening timer for carTalk
     Loaded: loaded (/home/przemek/.config/systemd/user/everymonday.timer; enabled; preset: disabled)
     Active: active (running) since Thu 2024-03-21 00:13:04 EDT; 4 weeks 0 days ago
    Trigger: n/a
   Triggers: ● getpodcasts.target

Mar 21 00:13:04 fedora systemd[2190]: Started everymonday

More importantly, contrary to what the docs seem to say, the timers do not seem to trigger reliably even after I wake up the system after their schedule elapsed during sleep. This is a regression to my previous setup of user-level cron jobs, although I am not sure if I should blame systemctl for not triggering timers, or blame deeper sleep modes that are being used now.

I note that the “Active Since” refers to the original setup in March, even though I did systemctl --user daemon-reload since that—is there another command that resets the timers?

Hmm

OnCalendar=Mon 22:30

Do you want to only run it once a week?

If I look at the timers on my system, they’re generally listed as active (waiting) and show a time on the “Trigger” line. I’m pretty sure that targets are always considered running once they’ve been successfully started, so I wonder whether this is a side effect of that? If systemd thinks your task is still running, it won’t reset the timer and run it again.

Maybe turn getpodcasts.target into getpodcasts.service? The only caution there is that if the timer expires while any of the wanted services (getpodcast1 or getpodcast2) are still running, the others would be called again but the still running service wouldn’t be. That doesn’t seem like it would be an issue here, but something to be aware of.

Something like this should work for getpodcast.service (not tested):

[Unit]
Description=Get all podcasts service, triggered by everymonday.timer and should start the individual services.
Wants=getpodcast1.service getpodcast2.service

[Service]
Type=simple
ExecStart=/usr/bin/true
1 Like

Deleting after re-reading the post this is not applicable.

1 Like

this. Also, what is a target needed for? I never used one. Just a service and a timer

1 Like

Re-reading this i noticed the target for the timer was a target vice a service.

You could try using

StopWhenUnneeded=true

in your target.

Or
If your services are just running different shell scripts it might be simpler to just use Type=oneshot with multiple ExecStart= lines.

1 Like

It looks like it’s being used to start multiple services when the timer is run.
Or am i miss-understanding the question.

1 Like