How To Copy Files With rsync Over SSH (And Fix Common Errors

How To Copy Files With rsync Over SSH (And Fix Common Errors)

If you’re trying to copy files between servers with rsync over SSH and keep hitting permission errors, weird local directories, or cron jobs that just silently fail, you’re not alone.

Let’s walk through how to run rsync over SSH correctly, why /root is giving you a headache, and what usually breaks when you put rsync into crontab.

1. The basic idea: rsync over SSH

rsync is for copying/syncing files.
SSH is for connecting securely to another machine.

When you combine them, you get:

rsync [options] -e "ssh [ssh-options]" source destination

In the example from the question, the command looks like this:

rsync -avz \
  -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
  --progress \
  ~/rocktree/dev/apps/countdown-timer/index.php \
  192.81.208.45:/root/

Quick breakdown:

  • -a → archive mode (permissions, timestamps, etc.)
  • -v → verbose output
  • -z → compress data during transfer
  • -e "ssh ..." → tell rsync to use SSH with specific options
  • --progress → show transfer progress
  • source → local file path
  • destination → remote host and path

So far, so good. But then comes the error.

2. Fixing “Permission denied” when sending to /root/

The error you’re seeing is:

rsync: change_dir#1 "/root/" failed: Permission denied (13)
rsync error: errors selecting input/output files, dirs (code 3) at main.c(562) [Receiver=3.0.9]
rsync: connection unexpectedly closed (8 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at ...

Plain English: you don’t have permission to write to /root/ on the remote server.

/root is the home directory for the root user. Normal users are not allowed to write there. So if you run:

rsync ... 192.81.208.45:/root/

you are connecting as your default SSH user (often something like user), and that user is not allowed to touch /root.

To upload into /root, you must actually connect as root:

rsync -avz \
  -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
  --progress \
  ~/rocktree/dev/apps/countdown-timer/index.php \
  [email protected]:/root/

Key difference: [email protected] instead of just 192.81.208.45.

If that still fails, likely causes:

  • Root login over SSH is disabled.
  • Root has no valid SSH key/password set.

In that case, safest next step is usually: copy to a non-root user’s home directory instead, and then move it with sudo once you’re logged in.

Example flow:

  1. rsync to a normal user:
    bash
    rsync -avz -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
    --progress \
    ~/rocktree/dev/apps/countdown-timer/index.php \
    [email protected]:/home/user/
  2. SSH into the server as that user.
  3. Use sudo mv or sudo cp to move it into /root if you truly need it there.

That way you respect permissions, and you’re not fighting with locked-down SSH settings.

3. Why rsync thinks you’re copying locally

Another problem mentioned: when using user@IP without a colon in the right place, rsync “thinks I’m sending it locally and creates a directory on my machine.”

This usually comes down to how rsync parses the destination.

Remote syntax needs to look like one of these:

The colon : is important.
If rsync doesn’t see a proper HOST:PATH pattern, it may think you meant a local path and happily create a local directory instead.

So:

If you see rsync creating user@IP as a directory locally, check the colon.

4. About using ssh://user@IP (and why it hangs)

Trying:

  • ssh://user@IP
  • ssh://IP:/root/

and seeing it “get stuck” with no errors is another clue.

rsync does not need the ssh:// URL-style prefix when you’re already telling it to use SSH with -e "ssh ...".

Typical remote form is just:

rsync ... user@IP:/path/on/remote

No ssh://.

If you add that ssh:// prefix, rsync may not understand what you mean and can appear to hang or just not behave as you expect.

So if things feel stuck and weird:

  • Drop the ssh:// prefix.
  • Make sure there’s exactly one colon after the host.

Example of a clean command:

rsync -avz \
  -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
  --progress \
  ~/rocktree/dev/apps/countdown-timer/index.php \
  [email protected]:/home/user/

5. Running rsync over SSH in crontab

Last piece of the puzzle: “how to run rsync over ssh in a crontab… script works fine when executed but when placed in crontab it doesn’t work at all.”

This is super common with cron.
The main reasons are:

  1. Different environment (PATH, HOME, etc.).
  2. SSH keys or host checking acting differently in non-interactive mode.
  3. Not using full paths to executables.

You already tried “full path to rsync, ssh etc.” which is good, but let’s go over the general checklist.

5.1 Use full paths for everything

Cron doesn’t know your regular shell PATH.

Instead of:

rsync -avz -e "ssh ..." ...

use something like (paths are just examples):

/usr/bin/rsync -avz \
  -e "/usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
  --progress \
  /full/path/to/local/file \
  [email protected]:/full/path/on/remote/

Same rule for any script you call from cron: bash /full/path/to/script.sh, not ./script.sh.

5.2 Use full paths for files and dirs

In cron, relative paths (~/something or ./something) can point somewhere else than you expect.

Change things like:

~/rocktree/dev/apps/countdown-timer/index.php

into:

/home/youruser/rocktree/dev/apps/countdown-timer/index.php

Cron doesn’t expand ~ the way your interactive shell does, and it may run from / as the working directory.

5.3 Check SSH access from non-interactive context

If your rsync command depends on SSH keys, make sure SSH works without asking for passwords or prompts.

From the same user that owns the crontab, run:

ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null [email protected]

If it prompts for a password or passphrase, cron will just hang or fail.
You’d need either:

  • A key without a passphrase, or
  • An SSH agent that’s properly initialized before cron (trickier), or
  • Use some other auth method that doesn’t require interaction.

5.4 Log cron output to see errors

Cron failures are annoying because they’re silent.
Add logging to your cron line so you can see what’s going on.

Example crontab line:

0 * * * * /usr/bin/rsync -avz -e "/usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
  /full/path/to/local/file \
  [email protected]:/full/path/on/remote/ \
  >> /var/log/rsync-cron.log 2>&1

Then you can open /var/log/rsync-cron.log and see exactly what rsync or ssh complained about.

6. Safety tips: permissions, root, and testing

A few quick safety notes so you don’t accidentally break things while experimenting with rsync and SSH.

  1. Be careful with /root
    If you don’t specifically need files in /root, consider using a normal user’s home dir. It simple avoids permission problems and keeps root access tighter.

  2. Test with a small file first
    Before syncing a big directory, just send one small file and confirm it lands where you expect. Saves a lot of time and surprises.

  3. Use --progress while debugging
    You already used --progress — keep that during testing so you can see whether rsync is actually moving data or hanging.

  4. Double-check your destination path
    One typo and rsync may create weird directories. Make sure user@host:/path/ is really what you want, especially when running under cron.

7. Quick summary and next steps

  • “Permission denied (13)” for /root/ → you must connect as root, or copy to a normal user and move with sudo later.
  • rsync creating local directories named like user@IP → you’re missing the colon : in the destination, or the format is off.
  • ssh://user@IP → not needed here; stick with user@IP:/path when using -e ssh.
  • Cron job works manually but not in crontab → use full paths for rsync, ssh, and all files; make sure SSH auth doesn’t require interaction; log output to a file.

Once you get the pattern right, rsync over SSH becomes one of those boring tools that “just works” in the background.

If this saved you time, bookmark CrushEdge for more fixes.

No Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.