I noticed for a while that although I could run the commands in my crontab just fine, they would not run from cron itself; when I looked at magento.cron.log, I found lines like this:
Directory "var/locks/" cannot be created Warning!mkdir(): Permission denied
I noticed that var/locks in my Magento installation directory had all of the correct permissions, and it was full of lock-files, but just from when I would manually run the cron jobs; also, ~/var was mostly locked down in my shared-hosting setup, and the only sub-directories I could see are the per-site directories (I can’t even enumerate sub-directories of ~/var, only guess at what the names are, and I can tell that in particular there is no ~/var/locks).
Prepending each Magento cron job with a command to switch to the Magento installation directory (using an absolute path) worked; for example, if my username were user and Magento were installed in ~/magento, then the relevant command would change from
* * * * * php /home/user/magento/bin/magento cron:run 2>&1 | grep -v "Ran jobs by schedule" >> /home/user/magento/var/log/magento.cron.log
to
* * * * * cd /home/user/magento && php /home/user/magento/bin/magento cron:run 2>&1 | grep -v "Ran jobs by schedule" >> /home/user/magento/var/log/magento.cron.log
(Here, the correct php executable was in my $PATH, which I had explicitly set near the top of my crontab.)
Why does Magento try to write lock-files to ~/var/locks rather than var/locks under its own installation directory? I’m currently running Magento 2.3.6-p1.