Why isn't my crontab working?

I can’t get cron to work on my Fedora Silverblue 35. As far as I can tell, it’s installed:

> systemctl status crond.service
● crond.service - Command Scheduler
     Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor pre>
     Active: active (running) since Fri 2022-02-18 03:52:03 CET; 34s ago
   Main PID: 1069 (crond)
      Tasks: 1 (limit: 77007)
     Memory: 1.1M
        CPU: 5ms
     CGroup: /system.slice/crond.service
             └─1069 /usr/sbin/crond -n

Feb 18 03:52:03 fedora systemd[1]: Started Command Scheduler.
Feb 18 03:52:03 fedora crond[1069]: (CRON) STARTUP (1.5.7)
Feb 18 03:52:03 fedora crond[1069]: (CRON) INFO (Syslog will be used instead of>
Feb 18 03:52:03 fedora crond[1069]: (CRON) INFO (RANDOM_DELAY will be scaled wi>
Feb 18 03:52:03 fedora crond[1069]: (CRON) INFO (running with inotify support)

Here’s my test crontab:

> crontab -l
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
* * * * * danut touch /var/home/danut/x

As far as I understand this should run every minute and create a test file in my home dir, but nothing happens and there’s nothing in the logs (the output of systemctl above shows the whole logs).

What am I missing?

If you had the correct path it should work.
Your user’s home directory is likely /home/danut and the command should be touch /home/danut/x .
It will not create a new file each time it runs but rather only touches the existing file after the first operation creates it. Each following touch only changes the access time.

Note that I am on fedora workstation and not silverblue so my thoughts about the path may be wrong, but to me anything under /var is a system file/directory and cannot be written to by a standard user. To verify your home directory a simple echo $HOME should give you that.

The touch command is a bad example as @computersavvy mentioned it just changes the time-stamp.

I made an example who works, alias where you see the time the command got executed.

I found this article, I don’t know if you saw it ? > Scheduling tasks with cron - Fedora Magazine

The command I use is: date >> /home/username/datefile (i don’t use silverblue).

Crontab command for every minute is:

*/1 * * * *  date >> /home/username/datefile

Now you can check with
cat /home/username/datefile
to see that every minute it gets a new timestamp there.

Bonus link (to customize the date):

For the records, on Fedora Silverblue, the home directories actually reside in /var/home and /home is a symlink to such directory.

Does this work?:
* * * * * danut /usr/bin/touch /var/home/danut/x
or
* * * * * danut /bin/touch /var/home/danut/x

You may need to figure out your path to the touch command, like
which touch

While writing a reply, I found the solution, so let me start with that. It seems that when editing a user-specific crontab (running crontab -e without sudo), one must not specify the username. I mean, that kinda makes sense, but the Fedora docs say (emphasis mine):

To create a crontab as a specific user, login as that user and type the command crontab -e to edit the user’s crontab with the editor specified in the VISUAL or EDITOR environment variable. The file uses the same format as /etc/crontab.

Which is kinda wrong, important detail omitted :slight_smile:. I guess the man page does mention this, subtly (again, emphasis mine):

The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions. Each line has five time and date fields, followed by a user name if this is the system crontab file, followed by a command.

@computersavvy: yeah, /var/home/ is a Silverblue thing :slight_smile:

@ilikelinux: you don’t have to /1, in fact the article you linked even says:

This cron job will run every minute, all the time:
* * * * * [command]

This would have been so trivial to debug if cron would just log somewhere what command it’s trying to run. Like, “Hey, it’s 3:24, I’m running ‘danut touch foo’” and you’d be like duh.

Anyway, thanks for the help, all!

On my fedora system, and I think most other distros as well, cron does create a log file. /var/log/cron

This entry is out of that log, for a job that was created using sudo crontab -e root (thus in the root users crontab and not in the system crontab)

As you can see it gives the date, time, user, and command run. It also gives both the start and end entries.

Feb 14 02:17:01 eagle CROND[3116829]: (root) CMD (cat /home/user/dnf/update.log >> /home/user/dnf/update.log.history && /usr/bin/date > /home/user/dnf/update.log && cat /home/user/dnf/update.err >> /home/user/dnf/update.err.history && /usr/bin/date  > /home/user/dnf/update.err && dnf update --refresh --exclude=dnfdragora  --exclude=dnfdragora-updater -y >> /home/user/dnf/update.log 2>> /home/user/dnf/update.err && /usr/bin/date >> /home/user/dnf/update.log && /usr/bin/date >> /home/user/dnf/update.err)
Feb 14 02:19:00 eagle CROND[3116802]: (root) CMDEND (cat /home/user/dnf/update.log >> /home/user/dnf/update.log.history && /usr/bin/date > /home/user/dnf/update.log && cat /home/user/dnf/update.err >> /home/user/dnf/update.err.history && /usr/bin/date  > /home/user/dnf/update.err && dnf update --refresh --exclude=dnfdragora  --exclude=dnfdragora-updater -y >> /home/user/dnf/update.log 2>> /home/user/dnf/update.err && /usr/bin/date >> /home/user/dnf/update.log && /usr/bin/date >> /home/user/dnf/update.err)

Interesting! Mine logs to the systemlog, and I can read it with journalctl -u crond.service -b, and it logs some things, like when it’s running anacron, apparently, but not my crontab commands:

Feb 19 02:29:38 fedora systemd[1]: Started Command Scheduler.
Feb 19 02:29:38 fedora crond[1057]: (CRON) STARTUP (1.5.7)
Feb 19 02:29:38 fedora crond[1057]: (CRON) INFO (Syslog will be used instead of>
Feb 19 02:29:38 fedora crond[1057]: (CRON) INFO (RANDOM_DELAY will be scaled wi>
Feb 19 02:29:38 fedora crond[1057]: (CRON) INFO (running with inotify support)
Feb 19 02:33:01 fedora crond[1057]: (danut) RELOAD (/var/spool/cron/danut)

# <-- this is where I'd expect to see my commands, they were running
# every minute, but there's no messages here.

Feb 19 02:53:01 fedora crond[1057]: (danut) RELOAD (/var/spool/cron/danut)
Feb 19 03:01:01 fedora CROND[8449]: (root) CMD (run-parts /etc/cron.hourly)
Feb 19 03:01:01 fedora anacron[8460]: Anacron started on 2022-02-19
Feb 19 03:01:01 fedora anacron[8460]: Will run job `cron.daily' in 18 min.
Feb 19 03:01:01 fedora anacron[8460]: Jobs will be executed sequentially
Feb 19 03:01:01 fedora run-parts[8462]: (/etc/cron.hourly) finished 0anacron
Feb 19 03:01:01 fedora CROND[8448]: (root) CMDEND (run-parts /etc/cron.hourly)
Feb 19 03:02:01 fedora crond[1057]: (danut) RELOAD (/var/spool/cron/danut)
Feb 19 03:19:01 fedora anacron[8460]: Job `cron.daily' started
Feb 19 03:19:01 fedora anacron[8460]: Job `cron.daily' terminated
Feb 19 03:19:01 fedora anacron[8460]: Normal exit (1 job run)
Feb 19 04:01:01 fedora CROND[16982]: (root) CMD (run-parts /etc/cron.hourly)

Perhaps there’s an option for it?

Yep, you are right, doesn’t work as described.

The man page says that you can write a line like this
* * * * * user command
if it would be the system crontab.

Not sure what system crontab means. For user root adding the user name doesn’t seem to work.

You could define a user action using “su” like this in the root crontab:
* * * * * su - danut -c "/bin/touch /tmp/danut"

However, just using the users crontab instead of root’s seems to be the better solution. :wink:

The system crontab is the file at /etc/crontab.

Additionally some users are allowed to have a personal crontab which is created/edited by the user with “crontab -e” and which can be edited by the root user (or when using sudo) with “crontab -e <user>”. The system crontab file is edited by someone with root privileges with a standard text editor.

The personal crontab file does not use the user name in the line because cron already processes that file as that user. The system file, OTOH, must have the user name because cron has to be told which user to run as when it processes that command.

That probably would not work since the su command would require an input of the root password. Also, since all users are allowed to write into /tmp su would not be needed.

When danut is logged in, using the command crontab -e would open the personal crontab for that user where they could then enter the line * * * * * /bin/date >> /tmp/danut . Saving that file would then tell cron to once every minute add an entry to the file /tmp/danut with the then current date info. After saving that crontab entry the command tail -f /tmp/danut would display the content of that file and the user would see the updates every time it was updated. The same results would be seen by adding this line in the system (/etc/crontab) crontab file. * * * * * danut /bin/date >> /tmp/danut.

Note that entering a command in a crontab file runs that command under the /bin/sh shell so the syntax must be compatible or it will fail. Commands in the crontab are also treated as scripts so the user should always use full paths to commands and to output files.

The system crontab is the file at /etc/crontab.

Ah, thanks, this clears things for me.

That probably would not work since the su command would require an input of the root password. Also, since all users are allowed to write into /tmp su would not be needed.

If you use the root-users crontab, eg. by executing sudo crontab -e, then su works without password like this:
* * * * * su - danut -c "/bin/touch /tmp/danut"

Edit
Root doesn’t need a password for the su command.
Root can do everything, read everything and write everywhere.
With su root switches into the role of the user and can do everything what the user can do.