systemd timers instead of cron, and why the logs are better

A cron job that fails emails root, which goes nowhere on a modern server, so the standard arrangement is a job whose failures are invisible.

# /etc/systemd/system/reports.service
[Service]
Type=oneshot
ExecStart=/usr/bin/php /var/www/app/artisan reports:generate
User=www-data

# /etc/systemd/system/reports.timer
[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=300
Persistent=true

[Install]
WantedBy=timers.target

Persistent=true runs a missed job after a reboot, which cron cannot do at all. RandomizedDelaySec stops forty servers hitting the same API at exactly 02:00. The real gain is that output goes to the journal with a unit name attached, so journalctl -u reports answers what happened last night, and systemctl list-timers shows when everything next runs — two questions that a crontab genuinely cannot answer.