Linux & Systems

When Cron Does Not Share Your Shell

Why a command can succeed interactively but fail under cron, with a small wrapper pattern that makes paths and output explicit.

2 min read
#cron#shell#automation#troubleshooting

A quiet road passing through a sunlit forest

Photo: Unsplash.

A command that works in an interactive terminal may fail in cron because cron starts with a smaller environment. It may not load your shell profile, language manager, aliases, or the same PATH.

First, use absolute paths in the crontab:

SHELL=/bin/sh
PATH=/usr/local/bin:/usr/bin:/bin

15 2 * * * /srv/myapp/bin/nightly-backup >> /var/log/myapp-backup.log 2>&1

Put multi-step logic in a version-controlled script rather than squeezing it into one cron line:

#!/bin/sh
set -eu

PATH=/usr/local/bin:/usr/bin:/bin
export PATH

cd /srv/myapp
/usr/bin/date -Is
/usr/bin/env
./bin/backup --config /etc/myapp/backup.conf

Make it executable and test it as the same user that owns the cron entry:

chmod 750 /srv/myapp/bin/nightly-backup
sudo -u deploy /srv/myapp/bin/nightly-backup

To compare environments temporarily, capture cron’s environment to a file with restrictive permissions, inspect it, and remove it afterward. Do not leave secrets in debug output.

Also check the mundane failures:

  • relative paths resolve from an unexpected working directory;
  • a command exists only in an interactive version manager;
  • the cron user cannot read a config file or write the destination;
  • output is mailed or discarded instead of reaching the expected log;
  • the server timezone differs from the schedule you had in mind.

The durable fix is not to make cron imitate your entire login shell. Give the job a small, explicit environment and make the wrapper independently testable.

Reference