DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
VGSOURCES
Get listed

BashPATCH 2026.10.15

Why Your Cron Job Isn’t Running Your Shell Script (and How to Fix It)

By VGSources Team 10 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A script that works in a terminal can fail under cron because cron starts it non-interactively with a small, explicitly defined environment. Make the interpreter, PATH, working directory, script path, input paths, output paths and logging explicit. For a Bash script owned by alice, a dependable per-user entry is:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * /usr/bin/env bash /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1

Then verify that the correct crontab contains the entry, the cron or crond daemon is running, the job’s user has every required permission, and the log or journal shows what happened.

As an Amazon Associate I earn from qualifying purchases.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What changes when cron starts your script

A terminal session is interactive: it has a login shell, startup files, a familiar current directory, your personal PATH, loaded credentials and often additional application variables. Cron does not recreate that session. The daemon examines stored crontabs and checks each job for the current minute, then launches matching commands in a non-interactive context.

Unless you override it, the command interpreter is /bin/sh. The job normally receives a limited environment such as SHELL, PATH, USER, LOGNAME, HOME and LANG; values exported by interactive shell startup files are not a dependable source of configuration.

That difference explains the common symptom: typing ./backup.sh succeeds, while the identical-looking crontab entry silently does nothing. It may be using a different shell, resolving a relative path somewhere unexpected, lacking permission to reach a directory, or failing before any output is captured.

Use a cron entry that removes the usual ambiguities

Per-user crontab

Edit the crontab as the account that should run the job:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
crontab -e

A per-user line has five schedule fields followed by the command. It does not include a username field. This complete example sets the shell and path, invokes Bash explicitly, and records both output streams:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# minute hour day-of-month month day-of-week command
15 2 * * * /usr/bin/env bash /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1

The schedule above requests 02:15 according to the machine’s cron time settings. The absolute path after bash must point to the real file, and the log directory must already exist and be writable by alice.

System-wide cron files

/etc/crontab and files in /etc/cron.d use a different format: they include the account name between the schedule and the command.

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

15 2 * * * alice /usr/bin/env bash /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1

Putting that username field into a per-user crontab shifts every field and prevents the intended command from being parsed correctly. Conversely, omitting it from a system-wide entry does not identify an account and is invalid for that format.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run the script directly or through its shebang

If the script is executable and starts with a suitable shebang, cron can invoke the file itself:

#!/usr/bin/env bash
set -e
# script commands here
15 2 * * * /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1

Explicitly calling /usr/bin/env bash is useful when you want the interpreter choice visible in the crontab. Either approach still requires an absolute script path and permissions that allow the cron user to execute it.

Diagnose the failure in the order that saves time

1. Confirm which crontab owns the job

List the entries for the intended account:

crontab -l

Run that command as the same user whose permissions and home directory the job should use. It is easy to edit root’s crontab while expecting a normal user’s job to run, or to edit one account and inspect another. For system locations, inspect /etc/crontab and the relevant file under /etc/cron.d, checking both the format and the username field.

Cron also has distribution-specific locations such as /etc/anacrontab. Do not assume that an entry copied between these locations retains the same syntax. After saving an edit, make sure the daemon has loaded the changed file; the service status and logs provide evidence.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Prove that the daemon is running

The schedule cannot fire if the daemon is stopped or failed to load the crontab. Check the service name used by your distribution:

systemctl status cron
systemctl status crond

Only one of those service names may exist. Start or enable the appropriate service according to your operating system’s administration policy, then inspect its recent messages:

journalctl -u cron
journalctl -u crond

Traditional implementations may write cron activity to the system log instead of the systemd journal. Look for entries showing that the daemon read the crontab and attempted the command. On systems using systemd-generated cron units, the generated unit and its journal entry are the authoritative record for an invocation.

3. Use a one-minute scheduler test

Before debugging a large backup or deployment script, replace the command temporarily with a harmless write to an absolute, writable location:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
* * * * * /usr/bin/date >>/home/alice/log/cron-heartbeat.log 2>&1

