keepcalculator

06 Numbers and Powers

Fix Bash to S3 Backups Failing Under Cron

Card No. 06·51118 · Updated · Standard formula, shown below

Bash scripts that back up files to Amazon S3 often work perfectly when run manually but fail silently when executed by cron. The root cause is almost always a difference in the environment: cron runs with a minimal PATH, a different shell, and no interactive login context. This article explains the specific reasons cron jobs fail and provides a step-by-step fix that makes your backup script robust enough to run unattended.

Why Cron Environment Differs from Your Shell

When you run a script from your terminal, your shell loads configuration files that set environment variables like PATH, HOME, and USER. Cron does not load these files. Instead, it uses a minimal environment defined by the system’s /etc/crontab and the cron daemon’s defaults. Typically, the PATH in a cron job is just /usr/bin:/bin (or even less), which means commands installed in /usr/local/bin (like the AWS CLI, if installed via pip) are not found. Additionally, cron runs scripts with /bin/sh by default, not bash, so if your script uses bash-specific syntax (like arrays or [[ ]]), it will fail with a syntax error. The fix is to explicitly set the interpreter and environment at the top of the script or in the crontab line.

Common Failure Modes for S3 Backup Scripts

  • Command not found: The AWS CLI (aws) is not in the cron PATH. This is the most common error.
  • Missing credentials: The script relies on environment variables like AWS_ACCESS_KEY_ID that are set in your shell but not in cron. Even if you use IAM roles (e.g., on EC2), the instance profile is available, but if you use named profiles, the AWS_PROFILE variable may not be set.
  • Wrong shell: The script starts with #!/bin/bash but cron ignores the shebang if you invoke it as sh script.sh in the crontab. Or the script uses source instead of . which is not POSIX.
  • Relative paths: The script uses relative paths like ./backup/ which depend on the current working directory. Cron runs from the user’s home directory by default, but it’s safer to use absolute paths.
  • Output redirection: Without redirecting output, cron emails the output to the user (if mail is configured) or discards it. You might not see errors at all.

Step-by-Step Fix: Make Your Script Cron-Proof

Follow these steps to update your script and crontab so that backups run reliably.

  1. Use a shebang and make the script executable. Start the script with #!/bin/bash and run chmod +x script.sh. In the crontab, call the script directly (/path/to/script.sh) rather than sh script.sh.
  2. Set a full PATH at the top of the script. Add export PATH="/usr/local/bin:/usr/bin:/bin" (and any other directories where your commands live). This ensures aws is found.
  3. Set environment variables explicitly. If you use access keys, either put them in the script (not ideal for security but works) or, better, use a named profile and set export AWS_PROFILE=myprofile. For IAM roles, no action is needed, but ensure the instance has the role.
  4. Use absolute paths for all files and directories. For example, BACKUP_DIR="/home/user/backups" and aws s3 cp "$BACKUP_DIR/file" s3://bucket/.
  5. Redirect output to a log file. In the crontab line, add >> /path/to/logfile.log 2>&1 so you can inspect errors.
  6. Test the script with a clean environment. Run env -i /path/to/script.sh to simulate cron’s environment. If it works, it will work under cron.

Testing Your Script Under Cron Conditions

Before adding the cron job, simulate the cron environment. The env -i command runs a command with an empty environment, which is close to what cron does. For example:

env -i /home/user/backup.sh

If this fails, you’ll see the exact error. Common errors include aws: command not found or Unable to locate credentials. Fix those by adjusting the script’s environment. Also check the script’s permissions: cron will not run a script that is not executable or is owned by another user with restrictive permissions. Ensure the script is owned by the user who owns the crontab.

Debugging Cron Failures: Logs and Exit Codes

When a cron job fails, you need to see the error output. If you didn’t redirect output, cron sends it via email to the user (if a mail system is installed). Check your mail with mail or mailx. If you redirected to a log file, check that file. Also, add set -x at the top of the script temporarily to trace command execution. The exit code of the script is not directly visible, but you can capture it by wrapping the script in another script that logs $?. For example:

0 * * * * /home/user/backup.sh >> /home/user/backup.log 2>&1; echo "Exit: $?" >> /home/user/backup.log

Note that the echo runs after the script, but only if the script is the first command. Better to include the exit code logging inside the script itself using trap.

Using AWS CLI Credentials with Cron

The AWS CLI looks for credentials in this order: environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), then the shared credentials file (~/.aws/credentials), then IAM roles. Under cron, the environment variables are not set, but the shared credentials file is still accessible if the user’s home directory is correct. However, cron may not set HOME to the expected value. To be safe, set HOME explicitly in the script or crontab. If you use a named profile, set AWS_PROFILE and also ensure the profile exists in the credentials file. For IAM roles (EC2), the instance metadata service is available, so no action is needed. Avoid putting access keys in the crontab file itself because it is readable by other users.

Frequently Asked Questions

Why does my script work manually but not in cron?

Because cron uses a minimal environment. Your interactive shell has a PATH that includes directories like /usr/local/bin, but cron’s PATH is often just /usr/bin:/bin. Also, cron does not load your shell profile, so any environment variables you set in .bashrc are missing. The fix is to explicitly set PATH and any required variables in the script.

How do I see the error output from a cron job?

Redirect the output to a log file using >> /path/to/log 2>&1 in the crontab line. Alternatively, check the system mail for the user (if mail is configured). You can also add set -x to the script to trace commands and see where it fails.

Should I use an IAM role or access keys for cron backups?

If your server is an EC2 instance, an IAM role is the best practice because it avoids storing keys. If you must use access keys, store them in the shared credentials file (~/.aws/credentials) with appropriate permissions (600) and set HOME in the script. Never put keys in the crontab itself.

Why does my script fail with ‘aws: command not found’ even though it works in the terminal?

Because the AWS CLI is installed in a directory not in cron’s PATH, such as /usr/local/bin (if installed via pip) or ~/.local/bin. Add the correct path to the PATH variable in the script. You can find the path with which aws.

Can I use a different shell in cron?

Yes, you can specify the shell in the crontab by setting SHELL=/bin/bash at the top of the crontab file. However, it’s still a good practice to use a shebang in the script and call it directly, so the script runs with the interpreter it expects.

What is the best way to schedule a backup with cron?

Use a dedicated backup script that is idempotent (can run multiple times without harm), includes error handling (e.g., set -e), and logs its output. Test it thoroughly under env -i before scheduling. Also, consider using a lock file to prevent overlapping runs.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →