You’ve written a shell script. It works perfectly when you run it manually. You add it to cron, and… nothing. No output, no errors, just silence.

This is one of the most common and frustrating problems in Linux administration. Here’s why it happens and how to fix it.

The Root Cause: Environment Differences

When you run a script manually, you get your full shell environment: $PATH, environment variables, your working directory, and all the context you’re used to.

Cron runs in a minimal environment. It doesn’t source your .bashrc, doesn’t have your $PATH, and starts from a different working directory.

Fix #1: Use Absolute Paths for Everything

This is the most common culprit. Your script calls python or aws or docker, and cron can’t find them.

Bad:

1
2
3
#!/bin/bash
python3 /home/user/scripts/backup.py
aws s3 sync /data s3://my-bucket/

Good:

1
2
3
#!/bin/bash
/usr/bin/python3 /home/user/scripts/backup.py
/usr/local/bin/aws s3 sync /data s3://my-bucket/

Find the full path with which:

1
2
3
which python3    # /usr/bin/python3
which aws        # /usr/local/bin/aws
which docker     # /usr/bin/docker

Fix #2: Set PATH at the Top of Your Script

If you have many commands, set the PATH explicitly:

1
2
3
4
5
6
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"

# Now these work without absolute paths
python3 /home/user/scripts/backup.py
aws s3 sync /data s3://my-bucket/

Or set it in your crontab:

1
2
PATH=/usr/local/bin:/usr/bin:/bin
* * * * * /home/user/scripts/myscript.sh

Fix #3: Set the Working Directory

Scripts often assume they’re running from a specific directory.

Bad:

1
2
3
4
#!/bin/bash
# Assumes we're in the project directory
./run_backup.sh
cat config/settings.json

Good:

1
2
3
4
#!/bin/bash
cd /home/user/myproject || exit 1
./run_backup.sh
cat config/settings.json

Fix #4: Capture Output for Debugging

Cron swallows all output by default. Redirect it to see what’s happening:

1
2
# Redirect stdout and stderr to a log file
* * * * * /home/user/scripts/myscript.sh >> /var/log/myscript.log 2>&1

Or send errors to yourself:

1
2
MAILTO=you@example.com
* * * * * /home/user/scripts/myscript.sh

Fix #5: Source Required Environment Files

If your script needs environment variables (API keys, database URLs), source them explicitly:

1
2
3
4
5
6
7
#!/bin/bash
source /home/user/.env
# Or
export $(cat /home/user/.env | xargs)

# Now $DATABASE_URL and $API_KEY are available
/usr/bin/python3 /home/user/scripts/backup.py

Fix #6: Check the Shebang

Make sure your script has the correct shebang and is executable:

1
2
#!/bin/bash
# NOT #!/bin/sh if you're using bash features

Make it executable:

1
chmod +x /home/user/scripts/myscript.sh

Fix #7: Handle Missing TTY

Some commands behave differently without a terminal. Add these flags:

1
2
3
4
5
6
7
8
# For docker
docker exec -i container_name command  # Not -it, cron has no TTY

# For SSH
ssh -o BatchMode=yes -o StrictHostKeyChecking=no user@host command

# For sudo (if you must)
echo "password" | sudo -S command  # Bad practice, but sometimes necessary

The Debugging Checklist

When your cron job fails, check these in order:

  1. Can cron find the script? Use absolute path in crontab
  2. Can the script find its commands? Use absolute paths or set PATH
  3. Is the script executable? chmod +x
  4. What’s the error? Redirect output to a log file
  5. Does it need environment variables? Source them explicitly
  6. Is cron even running? systemctl status cron
  7. Is the syntax correct? Use crontab.guru to verify

Test Like Cron

Simulate cron’s environment to debug locally:

1
env -i /bin/bash --noprofile --norc -c '/home/user/scripts/myscript.sh'

This runs your script with a minimal environment, just like cron does.

Example: A Bulletproof Cron Script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
set -euo pipefail

# Logging
LOG_FILE="/var/log/mybackup.log"
exec >> "$LOG_FILE" 2>&1
echo "=== $(date) ==="

# Environment
export PATH="/usr/local/bin:/usr/bin:/bin"
cd /home/user/myproject || exit 1
source /home/user/.env

# The actual work
/usr/bin/python3 scripts/backup.py

echo "Completed successfully"

Crontab entry:

1
0 2 * * * /home/user/myproject/backup.sh

This script will log everything, fail fast on errors, and work reliably in cron’s minimal environment.


The difference between a script that works manually and one that works in cron is explicitness. Don’t assume anything about the environment — specify everything.