Why does this script fail when run from cron, but works when run manually?

The usual answer to this type of question is that cron jobs are run in non-interactive, non-login shells, so most of your shell startup files (both the systemwide ones in /etc and your personal dotfiles in your home directory) are not sourced (read in and executed), because most shell startup files apply to login shells (the first shell you see when you log into the computer), or interactive shells (shells that are connected to terminals, ssh sessions, or terminal emulators because a user is interacting with them via said terminal).

So if you put a command in a cron job that actually depends on some environment setup (including PATH changes) that usually happens in places like /etc/profile, /etc/bashrc, ~/.profile, or ~/.bashrc, that setup won't happen for a cron job. The cron file format does allow you to specify environment variables for your jobs, so you might want to specify BASH_ENV or ENV to point it at a shell startup script to source. See the "Invocation" section of the bash(1) man page.


This doesn't qualify as an answer, but I can't comment. Suggestions:

  1. prepend the following to your bash script. The last line logged to mail output will be the failing command.

    set -x
    set -e
    
    • ensure you have sendmail (install a providing package such as postfix or esmtp)
    • install a mail reader (recommend mutt)
    • ensure the mail reaches you
      • via postfix (might be done by installer automatically): add root: my-user-name to /etc/aliases or /etc/postfix/aliases
      • via cron: add MAILTO="my-user-name" to the appropriate crontab file
  2. Verify the script will run in a different environment. Specify full path to apt-get (probably not the culprit since apt-get is known to have been found) and run it in a console (not terminal) outside of X. (some dpkg configure scripts require an X session).

    • modify the script to use absolute paths, ie /usr/bin/apt-get update -y (replace with the correct path)
    • switch to a console by pressing ctrl-alt-f1
    • switch to root user: sudo -i
    • start a non-login shell without an environment: env -i /bin/bash --noprofile --norc (replace with the correct path)
    • run the script: /my/full/path/to/cronscript. Does it work?
  3. Does the script have permission? Are you using the system crontab? (once again, probably not the culprit)

    • You've stated you are using the system crontab, so skipping this.
  4. Does apt-get require session support (consolekit or systemd). This is just a shot in the dark, however.

    • Don't know enough to be of assistance.

Managed to solve it with barely any understanding of what was going on. Turned out that even though I was running from root crontab, the apt-get commands still needed a sudo in front of them. Logically I had expected that not to be needed since the script was already executing "as root", but once I added sudo... everything worked exactly as expected.