How to Fix “Permission Denied” When Running rsync Over SSH in Crontab (SSH Agent Solution)


2 views

When executing rsync commands over SSH in a cronjob, you'll often encounter authentication failures even when the same command works fine interactively. This happens because cron jobs run in a minimal environment that doesn't have access to your SSH agent by default.

Your interactive shell loads your SSH agent through your .bashrc or similar startup files, but cron jobs:

  • Don't load your shell configuration files
  • Run with a minimal environment (no SSH_AUTH_SOCK variable)
  • Have no terminal or display associated

Method 1: Load SSH Agent in Crontab

The most reliable approach is to explicitly load the SSH agent in your crontab entry:

0 9 * * * source ~/.bashrc && rsync -a mydir remote_machine:

Or more precisely target the agent socket:

0 9 * * * eval $(ssh-agent) && ssh-add ~/.ssh/id_rsa && rsync -a mydir remote_machine:

Method 2: Use Keychain

Keychain manages ssh-agent for you across sessions. First install it:

sudo apt-get install keychain  # Debian/Ubuntu
brew install keychain         # macOS

Then modify your crontab:

0 9 * * * source $HOME/.keychain/$HOSTNAME-sh && rsync -a mydir remote_machine:

Method 3: SSH Config with Persistent Connection

Add this to your ~/.ssh/config:

Host remote_machine
    HostName actual.hostname.com
    User yourusername
    ControlMaster auto
    ControlPath ~/.ssh/control:%h:%p:%r
    ControlPersist 10m

Then establish the connection before cron runs:

ssh -f -N remote_machine

For debugging, add this to your cronjob temporarily:

* * * * * env > /tmp/cronenv.log

Then check if SSH_AUTH_SOCK is present in the environment.

When automating SSH connections:

  • Use dedicated SSH keys with limited permissions
  • Consider adding command= restrictions in authorized_keys
  • Regularly rotate automated keys
  • Never store passphrases in cronjobs

When executing rsync over SSH via cron while using ssh-agent, the authentication fails despite working perfectly in interactive shell sessions. This occurs because cron jobs don't inherit the SSH_AUTH_SOCK environment variable that ssh-agent sets.

The key difference lies in environment variables:

  • Interactive shells automatically inherit SSH_AUTH_SOCK from your login session
  • Cron runs in a minimal environment without these variables

Method 1: Forward SSH Agent in Cron

Explicitly set the environment in your cron job:

0 9 * * * SSH_AUTH_SOCK=$(ls -t /tmp/ssh-*/agent.* | head -1) rsync -a mydir remote_machine:

Method 2: Use Keychain

Keychain manages ssh-agent and provides a reliable way to access it from cron:

0 9 * * * source $HOME/.keychain/$HOSTNAME-sh && rsync -a mydir remote_machine:

Method 3: SSH Config Alternative

Configure passwordless SSH access permanently in ~/.ssh/config:

Host remote_machine
    HostName actual.hostname.com
    User yourusername
    IdentityFile ~/.ssh/id_rsa
    IdentitiesOnly yes
    ForwardAgent no

Add these debugging lines to your cron job to verify the environment:

0 9 * * * echo "SSH_AUTH_SOCK: $SSH_AUTH_SOCK"; env > /tmp/cronenv; rsync -avvv mydir remote_machine: > /tmp/rsync.log 2>&1

For enterprise environments, consider this robust approach:

0 9 * * * eval $(ssh-agent) && ssh-add ~/.ssh/id_rsa && rsync -a -e "ssh -o BatchMode=yes" mydir remote_machine: