Copy or move folders between two Linux machines on the same local network. SSH provides the connection and rsync does the transfer, resuming and skipping unchanged files on a re-run. The direction does not matter: the machine receiving the files runs the SSH server, and the machine sending the files runs rsync. Replace SOURCE_PATH, DEST_PATH, and the user and IP placeholders as needed.
Quick reference
# on the receiving machine: enable SSH and find its IP
sudo systemctl enable --now sshd
ip -4 addr show | grep inet
# on the sending machine: dry-run, then the real transfer
cd SOURCE_PATH
rsync -avh --dry-run ./ "$USER"@192.168.1.17:DEST_PATH/
rsync -avh --progress ./ "$USER"@192.168.1.17:DEST_PATH/
# verify on the receiving machine, then remove the source if this was a move
du -sh DEST_PATH
rm -rf SOURCE_PATHEnable SSH on the receiving machine
The machine that receives the files needs an SSH server running. Enable and start it in one step, then confirm it is active.
sudo systemctl enable --now sshd
systemctl status sshdActive: active (running) confirms it is up. On Debian or Ubuntu the unit and
package are named ssh / openssh-server instead of sshd.
Find the receiving machine IP
Read the local network address of the receiving machine. Use ip directly, since
a minimal install may not ship hostname -I.
ip -4 addr show | grep inetinet 127.0.0.1/8 scope host lo
inet 192.168.1.17/24 brd 192.168.1.255 scope global dynamic wlo1Use the LAN address, usually 192.168.1.x on the active interface. Do not use
127.0.0.1 (loopback) or a 172.x Docker bridge address. For more on reading
this output, see Find local network IP.
Test the SSH connection
From the sending machine, confirm SSH works before transferring anything.
ssh "$USER"@192.168.1.17On the first connection SSH asks whether to trust the host fingerprint; type
yes, then enter the receiving user's password if prompted. Run exit once the
login succeeds. To connect with a short alias instead of the full user and IP,
add a host block to ~/.ssh/config (see
SSH into a local network device).
Understand the rsync trailing slash
A trailing slash on the source changes what gets copied. This is the most common source of mistakes.
| Source form | Result |
|---|---|
rsync SOURCE_DIR/ DEST_DIR/ | Copies the contents of SOURCE_DIR into DEST_DIR |
rsync SOURCE_DIR DEST_PARENT/ | Copies SOURCE_DIR itself into DEST_PARENT |
The reliable pattern is to cd into the source folder and copy ./, which avoids
mistyping a long source path.
Dry-run first
Always preview with --dry-run. rsync lists what it would copy without moving any
file content. The flags are -a (archive: recurse and preserve permissions and
timestamps), -v (verbose), and -h (human-readable sizes).
cd SOURCE_PATH
rsync -avh --dry-run ./ "$USER"@192.168.1.17:DEST_PATH/If you see change_dir "...source..." failed: No such file or directory, the
source path is wrong. Confirm where you are with pwd before retrying.
Run the transfer
Once the dry-run output looks correct, drop --dry-run and add --progress to
watch larger files transfer.
rsync -avh --progress ./ "$USER"@192.168.1.17:DEST_PATH/rsync prints an incremental file list and finishes without an error code. Re-run the same command anytime to copy only what changed.
Mirror with --delete
By default rsync only ever adds and updates: it never removes anything on the destination. So if you delete a file on the source (or a tool renames or moves an attachment), the old copy still sits on the destination forever, and over repeated runs the destination accumulates orphans that are no longer in the real source.
--delete removes files on the destination that no longer exist on the source,
making the destination an exact mirror instead of an ever-growing pile.
# preview exactly what would be deleted before committing
rsync -avh --progress --delete --dry-run ./ "$USER"@192.168.1.17:DEST_PATH/
# then run it for real
rsync -avh --progress --delete ./ "$USER"@192.168.1.17:DEST_PATH/The key thing to understand: --delete deletes on the destination side. It is
safe only when the sync is strictly one-way and the source is the single source of
truth, so "make the destination match the source" is exactly what you want. It is
dangerous the moment that assumption breaks: if you point the direction the other
way, or keep unique files that live only on the destination, --delete wipes them.
Always run it with --dry-run the first time to see what it would remove.
Verify on the receiving machine
SSH into the receiving machine and confirm the destination size and a sample of the files before trusting the transfer.
ssh "$USER"@192.168.1.17
du -sh DEST_PATH
find DEST_PATH -maxdepth 2 -type f | head -50
exitRemove the source after verifying
Only after the destination checks out, delete the original on the sending machine. This step applies to a move, not a copy.
cd ~
rm -rf SOURCE_PATHrm -rf is irreversible. Verify the destination first, and prefer an absolute
SOURCE_PATH (or cd ~ first) so a typo cannot delete the wrong folder.
Safety notes
- Always dry-run before the real transfer.
- Prefer
cd SOURCE_PATHthenrsync ... ./ ...over typing a long source path. - Verify the destination before deleting the source.
- Do not add
--deleteunless you explicitly want the destination mirrored. - Be careful transferring secrets:
.envfiles, SSH keys, cloud credentials, age identities, browser profiles, and app state folders. - For project code, prefer Git. Use rsync over SSH for notes, archives, scans, and one-time folder moves.