A cron expression has five fields: minute, hour, day of month, month and day of week. They are compact and easy to misread, and a few rules, such as how the two day fields combine, surprise even experienced administrators.
The fields run minute (0-59), hour (0-23), day of month (1-31), month (1-12) and day of week (0-7, where both 0 and 7 are Sunday). An asterisk means every value, a comma separates a list, a dash is a range and a slash adds a step, so */15 in the minute field means 0, 15, 30 and 45.
# m h dom mon dow command
30 2 * * 1-5 /usr/local/bin/backupWhen both day fields are restricted, cron runs the job when either matches, not both. 0 0 13 * fri fires on every 13th and on every Friday, not only on Friday the 13th. If either day field starts with an asterisk, such as */2, the two are combined with AND instead. To get a true AND, schedule the job daily and test the weekday in the command.
0 0 13 * fri # the 13th OR any FridayA step restarts at the start of each larger unit. */7 in the minute field runs at 0, 7, 14 and so on up to 56, then at 0 again four minutes later. Choose steps that divide 60 for minutes and 24 for hours if you need even spacing.
*/7 * * * * # 56 then 0: a 4-minute gapCron runs in the server's time zone, so a job at 02:30 may be skipped or run twice around a daylight-saving change. A systemd timer expresses the same schedule as OnCalendar and can run in UTC. The two day fields cannot be combined with OR in a timer, so a schedule like the one above has no direct equivalent.
OnCalendar=Mon..Fri *-*-* 09:00:00Open the Cron expression generator
Day 31 only exists in seven months, so a schedule for the 31st skips the others, and day 30 and 29 skip February. For the last day of every month, a systemd timer can say it directly with OnCalendar=*-*~01, or in cron schedule daily and have the command check that tomorrow is the 1st.
It runs the job once each time the machine boots. Vixie cron and cronie support it, but not every cron implementation does, and a systemd timer with OnBootSec is the portable alternative.
Wrap the command in flock with a lock file, or use a systemd service, which never starts a second copy of a unit that is still running.