Wait for the next minute boundary and inspect the file:

cat /home/alice/log/cron-heartbeat.log

If a new timestamp appears, cron is scheduling commands and the destination is writable; concentrate on the script and its environment. If the file stays absent, investigate the crontab source, syntax, daemon state, account, directory permissions and service logs before changing script logic. Remove the test entry once the scheduler is proven.

4. Select the shell deliberately

The default command shell is /bin/sh, not necessarily Bash. Bash-only constructs such as arrays, [[ ... ]], certain parameter expansions and Bash-specific options can fail immediately when interpreted by /bin/sh.

Choose one consistent method:

  • Put a Bash shebang such as #!/usr/bin/env bash at the top and invoke the executable script.
  • Invoke Bash explicitly in the cron command, for example /usr/bin/env bash /home/alice/bin/backup.sh.
  • Set SHELL=/bin/bash in the crontab where that setting is supported, while still making the script’s interpreter clear.

Do not assume that setting SHELL changes how an already malformed command is parsed, or that it supplies variables your script needs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Define the environment instead of inheriting it

Use a deliberate, system-command PATH rather than relying on an interactive shell:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Then use absolute paths for important commands such as /usr/bin/date, /usr/bin/rsync or the interpreter. Add application-specific variables explicitly:

APP_CONFIG=/home/alice/etc/backup.conf
BACKUP_ROOT=/srv/backups

A crontab assignment is not an interactive shell assignment. The value string is not parsed for ordinary environmental substitution, so this is not a reliable way to append to an existing value:

PATH=$HOME/bin:$PATH

Write the complete value instead, or set and export variables inside the script after choosing its shell. If the program depends on credentials, sockets, locale settings or configuration files, identify each dependency and supply it explicitly; never infer that a terminal login’s state will be present.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

6. Remove current-directory assumptions

A relative name such as ./config.ini, logs/run.log or input/data.csv may resolve differently under cron. Use absolute names in the crontab and in the script. If the script is designed around a project directory, change into it at the beginning and stop with a useful error if that fails:

#!/usr/bin/env bash
set -euo pipefail

cd /home/alice/app || {
  printf '%sn' 'Cannot enter /home/alice/app' >&2
  exit 1
}

/usr/bin/rsync -a /home/alice/app/data/ /srv/backups/app/

The log destination should also be absolute. Create it before scheduling the job and verify that the cron account can traverse every parent directory.

7. Check identity, permissions and access controls

A per-user job runs as the crontab owner. A system-wide job runs as the account named in its line. Confirm the effective identity in a temporary diagnostic command:

* * * * * /usr/bin/id >>/home/alice/log/cron-identity.log 2>&1

That account must be able to:

  • Execute the script, or read it when invoking an interpreter.
  • Traverse every parent directory in the script, configuration and log paths.
  • Read input files and create or append output files.
  • Access any credentials, sockets, mounts or service endpoints the program requires.

“The file is executable” is not sufficient when a parent directory denies traversal or when the output directory belongs to another account. Test access as the cron user, not only as root or as the account you normally use interactively.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

8. Capture stdout and stderr

Without redirection, output may be mailed to the crontab owner or to the address in MAILTO, and some implementations also send messages to syslog. Mail is easy to overlook, and a script that writes only to standard error can appear silent. Redirect both streams to a known file:

>>/home/alice/log/backup.cron.log 2>&1

The first operator appends standard output; 2>&1 sends standard error to the same destination. Use a separate log for each job while diagnosing so that unrelated runs do not obscure the failure. Check the file immediately after a scheduled attempt, then inspect the daemon log or journal if no command output exists.

9. Separate scheduler failure from script failure

The heartbeat test establishes whether cron launched anything. Once it succeeds, run the real command with diagnostics appropriate to the script:

* * * * * /usr/bin/env bash /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1

