How to Back Up to a Remote Server Using rsync Over SSH on Fedora

Back up files to a remote server securely using rsync with SSH on Fedora.

The scenario

You just finished a major project on your Fedora desktop. The local disk is full, and you need a copy on a remote VPS before you reboot. You open the terminal, type rsync, and stare at a wall of flags. You run it anyway. The transfer finishes, but the remote directory structure is wrong, or the files are missing permissions, or the command hangs on a single large file. You need a reliable, repeatable way to push data over SSH without guessing.

What rsync actually does under the hood

rsync does not blindly copy files. It calculates a checksum for every file on the source and compares it to the destination. When a file exists on both sides, rsync only transfers the changed blocks. When a file is new, it sends the whole thing. This delta-transfer algorithm saves bandwidth and time. Think of it like a moving company that only packs the boxes you actually changed, instead of repacking the entire house.

The -e ssh flag tells rsync to use an existing SSH connection for transport. It does not handle authentication itself. It relies on your SSH configuration, keys, or password prompt. If SSH works, rsync works. If SSH fails, rsync fails. The tool streams data through the encrypted tunnel and expects the remote side to run a matching rsync daemon or binary. Fedora ships rsync in the base repository, so the remote side usually has it installed by default.

Run the command from a terminal with a stable network connection. A dropped connection mid-transfer wastes time and leaves partial files on the remote disk.

The command and why each flag matters

Here is the baseline command for a secure, permission-preserving sync.

rsync -avz --progress -e ssh /home/user/projects/ user@remote.example.com:/backups/projects/
# -a enables archive mode to preserve permissions, times, and symlinks
# -v prints every file name as it transfers for visibility
# -z compresses text data during transit to save bandwidth
# --progress shows a percentage bar so you know large files are moving
# -e ssh forces the SSH transport layer instead of the default rsync daemon
# trailing slash on source means copy the contents, not the folder itself
# trailing slash on destination ensures files land directly inside the target

Break down the flags before you run it. Archive mode (-a) is the foundation. It expands to -rlptgoD. That preserves relative paths, symlinks, permissions, modification times, group, owner, and device files. Verbose mode (-v) prints every file name as it transfers. Compression (-z) shrinks text files and logs during transit. The --progress flag shows a percentage bar for large files so you know the process did not freeze. The -e ssh flag forces SSH as the transport layer.

Notice the trailing slashes. The source path ends with /. The destination path ends with /. This distinction controls directory structure. A trailing slash on the source means "copy the contents of this directory." No trailing slash means "copy the directory itself." If you drop the slash on the source, rsync creates a nested folder on the remote side. Keep the slashes consistent.

Here is how to test the command before it touches the network.

rsync -avz --dry-run -e ssh /home/user/projects/ user@remote.example.com:/backups/projects/
# --dry-run simulates the entire transfer without writing any data
# it calculates checksums and compares files exactly like a real run
# use this output to verify paths and expected file counts
# remove the flag only after you confirm the list matches your intent

The --dry-run flag simulates the entire transfer. It calculates checksums, compares files, and prints exactly what would happen. It never writes data. Run this first. If the output lists files you did not expect, fix the paths before removing the flag.

If you are on a metered connection or a shared office network, throttle the transfer.

rsync -avz --bwlimit=5000 -e ssh /home/user/projects/ user@remote.example.com:/backups/projects/
# --bwlimit=5000 caps the transfer at 5000 KB/s to avoid saturating the link
# this flag applies to the entire transfer, not just individual files
# adjust the number based on your upstream bandwidth and remote server limits
# remove the flag when you need maximum throughput for large datasets

Test the dry run before committing. A wrong path overwrites production data.

Verify the transfer

A successful rsync run prints a summary line at the end. It shows the total size, transfer speed, and duration. Do not assume the job finished correctly just because the prompt returned. Check the remote side.

ssh user@remote.example.com "find /backups/projects/ -type f | wc -l"
# runs a remote command to count files inside the destination directory
# this gives you a quick baseline to compare against the local count
# if the number is zero, the transfer failed or the path is wrong
# if the number matches expectations, proceed to checksum verification

This counts the files on the destination. Compare it to the local count.

find /home/user/projects/ -type f | wc -l
# counts local files using the exact same criteria as the remote command
# matching numbers indicate a complete structural transfer
# mismatched numbers mean permissions blocked access or paths diverged
# investigate the difference before assuming the backup is safe

If the numbers match, run a quick checksum verification on a critical file.

sha256sum /home/user/projects/config.yaml
ssh user@remote.example.com "sha256sum /backups/projects/config.yaml"
# generates a cryptographic hash for the local configuration file
# runs the same hash command on the remote side for direct comparison
# identical output proves the data survived the network and filesystem
# mismatched hashes indicate corruption or an interrupted transfer

Matching hashes confirm the data survived the network and the filesystem. Trust the package manager. Manual file edits drift, snapshots stay.

Common pitfalls and how to read the errors

rsync fails in predictable ways. The error message tells you exactly what broke.

rsync: [sender] writefd_unbuffered failed to write 4 bytes to socket [generator]: Broken pipe (32)
rsync error: error in rsync protocol data stream (code 12) at io.c(228) [sender=3.2.7]

This error means the SSH connection dropped mid-transfer. The remote side closed the socket. Check your network stability or increase the SSH keepalive interval. Add ServerAliveInterval 60 to ~/.ssh/config to prevent idle drops.

rsync: failed to set times on "/backups/projects/": Operation not permitted (1)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1667) [sender=3.2.7]

This happens when you run rsync as a regular user but try to preserve root-owned files or set ACLs the remote user cannot modify. Drop the -a flag and use -rltv instead, or run the command with sudo on both sides if you actually need root ownership.

rsync: [receiver] mkstemp "/backups/projects/.file.tmp.XYZ" failed: No space left on device (28)

The remote disk is full. rsync creates temporary files during transfer. Clear space on the remote server before retrying. Do not force the transfer. You will corrupt the destination.

Fedora handles SSH configuration in ~/.ssh/config. Edit that file to set defaults for your remote host. Never edit files in /usr/lib/ssh/. Those ship with the openssh-clients package and get overwritten on updates. Keep your customizations in ~/.ssh/ or /etc/ssh/ssh_config.d/. SELinux also watches network connections. If you see Permission denied despite correct SSH keys, check journalctl -t setroubleshoot for denials before disabling the security policy.

Read the actual error before guessing. Half the time the symptom is gone after a path fix.

When to use rsync versus alternatives

Use rsync when you need incremental backups, bandwidth efficiency, and exact permission mirroring. Use scp when you only need to copy a single file or a small directory once and do not care about delta transfers. Use sftp when you need an interactive file manager interface over SSH. Use borg or restic when you need deduplication, encryption, and versioned snapshots stored on the remote side. Stay on rsync if you want a simple, scriptable push mechanism that works reliably across any Linux distribution.

Where to go next