Recommended Free Tools
If a shell script works in your terminal but fails from cron, the scheduler is usually not seeing the same shell, environment, working directory, user, or permissions. Make those assumptions explicit: name Bash, use absolute paths, set a known PATH, redirect both output streams, and verify the daemon and the crontab that actually contains the job.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
A dependable per-user entry looks like this:
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
Why a script works manually but not from cron
A terminal session is interactive. It loads shell startup files, starts in a directory you recognize, supplies your usual PATH, and runs as the account you expect. Cron launches a command non-interactively with a small, defined environment. It does not reliably inherit the settings your interactive shell assembled.
As an Amazon Associate I earn from qualifying purchases.
That difference creates several common failures:
- The default command interpreter is
/bin/sh, not necessarily Bash. Bash-only syntax such as arrays,[[ ... ]], process substitution, orset -o pipefailcan fail before the script does useful work. - A command found through your terminal’s
PATHcannot be found by cron’s shorterPATH. - A relative filename resolves from a different working directory, so an input file, configuration file, or log path is missing.
- The job runs as the crontab owner, or as the explicit account in a system-wide cron file. That account may not be able to traverse a directory, read a secret, open a socket, or write a log.
- Output is mailed or sent to a system log rather than displayed in your terminal. Without redirection, an apparent “silent” failure can be hard to see.
Cron examines its stored crontabs and checks each job for the current minute, as described by the crond(8) manual. Your first task is therefore to prove whether the scheduler fired; only then should you debug the script’s application logic.
Use the right crontab format
Before changing a command, confirm where it is installed. The syntax differs between a personal crontab and system-wide files.
#1 Best Overall
| Location | How to edit or inspect | Fields on a job line | Account used |
|---|---|---|---|
| Per-user crontab | crontab -e or crontab -l as that user |
Minute, hour, day of month, month, day of week, command | The owner of that crontab |
/etc/crontab |
Edit as root; inspect directly | The five schedule fields, then a username, then the command | The username on the line |
/etc/cron.d/<file> |
Edit as root; inspect the specific file | The five schedule fields, then a username, then the command | The username on the line |
Do not copy the username field into a per-user crontab. Conversely, omitting it from /etc/crontab or a file under /etc/cron.d shifts every field and prevents the intended command from running.
A reliable cron entry
Put the following in the intended user’s crontab with crontab -e:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchSHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=
* * * * * /usr/bin/env bash /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1
The one-minute schedule is for testing; replace it with the real schedule after the job is proven. MAILTO= suppresses cron mail, so use it only when the log is monitored. If you want mail instead, set [email protected] and still keep a durable log while troubleshooting.
For a system file, include the account:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * alice /usr/bin/env bash /home/alice/bin/backup.sh >>/home/alice/log/backup.cron.log 2>&1
Ensure the log directory already exists and is writable by the effective account. Cron will not create missing parent directories for a redirection.
Step-by-step diagnosis
1. Confirm the schedule and account
- Run
whoamito identify the account you intend to use. - Run
crontab -las that account. If you usedsudo crontab -e, you edited root’s crontab instead. - Check system locations if the job is supposed to be global:
/etc/crontab,/etc/cron.d, and the distribution’s other cron directories. - Check every schedule field for accidental punctuation, an invalid range, or a missing field. Save the file through
crontab -eso the cron implementation validates it when installing.
A schedule can be perfectly valid yet appear inactive if it was saved under another account. Always inspect the exact crontab that owns the process.
2. Prove that the daemon is running
Most Linux distributions call the service cron or crond. Check the name used by your distribution:
systemctl status cron
systemctl status crond
Use the command that returns a real service on your system. If it is stopped, start it and enable it according to your operating system’s service policy. Then inspect recent service messages:
Rank #2
journalctl -u cron --since "15 minutes ago"
journalctl -u crond --since "15 minutes ago"
On systems that do not use the systemd journal, cron messages may be in a syslog file such as /var/log/cron or /var/log/syslog. File names and facilities are distribution-specific; search for the job’s command, account, or the word CRON.
3. Use a harmless one-minute test
Temporarily replace the real command with a test that has no application dependencies:
* * * * * /usr/bin/touch /home/alice/cron-ran
After the next minute, check the file’s timestamp and ownership. If it appears, the scheduler is firing and the account can write there. If it does not, concentrate on the daemon, crontab location, schedule syntax, filesystem permissions, or service logs rather than the shell script.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Choose a path whose parent directories already exist and are traversable by the cron account. A test in a private directory can fail for permission reasons even when cron itself is healthy.
4. Select the intended shell
The crontab(5) manual documents that SHELL defaults to /bin/sh. A script that begins with a Bash shebang should still be invoked unambiguously while you diagnose it:
#!/usr/bin/env bash
set -euo pipefail
Then call it from cron with:
/usr/bin/env bash /home/alice/bin/backup.sh
Alternatively, set SHELL=/bin/bash in the crontab. Explicit invocation is clearer when a line contains shell operators, command substitutions, or redirections. Check that the interpreter exists at the path you selected; minimal systems may place it elsewhere.
5. Make the environment explicit
Cron commonly supplies variables such as SHELL, PATH, USER, LOGNAME, HOME, and LANG, but their values are not a substitute for application configuration. Define every variable the script needs in the script, an environment file that the script reads explicitly, or the crontab.
Rank #3
Do not assume that a crontab assignment performs interactive-shell expansion. The manual states: “The value string is not parsed for environmental substitutions or replacement of variables.” Thus this is unsafe:
PATH=$HOME/bin:$PATH
Use a literal value instead:
PATH=/home/alice/bin:/usr/local/bin:/usr/bin:/bin
For diagnosis, add a temporary environment dump:
* * * * * /usr/bin/env >/home/alice/log/cron-environment.txt 2>&1
Remove it after inspecting the file because environment dumps can expose credentials or tokens. Do not rely on aliases, shell functions, interactive prompts, terminal settings, or startup files such as .bashrc being loaded.
6. Replace every relative path
Use absolute paths for the interpreter, executable commands, configuration files, inputs, outputs, and logs. A command that works as rsync in a terminal may need to be /usr/bin/rsync under cron. Find a command’s location interactively with command -v rsync, then use the resulting path after verifying it is stable on that host.
If the program expects to run from a project directory, change directory explicitly and fail if that operation fails:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →cd /home/alice/app || exit 1
/usr/bin/python3 /home/alice/app/job.py
Do not assume that HOME points to a particular account or that the scheduler starts in your project directory. A relative redirect such as >>logs/job.log can fail before the script starts if logs is not beneath the current directory.
7. Check identity and permissions
Test the job as the same account cron uses. Confirm all of the following:
- The account can execute the script, or the command invokes the interpreter directly and can read the script.
- It can traverse every parent directory. Directory search permission is required even when the final file is readable.
- It can read input files and configuration, create or append the log, and write the destination directory.
- Credentials, SSH keys, database sockets, mounts, and service endpoints are available to that account at the scheduled time.
- Mandatory access controls, container boundaries, or network policies are not denying the operation.
Inspect ownership and modes with ls -ld on every parent directory and ls -l on the files. Avoid solving a cron failure by making secrets or entire directory trees world-writable; grant the narrow permission the job needs.
8. Capture stdout and stderr
Always redirect both streams while troubleshooting:
>>/home/alice/log/backup.cron.log 2>&1
The first operator appends standard output; 2>&1 sends standard error to the same file. Put the redirection on the cron command, not only inside a script branch that may never execute. Use a separate absolute log for each job when multiple schedules would otherwise interleave messages.
Cron normally mails command output to the owner or to the address in MAILTO, and some implementations also send events to syslog. Check local mail for the account and the cron or syslog files. On a systemd-cron implementation, inspect the generated unit and its journal entries because the generated service is the authoritative execution record.
9. Separate scheduler failure from script failure
Once the touch test proves scheduling, restore the script with logging and run the same command manually as the cron account. Compare the environments rather than comparing it with your normal desktop session:
sudo -u alice /usr/bin/env -i
HOME=/home/alice
PATH=/usr/local/bin:/usr/bin:/bin
/usr/bin/env bash /home/alice/bin/backup.sh
Adjust the account and paths for your host. This approximates cron’s sparse environment and often exposes an undeclared variable or missing executable.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsFor a Bash script, temporary tracing can show the command that fails:
set -euxo pipefail
Use this only when the script is compatible with those options. Tracing can print secrets and can change behavior in scripts that intentionally handle nonzero statuses, so remove or narrow it after diagnosis. The log and process exit status, not the absence of an error message in your terminal, determine whether the run succeeded.
Best Value
10. Check systemd-cron and duplicate daemons
Some systems provide cron behavior through systemd-generated units. In that case, inspect the generated unit and its journal entry to see the exact command, account, exit status, and captured output. A traditional cron daemon and a systemd-based implementation should not both be active for the same schedules; two daemons can run a job twice, making a missed run look intermittent or producing duplicate side effects.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Common symptoms and precise fixes
| Symptom | Likely cause | What to check |
|---|---|---|
bash: syntax error or an unexplained parse failure |
The command was interpreted by /bin/sh |
Use a Bash shebang and invoke /usr/bin/env bash, or set SHELL=/bin/bash. |
command not found |
Cron’s PATH does not include the executable |
Use its absolute path and define a literal, safe PATH. |
| Input or configuration file is “missing” | A relative path resolved from another directory | Use absolute paths and cd explicitly with an error check. |
| No log file is created | The parent directory is absent or not writable | Create it ahead of time and test write permission as the cron account. |
| The job never appears in logs | Wrong crontab, invalid system-file format, or stopped daemon | Run crontab -l as the intended user, verify the username field in system files, and check cron/crond status. |
| The job runs twice | Duplicate entries or two cron implementations | Search all crontabs and system locations, then ensure only the intended daemon handles the schedule. |
| The script works manually but cannot reach a service | Different user, credentials, mount, socket, or environment | Run it as the cron account with an explicit environment and verify the resource is available at run time. |
Writing a cron-safe shell script
A script should fail loudly, identify its working directory, and make its dependencies visible. For example:
Free tools Windows power users keep installed
One-click scans. No signup required.
#!/usr/bin/env bash
set -euo pipefail
PATH=/usr/local/bin:/usr/bin:/bin
export PATH
cd /home/alice/app || {
printf '%sn' 'Cannot enter /home/alice/app' >&2
exit 1
}
/usr/bin/rsync -a --delete
/home/alice/app/data/
/home/alice/backups/data/
The script sets its own PATH, changes to a known directory, uses an absolute executable path, and sends a clear error to standard error. The cron entry remains responsible for capturing that error in a durable log.
Be deliberate with concurrency. If a schedule can start again before a previous run finishes, protect the operation with an appropriate lock or an atomic application-level mechanism. Do not add a lock blindly: choose behavior for stale locks, failed runs, and overlapping work, and record those outcomes in the log.
Choosing classic cron or a systemd timer
For a simple, independent command, cron can be sufficient. A systemd timer may be a better fit when the job’s lifecycle is already managed by systemd, but the right choice depends on the host and operational requirements. Compare the implementations on these axes before migrating:
| Requirement | Questions to answer |
|---|---|
| Dependency ordering | Must the job wait for a mount, network target, or another service? |
| Environment and user isolation | Where are the account, working directory, environment, and permissions declared? |
| Persistence after downtime | Should a missed run be caught up when the machine returns, or simply skipped? |
| Logging | Will operators inspect cron mail/syslog, a file, or the systemd journal? |
| Retry behavior | What should happen after a nonzero exit status, and where is that policy configured? |
| Calendar expressiveness | Can the schedule express the required calendar without hidden edge cases? |
| Duplicate-run protection | How will overlapping executions be prevented and diagnosed? |
Do not run both mechanisms for one schedule unless duplicate execution is intentional and controlled. Whichever scheduler you choose, retain absolute paths, explicit identity, captured output, and a health check that proves the command actually completed.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
A practical verification checklist
- Source: The entry is in the intended user’s crontab or has the required username field in a system file.
- Schedule: A one-minute test has been observed at the expected path.
- Daemon: The correct
cronorcrondservice is active and its logs show the job being considered. - Interpreter: Bash is selected explicitly when the script uses Bash syntax.
- Environment:
PATHand application variables are literal and declared; no interactive startup file is required. - Paths: Every command, file, directory, and log path is absolute, or the script performs a checked
cd. - Identity: The effective account can execute, read, traverse, write, and connect to every required resource.
- Evidence: Standard output and standard error are redirected, and cron mail, syslog, or the systemd journal has been checked.
- Exit status: The log proves success or records the failing command; a missing terminal message is not treated as success.
Once these checks pass, restore the intended schedule and remove temporary environment dumps, tracing, and one-minute test commands. Keep the explicit interpreter, paths, permissions, and logging in the production entry so the next failure produces evidence instead of guesswork.
Quick Recap
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.




