Cron Job Script: How to Write One That Runs Correctly
September 9, 2026


What a Cron Job Script Actually Is
A cron job script is the executable file that actually does the work — the bash or Python file cron invokes at the scheduled time. It's easy to conflate this with the crontab entry, but they're different: the crontab line tells cron when to run something and what command to call, while the script is what actually executes, and it's where almost every real-world cron failure originates. If you've spent time getting the schedule syntax right, the crontab flags and syntax are only half the job. This article covers the other half: making the script itself behave correctly once cron calls it.
Why Scripts That Work Manually Break Under Cron
You run the script from your terminal and it works. You add it to crontab, and it silently fails or does nothing. This is one of the most common complaints developers have, and the cause is almost always the same: cron runs commands in a minimal, non-interactive shell environment that looks nothing like your login shell.
At a terminal, your shell has already sourced .bashrc or .profile, built out a full PATH, and set environment variables for your session. Cron does none of that. It starts with a stripped-down environment, a different working directory (often your home directory or /), and no interactive shell configuration loaded. That's why "cron script not running" and "command not found" are usually environment problems, not logic problems. The Unix cron model was built for unattended, minimal execution — reliable, but unforgiving of scripts that assume a full shell context.
Writing a Cron-Safe Script Step by Step
Here's a practical structure for how to write a cron job script that survives contact with cron's minimal shell.
Add the correct shebang and permissions
Every script needs a shebang line telling the system which interpreter to use: #!/bin/bash for bash, or #!/usr/bin/env python3 for Python. Without it, cron may fail to interpret the file correctly. Just as important: the file must be executable. Run chmod +x script.sh before you ever add it to crontab. Cron doesn't throw a helpful error if a script lacks the executable bit — it just silently fails to run it, which is why "permission denied" and "nothing happens" are so often the same root cause.
Use absolute paths everywhere
A command that works manually, like python3 myscript.py or curl, can fail under cron because cron's PATH is much shorter than your shell's. A bare command name like mysqldump might not resolve at all. Fix this two ways: use full paths to binaries (/usr/bin/curl instead of curl), and set PATH explicitly at the top of your script, or cd into a known working directory before doing anything relative to it. Relative file references (./data.txt) are a frequent source of "file not found" errors simply because cron's working directory isn't what you assumed.
Source or set environment variables explicitly
If your script depends on database credentials, API keys, or other environment variables normally loaded by .bashrc or .profile, cron won't have them. Two solid options: source a known environment file explicitly inside the script (source /etc/profile or a project-specific .env), or set BASH_ENV in crontab to point to a file sourced before every run. Don't assume anything beyond a handful of default variables is present — cron starts close to empty.
Log output instead of relying on cron's mail
By default, cron can email a job's stdout/stderr if MAILTO is set, but mail delivery is unreliable on most modern servers and easy to miss. Redirect output to a logfile instead, with timestamps so you can trace exactly when something ran and what happened:
echo "$(date '+%F %T') Starting job" >> /var/log/myjob.log
/usr/bin/curl -sf https://api.example.com/ping >> /var/log/myjob.log 2>&1
This single habit solves the "no idea where cron's output is going" problem for good.
Use meaningful exit codes
A script should exit 0 only on real success and non-zero on any failure. This matters because cron, and any external monitor, uses the exit status to detect trouble. A script that always exits 0 regardless of what happened defeats failure detection entirely — the classic "partial success" bug, where a script errors out halfway through but still reports success because the last line executed was harmless.
A Minimal Cron-Safe Script Example
Here's a small, well-commented cron job script example that pings an HTTP endpoint and ties everything together:
#!/bin/bash
# Absolute paths and explicit PATH avoid "command not found"
PATH=/usr/local/bin:/usr/bin:/bin
LOG=/var/log/http-check.log
echo "$(date '+%F %T') Starting HTTP check" >> "$LOG"
/usr/bin/curl -sf -o /dev/null -w "%{http_code}" https://api.example.com/health >> "$LOG" 2>&1
STATUS=$?
if [ $STATUS -eq 0 ]; then
echo "$(date '+%F %T') Success" >> "$LOG"
exit 0
else
echo "$(date '+%F %T') Failed with curl exit code $STATUS" >> "$LOG"
exit 1
fi
Once written and made executable, pairing it with a crontab line is straightforward — see 15 ready-made cron job examples if you need the scheduling syntax itself. If you're new to cron entirely, the beginner's guide to setting up a cron job covers the setup from scratch.
Testing Your Script Before You Trust Cron With It
Don't wait for cron's schedule to find out your script is broken. Simulate cron's minimal environment manually with env -i, which strips almost all environment variables:
env -i /bin/bash -c '/path/to/script.sh'
If it fails here but works in your normal terminal, you've found an environment-dependency bug before it hit production. This is the fastest way to test a cron script safely — run it stripped down, fix what breaks, then schedule it with confidence.
For scripts that modify data or call external APIs, also think about idempotency: if the job fails halfway and cron retries it (or you rerun it manually), can it safely run twice without duplicating work or corrupting state? Designing for safe reruns up front saves painful cleanup later.
A Correct Script Can Still Fail Silently — Here's the Gap
Here's the uncomfortable truth: you can follow every step above — correct shebang, absolute paths, sourced environment, clean logging, proper exit codes — and your script can still fail weeks later for reasons that have nothing to do with how it was written. An API you call goes down. Disk fills up and the log write fails. A dependency gets upgraded and silently changes behavior. None of these register as shell-level errors, so cron has nothing to email and nothing to flag. The script exits fine from cron's perspective while the actual job it was meant to do quietly stopped happening.
This is the gap between "the script ran" and "the job succeeded," and it's why cron job monitoring exists as a separate concern from writing the script itself. A cron job failed silently is worse than one that errors loudly, because nobody investigates until something downstream breaks.
The fix is to have your script report in, not just run. Wrap its HTTP call or webhook ping with a completion signal sent to a monitoring service, so you get alerted the moment a run doesn't check in on schedule — not days later when someone notices stale data. Cronevra does exactly this: it tracks execution history, flags missed or failed runs, and alerts you before the silence becomes a real problem. Check the pricing page to see which plan fits your setup.
Frequently Asked Questions
What's the difference between a cron job and a cron job script?
A cron job is the scheduled entry in crontab that defines when and how often something runs. A cron job script is the actual executable file that entry calls — the code that does the real work. The schedule can be perfect while the script itself still fails.
Why does my script work when I run it manually but not through cron?
This almost always comes down to environment differences. Cron runs with a minimal PATH, no loaded .bashrc or .profile, and a different working directory than your interactive shell, so commands, relative paths, or environment variables you rely on manually may not exist under cron.
Do I need to make my script executable before adding it to crontab?
Yes. Run chmod +x script.sh before scheduling it. Cron doesn't raise a clear error for non-executable files — it just fails to run them, which often looks like the job silently doing nothing.
How do I see the output or errors from a script cron ran?
Redirect stdout and stderr to a logfile inside the script itself, such as >> /var/log/myjob.log 2>&1, rather than relying on cron's MAILTO mail delivery, which is often unreliable or unconfigured on modern servers. Timestamped log entries make it easy to trace exactly what happened on each run.
Can a Python or Node.js script be used as a cron job script, or does it have to be bash?
Any executable script works, as long as it has the correct shebang line, such as #!/usr/bin/env python3 or #!/usr/bin/env node, and is marked executable. The same rules about absolute paths, environment variables, and exit codes apply regardless of language.
How do I know if my cron script actually failed instead of just not running?
Check exit codes and logs first — a script exiting non-zero with a logged error means it ran and failed, while no log entry at all suggests cron never triggered it. For ongoing visibility without manually checking logs, an external monitoring tool like Cronevra can alert you the moment a run is missing or fails, based on the script actually checking in.