6 ms·
I haven't used systemd timers enough to disagree, but > Ambiguous $PATH settings make cron script execution difficult to predict. What makes you say that? You
by thomashabets2 4mo ago
I haven't used systemd timers enough to disagree, but
> 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.
- jchw 4mo agoThe main nice thing about the environment in systemd is that it is standard and mostly a blank slate, whereas at least for me I was always getting bit by the fact that the environment in Crontab was completely different from say, the environment inherited by supervisord or sysvinit scripts. In systemd the actual unit that gets executed is the same regardless of what triggers it, so there is no gap. 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.
- skydhash 4mo agoI use cron in OpenBSD and it's a deterministic environment and mostly clean[0]. I like that instead of having other subsystems creep in. [0]: https://man.openbsd.org/crontab.5#ENVIRONMENT https://man.openbsd.org/crontab.5#ENVIRONMENT
- simoncion 4mo ago> 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. 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.
- jchw 4mo agoRegardless of exactly how clean the environment is, my favorite part of systemd is the fact that there is only one regardless of how something was triggered. Whether a unit is triggered via a mount unit, timer unit, udev rule, it's the same units at the end, so it's the same environment. The same problems that could be caused by a polluted environment in cron can be caused in reverse by a polluted environment elsewhere, when you unwittingly copy a command that depends on some environment being set. If you are using systemd as the service manager, this necessarily doesn't happen because it's all units. (Well, you could still copy something from outside of systemd and run into a similar problem, but at least there's essentially only one set of caveats you have to learn for whatever thing you want executed in the background.) So I guess this isn't so much cron vs systemd timers, but more cron + other init and service supervisors vs systemd init in general.
- simoncion 4mo ago> Regardless of exactly how clean the environment is, my favorite part of systemd is the fact that there is only one regardless of how something was triggered. Whether a unit is triggered via a mount unit, timer unit, udev rule, it's the same units at the end, so it's the same environment. > > The same problems that could be caused by a polluted environment in cron can be caused in reverse by a polluted environment elsewhere, when you unwittingly copy a command that depends on some environment being set. I'm confused about what you need this for? Are you running some utility command that needs the same environment provided by the daemon's service file? If so, any competent init system lets you extend upstream-provided service files. In OpenRC: # tail -n 1000 /etc/*/test-service ==> /etc/conf.d/test-service <== extra_commands="${extra_commands} maintenance" OTHER_THING="overriden other-thing" maintenance () { ebegin "doing maintenance. IV='$INIT_VAR' OT='$OTHER_THING'" set > /tmp/maintenance-set.txt eend 0 } ==> /etc/init.d/test-service <== #!/sbin/openrc-run name="test-service daemon" command=/usr/bin/socat command_user=nobody:nobody command_args="UDP-RECVFROM:6666,fork SYSTEM:'/bin/true'" supervisor=supervise-daemon extra_commands="rebuild" INIT_VAR=${INIT_VAR:-"init var"} OTHER_THING=${OTHER_THING:-"stock other-thing"} depend() { use net } start_pre() { set > /tmp/start-set.txt } rebuild () { ebegin "doing rebuild. IV='$INIT_VAR' OT='$OTHER_THING'" eend 0 } # /etc/init.d/test-service start test-service | * Starting test-service daemon ... [ ok ] # /etc/init.d/test-service maintenance test-service | * doing maintenance. IV='init var' OT='overriden other-thing' ... [ ok ] # /etc/init.d/test-service rebuild test-service | * doing rebuild. IV='init var' OT='overriden other-thing' ... [ ok ] # pgrep --list-full socat 133705 /usr/bin/socat UDP-RECVFROM:6666,fork SYSTEM:/bin/true The environment when the service is starting is effectively identical to the one when our custom function is being called: # diff -u0 /tmp/*-set.txt --- /tmp/maintenance-set.txt 2026-06-02 23:53:19.703048431 -0700 +++ /tmp/start-set.txt 2026-06-02 23:53:15.265094855 -0700 @@ -9 +9 @@ -BASH_LINENO=([0]="410" [1]="0") +BASH_LINENO=([0]="409" [1]="0") @@ -11 +11 @@ -BASH_SOURCE=([0]="/etc/init.d/../conf.d/test-service" [1]="/usr/libexec/rc/sh/openrc-run.sh") +BASH_SOURCE=([0]="/etc/init.d/test-service" [1]="/usr/libexec/rc/sh/openrc-run.sh") @@ -30 +30 @@ -FUNCNAME=([0]="maintenance" [1]="main") +FUNCNAME=([0]="start_pre" [1]="main") @@ -64 +64 @@ -PPID=133712 +PPID=133702 @@ -69 +69 @@ -RC_CMD=maintenance +RC_CMD=start @@ -73 +73 @@ -RC_OPENRC_PID=133710 +RC_OPENRC_PID=133700 @@ -75 +75 @@ -RC_RUNSCRIPT_PID=133711 +RC_RUNSCRIPT_PID=133701 @@ -93 +93 @@ -_='doing maintenance. IV='\''init var'\'' OT='\''overriden other-thing'\''' +_=']' So, if you need to do maintenance for a service on a schedule in the same environment that is provided for starting that service, you can simply extend the service script and use cron to execute that functionality. But. Another thing that confuses me is why you think that SystemD [0] provides anything special here? If you were to create a service file in most any other service manager and start it with cron, you'd get exactly the same environment sanitization as you get for all other services. Given your testimony, I expect that prior to SystemD, you'd have refused to create service files for things like one-off jobs that weren't system services... so why are you okay with it now that you're using SystemD? [0] I spell it "SystemD" not to mock it -as I understand some do- but to distinguish The Systemd Project from systemd(1). It sucks minor ass that the two share the same name, but what can you do?
- jerf 4mo ago"You just put numbers aligned with the titles." 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.
- eqvinox 4mo agoHow do you express those things in a systemd timer? E.g. run something 4x per day, */6 in cron.
- iam-TJ 4mo ago$ 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.target
- eqvinox 4mo agoI, er, what? It's... the same as cron? I'm confused now. It's not exactly the same, I guess?
- PenguinCoder 4mo agoSo the `OnCalendar` stanza is the same as a Cron job; without the helpful comments of the ordering? And that is considered _easier_?? Just use Cron. It does one thing and does it well.
- paradox460 4mo agoThe systemd one supports timezones. You can say America/Denver and the like, or Etc/UTC
- 4mo ago
- jeroenhd 4mo agoHaving had to work on an application supposedly supporting cron expressions: the numbers are just the basic parts of the language. 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.
- ink_13 4mo ago> 5,3/4 4-8,11 1 4,5,6,9-11 */2 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/ https://crontab.cronhub.io/ to decode it, to be fair)
- networked 4mo agoComplex expressions are one of the things I don't like in cron. On Debian/Ubuntu servers, I just bite the bullet with systemd timers. On my workstation, I have a personal job scheduler that feels easier and more fun to tinker with. The scheduler uses Starlark functions instead. For example: # 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-Simple-Examples 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.
- egorfine 4mo ago> I've used Linux since 1994 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.
- sophacles 4mo agoTotal n00b here. My first linux install was pretty recently, in late 1996 or early 1997 (sometime that winter). I just don't get it. Like is the core sentiment "How dare they address obvious system shortcomings"? Is it "I learned once and how dare you think I'm capable of learning again"? Is it "I want others to suffer the way I did to learn job scheduling"? cron did a job, but had shortcomings. Systemd addresses many of those shortcomings. One day something else will come along and address the shortcomings of systemd, and no one will care about systemd nostolgia. This is how technology is supposed to work: making progress and fixing the shortcomings of the past generation. It's not a religion, we don't have to maintain the weird old ways from the 80's, your soul won't be saved by cron or corrupted by systemd.
- gh02t 4mo agoYeah, I certainly have my complaints about systemd but the parent's point is undermined by the fact that cron still works. If you prefer it, carry on I doubt seriously it's going anywhere. I still do sometimes.
- thomashabets2 4mo agoI am not agreeing with egorfine. Indeed, why not improve what we can improve? > cron did a job, but had shortcomings. Systemd addresses many of those shortcomings Right, but what are those "many" shortcomings? The article lists four, and fully half of them seem to be nonsense, per my comment. (time spec syntax appears to be equally complex in systemd timers, and I have no idea what they mean about PATH, as it seems equal too) The remaining two are fairly good points, kind of. Sending mail is a black hole until you look there, sure. Believing that emails get meaningfully delivered on a non-email server is very anachronistic. But isn't logging a black hole until you look there too? So from the article that leaves "Execution history is difficult to follow and interrogate", which I super agree with. I would argue it's not inherent to cron, and one could have written a small tool that allows following and interrogating. And… surely that goes for logging too? cron does log to syslog, right? Maybe there's some integration with other stuff that is better, that I'm not aware of? I'm not disputing that it's better. I'm sure it is. But where's that list? If it's literally only the `list-timers` command, then that's very underwhelming. Is it the randomness to scheduling? I've never needed it (I just spread them out over an hour by fixed start time), but sure that would have value in other cases where coordination is not possible. You could add it to cron, but not in a nice way. To me it seems like engineers do what engineers like to do: enjoy greenfield implementations. It's open source. Nobody's going to ding your quarterly performance evaluation for going off on a amusement coding session without a requirements doc. I know that I have written a lot of tooling for amusement and to work the way I want it to work. I certainly understand the engineering mind that would rather write something from scratch than understand the previous system. I don't mind learning new things, but this article seems to fawn over stuff you can do in systemd timers, where… yeah that was always an option (in cron). I don't have faith that the article writer actually knew cron before they trash it in favor of something else. On a tangent, I do agree with egorfine that Lennartware inherently has a disdain for users and their workloads (e.g. kills user processes inside a screen/tmux arbitrarily), and that audio on linux was set back 5-10 years just from the mere disastrously bad quality of the pulseaudio implementation. It makes sense that he works for Microsoft.
- nailer 4mo ago> But luckily it's pre-printed in the crontab as comments 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.
- skydhash 4mo ago> 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. No `man man`? ;)
- helterskelter 4mo agoMan Man: the man with the strength of two men.
- thomashabets2 4mo agoA man who was bitten by a radioactive man.
- PunchyHamster 4mo agoproblem with vars is that they apply to any subsequent entry in the file so you need to take that into consideration; the nice thing about timers is that all settings are self contained and not affected by previous entries. The standard /10 and similar cron expressions also have thundering herd problem when on bunch of servers, tho some variants like in Jenkins use variant H/10 (H standing for hash) where the thing is randomly shifted in time to not hit same minute on same server/job another 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.
- thomashabets2 4mo ago> 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. There will always be a default. Is systemd timer's default inherently correct for all users any more than cron's is? I'm just playing devil's advocate here, but why not just change cron, then? Good for the goose is good for the gander?
- mike_hock 4mo ago> What makes you say that? You can set the PATH right in the crontab. 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).
- thomashabets2 4mo ago> $PATH is one thing when cron runs and another when you try out the command yourself. Why would that be different with systemd timers? If my ~/.bashrc adds /opt/foo/bin, that's also not part of the systemd timer's PATH, right? But I guess you're saying the ability to trigger the systemd timer off-schedule is the difference? Yeah, it's annoying with cron to have to temporarily set the trigger two minutes into the future. :-P Not sure adding that feature justifies a complete rewrite, but certainly a nice addition. > due to logic bugs in systemd Yeah my main gripe with systemd and other Lennartware is the extremely low implementation quality, not necessarily the ideas. Though the idea of killing tmux/screen on logout is downright criminal. And the fd passing nonsense[1] for system services is clearly just the idea of a child that found a tool and is misusing it. [1] which is an awesome and underused feature (https://blog.habets.se/2025/10/The-strange-webserver-hot-potato-sending-file-descriptors.html https://blog.habets.se/2025/10/The-strange-webserver-hot-pot...), but completely misapplied by systemd.
- imtringued 4mo ago>But I guess you're saying the ability to trigger the systemd timer off-schedule is the difference? Yeah, it's annoying with cron to have to temporarily set the trigger two minutes into the future. :-P >Not sure adding that feature justifies a complete rewrite, but certainly a nice addition. If there is a feature that justifies using a completely different tool it's obviously this one.
- themafia 4mo ago> But luckily it's pre-printed in the crontab man 5 crontab Way easier.
- emmelaich 4mo ago> You can set the PATH right in the crontab. Yes, but people don't. I've had to debug other's crontabs many times over the last umpteen years.
- thomashabets2 4mo agoBut how is that different with systemd timers? Two environments that set the PATH differently won't have the same value set, either way. Is this about "yes, but cron doesn't let me trigger through its environment, and systemd timers do"?
- thayne 4mo agoI wouldn't say that the PATH is ambiguous, but cron does have some problems with PATH: - the default value is missing some values you would expect, like /use/local/bin and /usr/sbin for root. - on some distributions (for example Arch Linux) the man page doesn't even say what the default path is, or recommend setting it. - if you need to add something to the path for a single script, you either need to wrap it with a call to env, set it in a wrapper script, or set the path before the entry and reset it afterwards - you can't use ~ or $HOME in the path, you have to write out the full absolute path. Which is particularly annoying for user crontabs. Sure, it isn't too hard to work around those, but IMO systemd timers are a better experience, especially since the default uses the same path as all your other services.
- thomashabets2 4mo ago> - the default value is missing some values you would expect, like /use/local/bin and /usr/sbin for root. What do you mean by "you would expect", that doesn't also apply to systemd timers? /opt/foo/bin is not in the path. Would you expect that? And if this is an objective problem, can we just change the cron default PATH? > - on some distributions (for example Arch Linux) the man page doesn't even say what the default path is, or recommend setting it. Send a PR. This doesn't seem like an inherent problem. > - if you need to add something to the path for a single script, you either need to wrap it with a call to env, set it in a wrapper script, or set the path before the entry and reset it afterwards Or on the line, right? * * * * * FOO=bar $HOME/bin/foo.sh The line can get long, but is this really a problem? > - you can't use ~ or $HOME in the path, you have to write out the full absolute path. Which is particularly annoying for user crontabs. This is incorrect. You can definitely use $HOME in user crontabs. I'm still not seeing something that warrants a rewrite. (except what you did not mention, which is the ability to run "trigger this now" as a missing feature)
- thayne 4mo ago> What do you mean by "you would expect" I mean it should include things that are usually on the path, like /usr/local/bin. And for the root user it should include sbin. I don't really expect /opt/foo/bin to be there, but if you want to have it available by default everywhere then you can just add it to systemd once, rather than having to remember to add it to crontab as well. > Or on the line, right? Oh you're right. I forgot cron evaluates the command with the shell (which has its own set of issues...) > This is incorrect. You can definitely use $HOME in user crontabs. You can in the command itself, because that is evaluated by a shell, but not when setting an environment variable. From the Ubuntu crontab(5) manpage: > The value string is not parsed for environmental substitutions or replacement of variables or tilde(~) expansion Maybe it depends on the cron implementation though. The cronie documentation doesn't say either way.