For a script that is compatible with strict Bash options, adding set -euxo pipefail near its start can show commands, stop on errors and expose failures in pipelines. Use those options only when the script’s logic supports them; they can change behavior in scripts that intentionally handle nonzero statuses or unset variables.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not infer success from the absence of an email. Check the log, the expected output and, where appropriate, the command’s exit status. A scheduler event proves only that a process was started, not that the work completed.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common symptoms and the corresponding fix

Symptom Likely cause What to check
Nothing is logged and the heartbeat file is absent Wrong crontab, malformed schedule, stopped daemon or unread system entry crontab -l, the system-wide file format, systemctl status cron or crond, and service logs
The heartbeat runs but the script fails immediately /bin/sh interpreted Bash syntax, or the script/interpreter path is wrong Shebang, explicit Bash invocation and the first stderr lines in the redirected log
A command is “not found” Cron’s limited PATH Set a complete PATH and use an absolute command path
Files are found interactively but not by cron Relative paths or an unexpected working directory Convert paths to absolute names and cd explicitly where needed
The script cannot create a log or output The cron account lacks directory traversal or write permission Verify identity with id and test every parent directory as that user
The command runs twice Two cron implementations or duplicate entries Inspect active cron services, generated systemd units and every crontab source
The job runs but appears to lose settings Variables supplied by interactive startup files are absent Define required variables in the crontab or script and avoid unexpanded assignments

Logging on systemd-based and traditional systems

There is no single log location for every distribution. Traditional cron daemons may send execution notices to syslog and command output to local mail. On a systemd-cron implementation, generated units and journal entries can show the command, start time, exit result and captured output. Inspect both the service that schedules the job and the unit representing the generated invocation when your platform provides one.

Keep the application log separate from daemon diagnostics. The application log answers “what did the script print?”; the daemon or journal answers “did the scheduler launch it, under which account, and with what result?” Reading both prevents mistaking a quiet script for a scheduler failure.

When a systemd timer is a better fit

Some jobs can be implemented with classic cron or a systemd timer. The choice depends on the operating system and operational requirements rather than a universal rule.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Decision axis Classic cron systemd timer
Dependency ordering Schedule-oriented; dependencies must be handled by the command or script Can integrate with service dependencies and ordering
Environment and user isolation Controlled through crontab variables, command paths and the selected account Declared in unit configuration with systemd service controls
Persistence after downtime Behavior depends on the cron implementation and schedule Timer options can express missed-run behavior where supported
Logging Mail, syslog or a manually redirected file Native journal integration for the generated service
Retries and failure handling Usually implemented in the script Service and timer settings can model more operational behavior
Calendar expressiveness Five schedule fields Calendar and elapsed-time expressions supplied by systemd
Duplicate-run protection Must be designed in the command or script Can use service-level controls, but still requires careful unit design

If you keep cron, avoid running two cron implementations for the same schedules. Duplicate daemons can execute one line more than once, making a correct script look unreliable.

A repeatable repair checklist

  1. Run crontab -l as the intended account and verify that the line is in the expected crontab.
  2. Identify whether the line belongs in a per-user crontab, /etc/crontab or /etc/cron.d, and use that file’s correct field format.
  3. Confirm that the appropriate cron or crond service is active and has loaded the file.
  4. Install a one-minute date or touch test at an absolute writable path.
  5. Choose Bash explicitly when the script uses Bash syntax; otherwise make the script’s POSIX shell assumptions clear.
  6. Set a complete PATH and define every application variable the script needs.
  7. Replace relative interpreter, command, configuration, input, output and log paths with absolute paths; cd explicitly when a project directory is required.
  8. Verify the effective user, execute/read permissions, directory traversal, credentials and required sockets or mounts.
  9. Redirect standard output and standard error, then inspect the application log plus cron mail, syslog or the systemd journal.
  10. Only after the scheduler is proven, debug script logic with suitable tracing and confirm the expected result and exit status.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

BashcronCrontabLinuxshell scriptingsystem administrationtroubleshooting

Leave a reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.