Skip to main content

Diagnose cron jobs that work only in an interactive shell

A command that succeeds in your SSH shell can fail under cron because the account, environment and working directory differ. This guide covers traditional cron on Debian and Ubuntu. Confirm that the task is actually a cron job; systemd timers and panel schedules have different configuration and logging paths.

Find the correct schedule and account

crontab -l
sudo systemctl status cron --no-pager
sudo journalctl -u cron --since "2 hours ago" --no-pager

The first command shows the current user's crontab. An administrator may inspect a named account with the appropriate crontab user option. Avoid editing another user's schedule merely because your own list is empty. System crontab files include an account field that per-user crontabs do not; confusing the formats breaks the command.

Save the existing crontab privately before editing it. Schedules can contain paths and sensitive arguments. Record the server timezone and the expected execution time, especially when daylight-saving changes or a recent timezone adjustment could explain the observation.

Make dependencies explicit

Cron does not normally load your interactive shell profile. Use absolute paths for the script and important executables, and set a deliberate working directory inside a wrapper script. Check that the shebang selects the intended interpreter and that the account can traverse every parent directory.

Do not depend on an activated virtual environment, shell alias or manually exported variable. Refer to the application environment through its supported secure configuration method. Avoid putting passwords in the crontab. A successful command run as root does not prove it will work under the scheduled user.

Review cron's special syntax. For example, an unescaped percent sign in a command has meaning beyond an ordinary shell argument. Move complex logic into a reviewed script rather than growing a fragile one-line schedule. Consult the installed crontab manual because implementations differ.

Capture useful output privately

Arrange stdout and stderr logging to a directory writable only by the intended account and administrators. Logs may contain personal data or application secrets, so do not send them to a publicly served folder. Set retention so repeated failures cannot fill the disk.

Check the exit status and a concrete success marker, such as a completed report with the expected date. A cron log showing that a command started does not prove it finished. If the script emails results, verify mail delivery separately rather than treating an empty inbox as the only failure signal.

Prevent overlapping runs

Determine whether a previous instance is still active before retrying. Slow backups or imports can overlap the next scheduled time and contend for the same files. Use the application's supported locking or a reviewed single-instance wrapper when appropriate. Do not solve an overlap by killing every matching process.

Validate and recover

Test the wrapper under the intended account with the same explicit configuration. Ensure a manual test will not duplicate payments, notifications or destructive maintenance. Then observe one scheduled execution and inspect its result. If the edit fails, restore the saved schedule and retain the private failure log for diagnosis. Changing the task to root should require a real privilege need, not serve as a shortcut around missing paths or permissions.

Investigate systemd services. Check Linux time and NTP.

Reference: Official documentation. Check the documentation installed on your own operating-system release when an option differs.