Skip to content
Commitpedia
Log in Join & edit

cron and systemd timers: run something every night

Schedule a script with crontab or a systemd timer: the five cron fields, environment and PATH pitfalls, logging, catching up missed runs with Persistent=true, and how to check that it actually ran.

Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?

Edit history of cron and systemd timers: run something every night: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
3 min

You have a script that works when you run it by hand and you want it to run at 02:30 every night. Linux gives you two standard tools: cron, which is simple and everywhere, and systemd timers, which give you logging, dependencies and catch-up of missed runs for free. This guide sets up the same nightly job both ways.

Before you start

  • The script runs with no terminal and a nearly empty environment. Use absolute paths inside it and start it with set -euo pipefail.
  • Make it executable: chmod +x /usr/local/bin/nightly-report.sh.
  • Test it once from a clean shell: env -i HOME="$HOME" PATH=/usr/bin:/bin /usr/local/bin/nightly-report.sh. If that fails, cron will fail the same way.

Option 1: cron

Each user has a crontab. Edit yours with crontab -e (it opens in your editor) and list it with crontab -l. A line has five time fields followed by the command.

# min  hour  day-of-month  month  day-of-week   command
  30   2     *             *      *             /usr/local/bin/nightly-report.sh >> /var/log/nightly-report.log 2>&1
  • * means every value. */15 in the minute field means every 15 minutes. 1-5 in day-of-week means Monday to Friday. 0,30 lists values.
  • Shortcuts: @daily (midnight), @hourly, @weekly, @reboot.
  • Set [email protected] at the top to get any output by mail, or redirect output to a log file as above. A job that prints nothing and exits 0 is silent.
  • Set PATH=/usr/local/bin:/usr/bin:/bin at the top of the crontab; the default PATH in cron is minimal, which is why command not found is the most common cron failure.
  • The % character is special in crontab lines (it becomes a newline). Escape it as \%, for example in date +\%F.

System-wide jobs that must run as root go in /etc/cron.d/ as separate files with an extra user field before the command, or you simply drop an executable script into /etc/cron.daily/.

# /etc/cron.d/nightly-report
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=root
30 2 * * * root /usr/local/bin/nightly-report.sh >> /var/log/nightly-report.log 2>&1

Check that it ran: look at your log file, or at the cron daemon’s log with journalctl -u cron (Debian, Ubuntu) or journalctl -u crond (RHEL, Fedora), or grep CRON /var/log/syslog on older systems.

Option 2: a systemd timer

A timer is two small files: a .service that says what to run and a .timer that says when. Put them in /etc/systemd/system/.

# /etc/systemd/system/nightly-report.service
[Unit]
Description=Nightly report

[Service]
Type=oneshot
ExecStart=/usr/local/bin/nightly-report.sh
User=report
Nice=10
# /etc/systemd/system/nightly-report.timer
[Unit]
Description=Run nightly report at 02:30

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=5m

[Install]
WantedBy=timers.target

Check before you run this

  • Runs with administrator rights. Make sure you understand every line.
sudo systemctl daemon-reload
sudo systemctl enable --now nightly-report.timer

systemctl list-timers --all                 # next and last run of every timer
sudo systemctl start nightly-report.service  # run the job right now, by hand
journalctl -u nightly-report.service -n 50   # its output, no redirection needed
  • Persistent=true runs the job at boot if the machine was off when it was due. cron simply skips missed runs.
  • RandomizedDelaySec spreads jobs on many machines so they do not all hit a server at the same second.
  • Check a calendar expression before you rely on it: systemd-analyze calendar "Mon..Fri 02:30" prints the next run time.
  • Shortcuts such as OnCalendar=daily, weekly and hourly work too.

Per-user timers without root go in ~/.config/systemd/user/ and are managed with systemctl --user. They only run while you are logged in unless you enable lingering with loginctl enable-linger $USER.

Which one to pick

  • cron: a one-liner on a machine you do not own or that is not running systemd, or a quick personal job.
  • systemd timer: anything on a server you maintain. You get journalctl logs, catch-up of missed runs, resource limits and systemctl status for free.

Whichever you choose, make the script itself safe to be started twice and loud when it fails. The cron-safe wrapper script in this encyclopedia adds locking, logging and failure reporting around any command.

Official documentation

Did this work for you?

No votes yet. Be the first.

If it did not work, the report form below tells the editors what went wrong.

See also

Repos in the wild

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +4,293 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?