My favorite metonymic technology term is "cron job": even though cron may not literally be the daemon that executes actions on a schedule, we apply the term to anything that walks like a cron and quacks like a cron. As Patrick McKenzie likes to point out, cron jobs are one of the most eminently useful computing primitives. They offer utility that's almost immediately obvious for plenty of use cases that almost everybody has: do this every day; do that once a month.
And yet. You probably shouldn't use literal cron (or its more modern cousins) for scheduled tasks! In 2026 there are more modern options available, and my favorite is the humble systemd timer. I love systemd timers. If you don't love them yet, maybe I can show you the reasons why you should love them, too.
cron? Cooked?A systemd timer is a type of unit that schedules other units (usually a service) on a particular schedule. (How a systemd service unit works is another article, but you can logically consider the .service target of a systemd timer to be a script.) Timers are effectively a functional replacement for a traditional cron daemon (though you could conceivably run both), and timer calendar settings offer some similarities to help bridge the gap from traditional cron-like expressions.
At this point the systemd haters peer out of the woodwork in anticipation of torpedoing timers because they are part of the systemd project and because they replace mature (if clunky) technology. I'd rather not spend our time arguing about cron, so briefly consider why newer solutions like systemd timers that benefit from years of hindsight are better:
$PATH settings make cron script execution difficult to predict.stdout and stderr output often ends up in a black hole (and, often, sent to the host's mail system, which is usually not what you want to happen.)01,31 04,05 1-15 1,6 * isn't easy or intuitive for humans to read.Incidentally, timers solve all these problems (and more.)
We can cover the basics without a lot of ceremony. First you need a target for a timer to execute. On a Linux host with systemd operational, placing the following unit contents at /etc/systemd/system/roulette.service installs a service with a 1 in 10 chance to be free (i.e., shut down your computer):
Systemd
Font used to highlight strings.
Font used to highlight keywords.
Font used to highlight type and class names.
[Unit] Description=1 in 10 chance to break your chains
[Service] ExecStart=/usr/bin/env bash -c '[[ $(($RANDOM % 10)) == 0 ]] && systemctl poweroff || echo LIVE ANOTHER DAY'
Update: [2026-05-05 Tue]
Twitter mutual HSVSphere points out that the service option ExecCondition= offers a native way to handle conditional execution. This is a more tightly-integrated way to express "should I continue to execute?" and I agree that it offers a clearer way to express intent at the unit level (I'm using absolute paths here for a NixOS system):
Systemd
Font used to highlight strings.
Font used to highlight keywords.
Font used to highlight type and class names.
[Unit] Description=1 in 10 chance to break your chains
[Service] ExecCondition=/run/current-system/sw/bin/bash -c '[[ $(($RANDOM % 10)) == 0 ]]' ExecStart=/run/current-system/sw/bin/systemctl poweroff
This has the same effect as the prior bash conditional, and you end up with different wording in the journal that (in my opinion) expresses the situation more clearly for you when the condition is met:
May 05 11:05:32 diesel systemd[3117]: Condition check resulted in 1 in 10 chance to break your chains being skipped.
In general, leaning into the options that systemd presents is a better experience than scripting your own. (Another example would be to use OnFailure= to react when your service scripts fail or Restart= to attempt recovery in the case of ephemeral failures.)
Associate that service with a timer by placing a file with the same file stem (roulette) at /etc/systemd/system/roulette.timer:
Systemd
Font used to highlight keywords.
Font used to highlight type and class names.
[Unit] Description=impending destruction
[Timer] OnCalendar=10:00
[Install] WantedBy=timers.target
What I mean by associate is that, by default, a timer's Unit= setting will choose a service unit with a matching stem suffixed by .service. In this case, roulette.service. You can always change this if you want to execute a service with a different unit name.
I want to call out a few things right away:
ExecStart= target does not run as a shell command by default. You should treat the absolute path target like a script or, in our case, an interpreter that expects a script as a string argument. For example, ExecStart=/usr/bin/echo Hello | /usr/bin/awk straight-up won't work; the pipe makes no sense in context here.ExecStart= argument does not inherit any environment variables by default (outside of some system manager defaults), so we begin with a pretty bare $PATH by default. Executing /usr/bin/env is a shortcut to ensure things like systemctl are available, but out of the box, you get a clean state to begin with. If we had used a bare ExecStart=/usr/bin/bash, we'd have the basics in $PATH, but using env here is an extra safeguard.You can roll the dice without the aid of the timer at all:
shell
systemctl start roulette
Although note that you cannot enable this service without any usable [Install] section: our timer is the canonical way to make the service run in a consistent way. Also useful to highlight that systemctl operates on roulette.service by default without any explicit suffix.
When applied to a .timer unit, the systemctl start subcommand puts it on the clock, per se, but does not actually execute the Unit= target.
shell
systemctl start roulette.timer
The timer is now active, but not the service.
Depending on the moment in time, status will tell you when to next expect the timer to decide your fate:
shell
systemctl status roulette.timer
You'll see plenty of information about the timer on its status page, including the next time it'll fire:
Trigger: Sat 2026-04-18 10:00:00 MDT; 35min left
That's the simplest timer onboarding: create a target, place the target service file alongside a timer with a schedule, and start the timer (not the target) to get the schedule started. Because the .timer defines an WantedBy= within [Install], we can ensure the timer comes up at boot time too, not just when we start it:
shell
systemctl enable roulette.timer
Let's move on past the basics.
Arguably the most important bit of information about timers is how to express a schedule, whether a repeating period of time (which the manual usually refers to as a time span) versus a calendar event (or a timestamp). Fortunately, I think the man page for this under systemd.time(7) is actually very good with plenty of examples. You should use it as the first resource when writing timers; it's good (or better) than, uh, casual blog posts by casual writers.
systemd also ships with a command-line tool called systemd-analyze which includes the ability to validate and explain time expressions from the command line directly in an imperative way to help understand them. You can even disambiguate the classic wildcard cron expression which systemd-analyzer can parse and then explain to you, complete with the expected execution times:
shell
systemd-analyze calendar '*-*-* *:*:*'
Normalized form: *-*-* *:*:*
This blog post is not the place to reproduce the entirety of systemd.time(7) verbatim, so I encourage you to Read The Helpful Manual (RTHM). Writ small, you can pretty simply define either a recurring wallclock period or, in contrast to plain old cron, a recurring period of time against some previous event.
The first category of time expressions is easy to envision. For example, in fully-qualified form, daily means:
*-*-* 00:00:00
You can use shorthand terms like daily, write out the complete form, or use any other supported value listed out in systemd.time(7) and subsequently validate your assumptions against systemd-analyze.
The second category of time expressions apply to "run this relative to some other event." This distinction from "run at the same time very day" is very often what you actually want. Consider a job that clears out a temporary directory, for example: if a cron expression lapsed right after boot, there probably isn't much to clean out of /tmp at all. But if you encode "execute an hour after my computer has started and then every hour after that", the schedule logic is meaningful for what the related service is actually doing.
This is easy to do in a timer:
Systemd
Font used to highlight keywords.
Font used to highlight type and class names.
[Timer] OnBootSec=1h OnUnitActiveSec=1h
That is: "run an hour after the machine starts" (which will execute once) and also "run one hour after my Unit= runs" (which implicitly makes the timer repeat indefinitely.)
Periodic time spans like this fit the "every once in a while" use case surprisingly more often than "run at this minute every hour" and similar expressions. Another good example is a timer I use every December to poll the Advent of Code API for a Slack bot I wrote for some friends. The */15 cron expression honors the "every 15 minutes" policy that their API requests, but since that's the easiest way to express it in cron language, I'm sure it makes spiky traffic alongside everyone else polling the API! Starting my timer when I've made a code fix that runs whenever 15 minutes has lapsed is all I care about, and probably creates less of a thundering herd problem.
Calendar versus time span units is probably the biggest conceptual leap from a traditional cron job, but timers offer more, too.
My favorite high-level command to get a picture of a machine's timer situation is the list-timers subcommand. Here's my host's summary:
shell
systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
From one command you glean a total picture of anything executing on a timer schedule. Very useful.
list-timers is part of a family of systemd subcommands that I use fairly often. Others that are useful include list-units and list-paths (the latter is a more recent addition to systemctl.)
Waking a suspended system to run an important script even if you're not around to perform the physical action of, say, lifting a laptop lid sounds like a daunting feat until you find WakeSystem=:
WakeSystem=
You can imagine the utility for something like this. On a distribution that supports downloading package updates before using them (like Arch or NixOS, for example), you can pre-fetch update packages late a night for morning updates when you're at a keyboard, and there are plenty of other ideas you could apply this too as well. The man page highlights that you'll need to manually re-suspend if you intend for that to happen after your .service is done.
I touched on the thundering herd problem a few paragraphs ago, which is the systems problem of, "what happens when a set of processes all wake up at the same time?" If every Debian system in the world were hard-coded to apt update at 00:00:00, midnight would be a bad, spiky time for everyone.
Two timer options called FixedRandomDelay= and RandomizedOffsetSec= help:
FixedRandomDelay=
I've used this for real systems that check in to update software. Not only does it help with thundering herd problems, but spreading out execution along a uniform distribution ensures that the behavior is consistent and avoids disruptive activities like restarting daemons that may be coordinating distributed services.
In general, timing options are very configurable and expose a great deal of granularity (again, all of which are explained in the man page.)
This option is particularly well-suited for scripts on a schedule that shouldn't ever be skipped due to a suspended laptop but may not warrant WakeSystem=:
Persistent=
If you're scheduling a system to checkin to configuration management but the host has undergone downtime, slapping Persistent= on the .timer can mean the difference between converging onto correct state immediately after coming online versus whenever the timer may normally fire (which could be a long time.) There are other good examples of service activations you don't want to wait for if the timer detects a missed activation: system updates, checking for batch jobs, that kind of thing.
If you start writing timers in earnest, bear the following in mind:
systemctl --user) are totally valid, but keep an eye on the target you use for [Install]. Sometimes the appropriate target to use for this is default.target depending on the distribution.cron. Fellow systemd cult members can rely on timedatectl timesync-status to peek at their synchronization status.Why? It's one of those fuzzy and somewhat hard to explain things. The systemd approach just maps more cleanly to my mental model of "how things should work" I guess. And maybe some of it is that I did indeed experience plenty of " Ambiguous $PATH settings make cron script execution difficult to predict" in the past, although it's not just that.
I won't sit here and claim that systemd timers are necessarily better than cron in any universal / objective sense. But they've won me over, for what it's worth.
Ain't that the truth. Literally every crontab I've written for the last 10 years has had this in it:
2>&1 | logger -t cron-WHATEVER
...and that does a pretty good job of capturing anything that the script emits and making it easy to grep for in syslog the following morning.
But I'm still amazed at how many crontabs I run across that don't capture any output at all.
And in fact I do have a use-case for needing to run something ~5 minutes after the system boots and then every ~12 hours onward from there. It's great that systemd timers has me covered!
For what it's worth there are usually web apps popping up that can decipher goofy cron time/date incantations. [1] This one has a git repo in the top right, not my repo. Maybe clone it just in case their site goes away some day.
I have knocked together a systemd service or three based on google copypasta. But generally, for cron jobs, why make it complicated? One line in /etc/crontab and done. I generally call an encapsulation script that sets the right environment variables, uses absolute paths, captures stdout/stderr if required and so on. I just want the simplest possible way to launch that script on a schedule.
I am dealing with mostly non systemd system: BSD, Alpine, termux On BSD anacron works well, but I do not why I am always running into problems with the cronie anacron implementation. And it is very hard to debug.
I would really like a simple modern cron/anacron alternative.
Cronicle looked cool but it is node.js, a bit heavy and being replace now by their new product called xyOps anyway.
I wish documentation for tools would explain their abstractions concepts in terms of its primitives.
Great post, thanks!
One of our customers called in with a production down incident caused by a full disk. We got a copy of the VM and took a look. Investigation revealed that / was full because /var/log was full and that our 'logrotate' timer unit that was scheduled to run once a day had run either exactly never or exactly once... I can't remember which. Further investigation revealed no difference in software load or configuration between this VM and a VM that had a functional logrotate timer unit. Exactly one VM out of hundreds of identical VMs at this site (and many multiples of that at other customer's sites) were affected by this. Advising the customer to clear out /var/log and reboot did not unstick 'logrotate', and none of the diagnostics or fixes we could find anywhere unstuck it. Once "systemd-crond" decided to never schedule this job ever again, it stuck to that decision.
After a lot of searching, we found an open bug report from a year or three prior where someone reported exactly the same symptoms and was scheduling a unit with pretty much the same set of unit configuration flags that we were using. The conversation from the core devs ran through the pattern that one gets used to seeing when one runs into SystemD bugs that are caused by extremely complex unanticipated interactions between parts of the project: "That's not a bug, only an idiot would want that to work.", "Oh, we don't document that that's not supposed to work?", "Wow, okay, yeah, I can see how that maybe should work. That it doesn't sure does seem weird.", "Having said that, I don't know if it's supposed to work, or if it's unsupported. Someone should really either document that or fix it."... and then the behavior is neither fixed nor documented. [1] Absent any actual explanation for the failure, we ended up swizzling the options in our 'logrotate' unit and praying that satisfied whatever gremlin arose from the depths to trouble our customer.
SystemD contains an enormous -and ever-growing- amount of accidental complexity, and has a set of core maintainers who are generally disinterested in either documenting the places where one or more complex systems bind together to cause stop-the-world problems or fixing the systems involved so that they don't bind up. It's a fine project until it's very, very suddenly not, and then you're absolutely SOL. If you're lucky, you can shuffle around what you're doing [2] and hope that avoids the problem. [3]
[0] Some folks use the spelling "SystemD" to mock the project. I use the spelling "SystemD" to distinguish between "the entire systemd project" and systemd(1). I do this because some folks will make a claim like "systemd is very, very small and self-contained. I don't understand why anyone would say otherwise.", but what they are actually saying is that systemd(1) is a fairly small program that doesn't do all that much when run as PID 1. It sucks minor amounts of ass that the project and the program it runs as PID 1 share the same name, but what can you do?
[1] No, I don't have a link to the open bug report. This was more than a year ago, so the bug ID has been long forgotten.
[2] The term of art for this practice is "wave a dead chicken at it".
[3] Plus, like, even disregarding most of the rest of my report... how in the hell do you design a cron that knows a job is scheduled to be run periodically, can tell you how long it has been since it last ran, but never manages to run it? To me, that's unforgivable. It's a "You had one job!"-tier cockup.
> Ambiguous $PATH settings make cron script execution difficult to predict.
What makes you say that? You can set the PATH right in the crontab. Is that harder to "predict" than it being set in /etc/bashrc, ~/.bashrc, ~/.profile, ~/.bash_profile, /etc/systemd/β¦, or wherever else?
> You might feel cool knowing the scheduling grammar by heart
I've used Linux since 1994 and I don't know it by heart. But luckily it's pre-printed in the crontab as comments:
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
You just put numbers aligned with the titles.The rest of the complaints, sure. Next time I need a cronjob, I'll try it out.
Yet there's always something new to learn and actually consider as another useful tool.
I've noticed more and more open source projects recommending timers as a deployment method and I think that's great!
Btw this is my repo for the backup automation: https://github.com/gchamon/borg-automated-backups
But now obviously we were so blind and wrong all this time and the only true solution is of course systemd.
Which makes it nice to distribute a tool for NixOS so that it can lean into systemd instead of as some bolted-on afterthought.
Makes me wonder what you'd do if you were distributing a lifecycle-heavy tool for Linux users in general since systemd isn't ubiquitous.
I use a systemd timer to run a monthly scrub for my btrfs pool. Kinda cool how you can do increasingly useful things like skip the next scheduled event if the user initiates a scrub, do or don't accumulate tasks if you have a monthly task but the machine was offline for 6 months -- or fold them into a single task, etc.
I will admit thought, timers are up there in terms of being the clunkiest systemd unit type to use on a regular basis. I get why they're split up into two files and require different start vs enable syntax's, but man sometimes I just want to create a file that runs a script and be done with it.
Like imagine trying to explain systemd timers and services and unit files to a beginner.
https://www.freedesktop.org/software/systemd/man/latest/syst...
It's a shame docker never supported it. I feel like if they had got on board all those years ago there would be broad support across the software ecosystem for it and we wouldn't need half of these complicated iptables rules and proxies and service mesh. It would be a step towards a capability based system.
OK but I don't want to hardcode $PATH in the crontab just so I can test the cronjob. Barring the hardcode, $PATH is one thing when cron runs and another when you try out the command yourself. systemctl start foo.service starts the command inside with the same environment as when the timer fires so you know it'll work the same.
On the flip side, your cron job will run at the time you specify in the crontab. Your systemd timer, on the other hand, may fire at the specified time (and most of the time, it will), but it can also suddenly stop firing once it has fired on a February 29th and then never fire again, due to logic bugs in systemd, or it may or may not fire when you "restart" the timer unit, due to logic bugs in systemd (that's when it only has OnCalendar, so yes, definitely a bug).
Same here.
We are now considered old and therefore irrelevant. The new generation uses timers and couldn't care less about cron that has served us just fine for decades.
I use cron and my general attitude towards LP and systemd is very similar to the attitude of LP and systemd to us.
/some/shell -l myjob.sh
or sometimes . ~/.profile && cd /some/where && ./job >>cron.log 2>&1another benefit is having logs in one place for the job; cron's "send a mail when there is any amount of output text" is just annoying behaviour, but also only place to get the job output unless you redirect it somewhere. Also starting from timer vs just doing systemctl start job.service is the same so easier to debug
other than that the few improvements in how to specify run time have been pretty useful.
For example, setting timer as "persistent" will mean any run "lost" to machine powered off will just be ran next time after boot, so you can have job on your PC that is just "run backup at 2AM" and if you turn it off before that you get the backup done first thing in the morning
There is also both random, and fixed (depending on machine UUID) random delay so avoiding thundering herd problem with backups is also pretty convenient.
There is even option to wake a device for the job if necessary tho the problem of shutdown is left to the user. And picking whether to start counting to next timer from previous one or from the job's end.
What I would like also is to have job summary page ("hey this job was done X times but failed Y times") but that's probably better left to external tooling
> You can set the PATH right in the crontab. Is that harder to "predict" than it being set in /etc/bashrc, ~/.bashrc, ~/.profile, ~/.bash_profile, /etc/systemd/β¦, or wherever else?
There is* a common trap as the cron PATH is usually just /usr/bin:/bin so anything in /usr/local/bin, or in /sbin won't be there.
I am perfectly happy with projects recommending timers as long as I can ignore them and use cron.
It sat unused and powered off for a couple of years after he passed, until I needed a color print.
Didn't do anything but hook it up to power and print. Took about 1/5 of a page until all colors were back in action, after that it printed about 20 pages flawlessly.
I think it was software and not hardware, but for some reason when I had that printer hooked up to my computer and idle for more than a week, it would simply stop printing. I probably could have dug through logs and figured it out, but I instead set up a cron job to print a test page every Monday and Thursday. The test pages would just have something on the top that said something like LOL PRINTER WORKS.
This wasn't actually as wasteful as it sounds; I was taking a boatload of math courses and needed tons of scratch paper in order to do my problems. Since it was scratch paper and would eventually end up in the trash anyway, I would usually prioritize doing my problems on failed prints and/or test prints, and I would usually exhaust those and then use blank paper afterwards.
Coz it's looks crazy complicated to set them up.
> runs the service as soons as the system is available.
cron has the @reboot option which I use for a few scripts and works great.
To do this at the user level, you can add something like "@hourly anacron -t /path/to/anacrontab -S /path/to/spooldir" to the user's crontab, though I've never tried this.
Many cron implementations have a similar mechanism.
Cronie doesn't have a `@reboot` meta-trigger?
That is not a fair summarization of their point because that is not the grammar. There's commas, slashes, asterisks, combinations, and then if you want randomization you need to put it in the command itself because cron can't do it. (Some crons can, but it's not a general capability of cron.) Writing a non-trivial cron spec is not easy.
That does require you to still know what the default environment is, but it is a mostly completely clean environment, without any influence from any shell.
I'd have to concur that I agree this is an advantage of systemd.
When someone inputs something ridiculous like "5,3/4 4-8,11 1 4,5,6,9-11 */2" you get to enjoy the fun of reverse engineering what they meant (it's never what they actually wrote).
And that's before you get to all the extensions supported in some cron environments (but not all).
I find systemd timers a lot more manageable. Things like having control over whether or not long-running jobs are allowed to overlap and the ability to run tasks between start-finish rather than a fixed time window are major improvements for me. At some point my VPS went down because the backup job ran into some kind of symlink loop and cron just kept spawning more and more backup tasks even though none of them finished.
Having to re-write commands and scripts because CRON had its own special PATH was also a pain point, but the same can be true for some types of systemd timers. But: you can execute those timers manually if you want instead of updating the crontab to trigger in 30 seconds and simply waiting.
That's true, but most people don't know the numbered manual sections, so they get the docs for the cron table command not the cron table config file.
If nothing else, maybe it could be some kid's high school science fair project idea.
I am not the biggest fan of systemd, but today I will always reach for a systemd timer over cron simply due to the sheer amount of bad experiences I've had with cron. Hours upon hours wasted trying to troubleshoot crons that weren't working due to some stupid obscure issue, having to use dirty hacks to monitor for success or retry failed jobs.
A few years ago I was trying to run a very simple bash script with cron and the script just died halfway through for no reason. Nothing in logs, worked fine when run directly, but in cron it just stopped halfway through a loop. Never figured out the cause, just gave up and used a timer instead, which worked fine. Never touched cron again after that.
The ease and convenience of monitoring and troubleshooting alone are worth switching over.
I have done scheme all my life, which is why I prefer shepherd. Not only is it in a syntax that i can use elsewhere, I get completion in Emacs.
But that's because I'm old because obviously systemd-* is the only right way and everyone else who see things differently is a pundit.
Come on, dude. That's unnecessarily polemic.
cron et al have served us for decades, yes. But that doesn't mean that cron is the solution that needs to accompany us until the heat death of the universe or year 2038, whatever comes first.
I agree, the systemd folks haven't exactly been the best when it comes to PR or when it comes to being even near feature parity with what they tried to replace. But now, they aren't just at feature parity, they surpassed plain old cron.
Maybe it is time to lay cron to rest, at least slowly.
systemd.services.sync-recyclarr = {
serviceConfig.Type = "oneshot";
path = [ pkgs.podman ];
script = ''
podman exec -it recyclarr recyclarr sync radarr
podman exec -it recyclarr recyclarr sync sonarr
'';
};
systemd.timers.sync-recyclarr = {
timerConfig = {
OnCalendar = "daily";
Persistent = true;
Unit = "sync-recyclarr.service";
};
partOf = [ "sync-recyclarr.service" ];
requires = [ "podman-recyclarr.service" ];
wantedBy = [ "timers.target" ];
};Odd. This script
#!/bin/bash
set > /tmp/set.txt
when scheduled like so * * * * * $HOME/bin/testCronScript.sh
Produces this file in /tmp/set.txt which has had a handful of values (HOME, UID, etc) lightly redacted prior to posting here -to remove PII or for length- but its keys are entirely untouched: BASH=/bin/bash
BASHOPTS=<redacted because long>
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=([0]="0")
BASH_LOADABLES_PATH=/usr/local/lib64/bash:/usr/lib64/bash
BASH_SOURCE=([0]="/home/user/bin/testCronScript.sh")
BASH_VERSINFO=<redacted bash 5.3.x>
BASH_VERSION=<redacted bash 5.3.x>
DIRSTACK=()
EUID=13370
GROUPS=()
HOME=/home/user
HOSTNAME=hostname
HOSTTYPE=x86_64
IFS=$' \t\n'
LANG=en_US.utf8
LOGNAME=user
MACHTYPE=x86_64-pc-linux-gnu
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/bin:/bin:/usr/sbin:/sbin
PPID=1337
PS4='+ '
PWD=/home/user
SHELL=/bin/sh
SHELLOPTS=braceexpand:hashall:interactive-comments
SHLVL=1
TERM=dumb
UID=13370
USER=user
_=/home/user/bin/testCronScript.sh
Seems pretty clean to me. Even when I run this via /etc/crontab, rather than as a user cron job: * * * * * root /home/user/bin/testCronScript.sh
I get effectively the same results.Maybe your distro's default cron environment was bad, and you never bothered to check and unset the badness? I'd be surprised if they were unable to make the default environment for Timer Units to be bad.
# Run if at least a day has passed since the last run
# and it isn't the weekend.
def should_run(finished, timestamp, dow, **_):
return dow not in [0, 6] and timestamp - finished >= one_day
This was inspired by GNU mcron. In mcron, jobs can calculate the next time they should run using Guile (https://www.gnu.org/software/mcron/manual/mcron.html#Guile-S...): (job
'(next-minute-from
(next-hour (range 0 24 2))
'(15))
"my-program")
I found mcron's scheduling counterintuitive and decided I wanted a function that returned a boolean. I can tentatively recommend it.Is your example (which I agree, looks cryptic) any less cryptic in systemd?
I asked jippity, and it said this:
[Timer]
OnCalendar=*-04,05,06,09,10,11-01 04..08,11:03/4,05:00
OnCalendar=Sun,Tue,Thu,Sat *-04,05,06,09,10,11-* 04..08,11:03/4,05:00
To which I have to go: "what?"> Things like having control over whether or not long-running jobs are allowed to overlap
With cron that's just prefixing the command with `flock -n <lock>`, but sure the "pick somewhere to put the lock" is probably better with systemd.
> Having to re-write commands and scripts because CRON had its own special PATH
Why? Wouldn't you just put that in the crontab? I don't even see this as different. It's in the cron config or the systemd timer config.
The other improvements you mentioned seem good.
What's so hard about "At 5 minutes past the hour and every 4 minutes, starting at 3 minutes past the hour, at 04:00 AM through 08:59 AM and 11:00 AM, on day 1 of the month, every 2 days of the week, only in April, May, June, and September through November"?
(I used https://crontab.cronhub.io/ to decode it, to be fair)
This isn't the same as with systemd timer because timer lets you specify when you want to run your service exactly and will fallback to running when the system comes online. With @hourly I lose this control and multiple machines could potentially trigger backups at the same time, hogging the physical hard drives and the network.
Not an option either, because if I reboot two machines and the backup starts in both of them it'll cripple my NAS
This is generally my only real complaint about systemd. I don't care if it is too monolitic, written in C or whatever, I just want a straightforward syntax for straightforward operations. I'd like it if systemd could recognize if a .target file is a shell script and just do "the right thing". Perhaps it would make sense for a timer file to recognize cron syntax as well. Or at least allow for a kind of extensibility so that I can have it supported.
If systemd had a little more respect for existing conventions, I am pretty sure it wouldn't be so controversial. After all, system administrators like it because they use it all the time, but a regular, full-timer user like me, who only deals with it when something is broken or have to use it as a means-to-an-end to set something up, then all friction is annoying and bad UX. (And no, using Nix is not the solution)
No `man man`? ;)
I have an ink jet printer that I like. I don't print very often (average a couple pages per week) but when I do it's a mix of documents and photos. The ink isn't cheap, but the quality seems good and for the amount I print the expense is minor.
Oh but it won't appear in the timer-specific logs, I guess...
I think it's... easier? Like "systemd is the place where your system manages all the processes it needs to run. Part of those processes can be run on a schedule, or on a timer, and you define them using this simple text file".
If I never recall hacking in ulimit calls in the top of buggy shell scripts for crappy old services that done respect pam_limits it wonβt be soon enough.
Over all I think Systemd get way to much criticism. You don't have to use all the parts, but if you care to go through the documentation you'll find interesting features such as journald log-shipping and systemd-machined which can manage containers and VMs.
It is also easier to debug as every job gets its own log rather than trying to write to system mailer nobody had set up with the job errors
Yeah I agree.
> systemd folks haven't exactly been the best when it comes to PR
It's deeper than that. Systemd folks are enemies of Linux. First, it's "fuck your opinion, do as we say" attitude which makes me want to throw away everything that comes from that poisonous well. Second, it's the embrace and extinguish strategy employed by the systemd project. And third, systemd author is up to no good: https://news.ycombinator.com/item?id=46784572
And I printed a lot of photos, notes, documents, etc
Once you learn that env in cron is not same as in your shell and once you learn to redirect output to loggers - it works just fine.
It would be a lie to say that I never debugged cron and sure it's annoying.
> and the script just died halfway through for no reason
Unrelated to cron. Bad script.
The problem I have found is that nixos doesn't seem to pickup and run systemd timers and services placed into the ~/.config/systems/user folder and additionally things like WantedBy=default.target have no effect.
So after I restart all my services manually on reboot I agree, systems timers are cool.
CPU speeds have increased & and i/o latency has decreased so much since then that startup times are generally imperceptible, so the pendulum has swung back to favouring socket activation.
The anti-systemd "traditionalists" never seem to acknowledge that history, though!
OnCalendar=00/6
You can test it with: systemd-analyze calendar --iterations=6 '0/6:00:00'
The format is `DayOfWeek Year-Month-Day Hour:Minute:Second`https://www.freedesktop.org/software/systemd/man/latest/syst...
$ sudo systemctl edit --force --full my-scheduled-work@.timer
or $ systemctl edit --user --force --full my-scheduled-work@.timerThat isn't something I'd want to happen, it sounds like it creates a potential queue of scripts that will flood the system on start, if it works the way you described.
I prefer the deterministic behavior of cron, the script will run when it is specified to run, as you said earlier, as long as the system is running; and as I stated in a separate comment, it will run @reboot if I need it to run then.
> With @hourly I lose this control and multiple machines could potentially trigger backups at the same time
Then don't use @hourly, use staggered times, it's very easy.
$ systemctl cat public-inbox-watch@.timer
# /etc/systemd/system/public-inbox-watch@.timer
[Unit]
Description=Periodic fetch of public mailing list
[Timer]
# twice a day
OnCalendar=*-*-* 5,17:35
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=multi-user.targetIn cron, you basically have to either use your configuration management to generate those times, or have a random delay script running before the command
In systemd timers, it's just
OnCalendar=0/6:00:00
RandomizedOffsetSec=60m
and the offset generated will be stable for the job on a given machine (i.e. always same on this machine but different on others) so you will get nice uniform distribution of load.If you add
Persist=true
the job will also be run once if there was one or more scheduled runs when the machine was down Mon,Fri *-01/2-01,03 *:30:45
Who'd ever want to go back crontab format for nontrivial scheduling? [1][0] <https://www.freedesktop.org/software/systemd/man/latest/syst...>
[1] This question is sarcasm. SystemD is often like this... dead simple things look dead simple, but complex things are -if they're possible at all- at least as complex as they are everywhere else.
> Unrelated to cron. Bad script
Again, worked fine when run manually, worked fine in a systemd timer. Pretty sure I still have it running today and it continues to work fine without ever failing.
We are all guilty of making bad scripts, bash is a disgusting degenerate language (and I love it). The way we learn to write good scripts is by writing bad scripts in enough amounts to get bitten by all the warts.
One thing I really love about cron, is that if you set up mail on the server (which: you should btw), then cron actually sends emails if it sees anything in stdout and stderr.
I am a dyed in the wool systemd non-believer, but I really do like the timers.
a) It is way nicer and you get decent validation at build time
b) A LLM can port units over if the need arises; itβs a very light abstraction around systemd syntax
c) I personally donβt see how I would ever move to another distro :)
Could have been XML Property Lists.
ducks
1) It's supported by cronie. I bet it's supported by many other crons.
2) "Great" news! The software in the Systemd Project only officially runs on Linux, so "it's not portable" is a really bad counterargument when "alternatives to some Systemd Project feature" is the discussion topic.
It would also make it much simpler to make good GUI editors for the files instead of the Notepad approach most unix config files take.
[Service]
Type=oneshot
WorkingDirectory={{ home }}/current/
Environment=RAILS_ENV=production
ExecStart=/bin/sh -lc "bin/db-backup --verbose"
which fills me with sadnessthere's also `systemctl --all list-timers` to view them.
There are two options to fix it;
Disable persist so no catching up on missing scripts. Set OnBoot=5m so it gets ran 5 minutes after boot, so your script (say backup) is ran on boot first, then every time on schedule
Enable persist but just add sleep in ExecStartPre - very "cron" way but there is just no in-systemd option to enable "catch up" script to be delayed
Sadly no option to "run catch-up timers with delay" at least yet
> Then don't use @hourly, use staggered times, it's very easy.
Not in cron. In systemd it's just RandomizedOffsetSec=30m and it is "stable" - same host with same job will always have same delay so on multiple hosts it is spread nicely. There is also non-stable version
Just use Cron. It does one thing and does it well.
Nope. From crontab(5)
The RANDOM_DELAY variable allows delaying job startups by random amount
of minutes with upper limit specified by the variable. The random scalβ
ing factor is determined during the cron daemon startup so it remains
constant for the whole run time of the daemon.
That's from my cronie install, but it looks like this has been a feature of some crons for at least a decade. (Notice that the post date of [0] is in 2016.) Given that cronie is based on vixie-cron, and I think I was was using vixie-cron in 2002, I bet it's been a thing for at least twenty years.This allows them to work well even if years go by between prints. It's a very thoughtful design element.
(They don't survive sitting for months and months unpowered on a shelf very well, but... you'll have that.)
This isn't what happens. If you leave it offline for days it'll only trigger the service only a single time.
https://www.freedesktop.org/software/systemd/man/latest/syst...
XML is that wonderful format that gave us vulnerabilities like death by million laughs, up to a certain moment, you could MitM DTDs, and a whole slew of everything-XML stuff back when XML was like AI is today, none of which I miss today.
Oh, and remember times when programmers would argue whether argument order in XML files should be significant or not?
But XML books with their idealized XML future description did give me the same warm fuzzies as some intricate clockwork mechanism to a Victorian geek.
[Service]
Type = oneshot
WorkingDirectory = %h/current/
Environment = RAILS_ENV=production
ExecStart = /bin/sh -lc "bin/db-backup --verbose"100 jobs all running at different times throughout the week is a very different load than them all falling back and running at the same time on system boot.
Looking at the other examples on that page, I'm gonna say that it's only arguably easier to read for basic stuff... especially if you're familiar with the syntax. The complex stuff is -at best- just as difficult.
Environment = MULTIPLE=environment VARIABLES="in single line"I find myself doing this sort of thing all the time..