Skip to content
Shell & TerminalFix Published Updated 3 min readViews unavailable

Fixing Scripts That Work Interactively but Fail Under cron

cron starts jobs with a small, non-interactive environment — make paths, interpreters, working directories, and logging explicit rather than sourcing a login shell.

A script that succeeds in a terminal but fails from cron is usually depending on invisible interactive state: a broader PATH, the current directory, a shell alias, an unlocked credential helper, or configuration loaded by a login shell.

Reproduce the smaller environment

Start by logging what cron actually sees, without writing secrets into a world-readable file:

SHELL=/bin/sh
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=you@example.net

15 2 * * * /home/alex/bin/backup.sh

Inside the script, send diagnostic output to a protected log or the scheduler’s mail facility. Record id, pwd, a sanitized environment, and resolved executable paths. Do not assume cron starts in the project directory.

Use an absolute interpreter and paths

The shebang controls which interpreter runs when the script is executed directly:

#!/bin/sh
set -eu

cd /home/alex/app || exit 1
/usr/bin/find ./exports -type f -mtime +14 -delete

An executable discovered through a version manager in .bashrc may not exist in cron’s PATH. Point at a stable installed interpreter or create a small wrapper that activates only the specific runtime environment the job needs.

Do not source an entire interactive profile as a shortcut

Interactive startup files often print output, assume a terminal, launch agents, define aliases, or return early for non-interactive shells. Sourcing them makes an automated job fragile. Put shared, non-interactive variables in a dedicated file with restrictive permissions, and source that file explicitly.

Check non-environment differences

Cron jobs have no controlling terminal, so password prompts and sudo interaction fail. Desktop keychains may be locked. Relative redirections land in unexpected locations. Percent characters have special meaning in some crontab implementations unless escaped. Daylight-saving changes can skip or duplicate wall-clock times.

Once fixed, test the exact cron entry or run the command with a deliberately minimal environment such as env -i HOME="$HOME" PATH=/usr/bin:/bin .... Success in a richly configured terminal is not the acceptance test for unattended automation.

Why cron’s environment has always been this minimal

cron itself dates back to the mid-1970s at AT&T Bell Labs, formally shipping as part of Unix Version 7 in 1977 — its original design woke up once a minute, read a system-wide job table, and executed whatever was due, with no concept of an interactive login session at all baked into that model from the start. Every later cron implementation inherited that same fundamental assumption: a cron job runs detached from any terminal, any login shell, and any of the interactive setup a user’s normal session accumulates, which is exactly the design decision that makes today’s “works interactively, fails under cron” symptom a permanent, structural property of the tool rather than a bug someone forgot to fix.

Where cron actually logs what it did

On systemd-based Linux distributions, journalctl -u cron (or -u crond, depending on the distribution’s service name) shows cron’s own job-invocation log, separate from anything your script itself writes; older or non-systemd systems typically log to /var/log/cron or route cron’s messages through the general syslog facility instead. Checking this log — confirming cron actually attempted to run the job at the expected time at all — is a useful first step before assuming your script’s own internal logging is the whole picture, since a job that never launched (a syntax error in the crontab entry itself, for instance) produces no application-level log output whatsoever — the scheduler’s own log is the only place that specific failure mode ever shows up, which is exactly why it’s worth checking first, not last, whenever a scheduled cron job appears to have simply done nothing at all, silently, without producing any obvious error.

Related:

Sources:

Comments