How to Copy Files Recursively in Linux: cp, rsync Examples

Copy files recursively in Linux with cp and rsync — preserve permissions, sync directories, copy over SSH, and handle large transfers efficiently.

April 22, 2026·4 min read·Damon

Two tools: cp for local copies, rsync for everything that needs more control — preserving permissions, syncing, or copying over the network.


TL;DR

# Copy directory recursively (local)
cp -r /source/dir /destination/

# Copy preserving all attributes
cp -a /source/dir /destination/

# Sync with rsync (local or remote)
rsync -av /source/dir/ /destination/
rsync -av /source/dir/ user@remote:/destination/

cp: Local Recursive Copy

# -r: recursive (required for directories)
cp -r /var/log/app/ /backup/app-logs/

# -a: archive mode (recursive + preserves permissions, timestamps, symlinks)
cp -a /etc/nginx/ /backup/nginx-config/

# With verbose output
cp -av /source/ /destination/

# Copy and overwrite without prompting
cp -rf /source/ /destination/

# Preserve timestamps and permissions without full archive
cp -rp /source/ /destination/

-a vs -r:

  • -r — recursive only, may not preserve all attributes
  • -a = -dpR — recursive + preserve everything. Use -a for backups.

rsync: More Powerful and Flexible

# Basic sync (local)
rsync -av /source/dir/ /destination/

# The trailing slash on source matters:
rsync -av /source/dir/   /dest/   # copies CONTENTS of dir into dest
rsync -av /source/dir    /dest/   # copies dir ITSELF into dest (creates dest/dir)

Key flags:

  • -a — archive (recursive, preserve permissions, timestamps, symlinks)
  • -v — verbose
  • -z — compress during transfer (useful over network)
  • -P — show progress + resume partial transfers
  • --delete — delete files in destination that don't exist in source
  • -n — dry run (preview without copying)

rsync Over SSH

# Copy to remote server
rsync -avz /local/path/ user@server:/remote/path/

# Copy from remote server
rsync -avz user@server:/remote/path/ /local/path/

# With SSH options
rsync -avz -e "ssh -p 2222" /local/ user@server:/remote/

# Show progress for large transfers
rsync -avzP /local/ user@server:/remote/

Sync Directories (Mirror)

# Make destination exactly match source (deletes extra files in dest)
rsync -av --delete /source/dir/ /destination/dir/

# Dry run first to see what would be deleted
rsync -avn --delete /source/dir/ /destination/dir/

Real Examples

Backup application directory before upgrade

cp -a /opt/myapp/ /opt/myapp-backup-$(date +%Y%m%d)/

Sync config files to multiple servers

for server in web01 web02 web03; do
  rsync -avz /etc/nginx/ $server:/etc/nginx/
  ssh $server "nginx -t && systemctl reload nginx"
done

Copy large files with progress

rsync -avP /var/backup/large-dump.sql user@remote:/backup/
# Shows:
# large-dump.sql
#     2,345,678,901 100%   45.23MB/s    0:00:49 (xfer#1, to-check=0/1)

Exclude files during copy

rsync -av --exclude="*.log" --exclude="*.tmp" /source/ /dest/

# Exclude multiple patterns from a file
rsync -av --exclude-from=/tmp/exclude.txt /source/ /dest/
# exclude.txt:
# *.log
# *.tmp
# .git/

Copy preserving hard links

rsync -aH /source/ /destination/   # -H preserves hard links

Common Mistakes

Mistake 1: Forgetting -r with cp

cp /var/log/app/ /backup/    # ERROR: omitting directory
cp -r /var/log/app/ /backup/ # CORRECT

Mistake 2: rsync trailing slash confusion

rsync -av /etc/nginx/  /backup/    # copies nginx/ CONTENTS into /backup/
rsync -av /etc/nginx   /backup/    # creates /backup/nginx/

This is the single most common rsync mistake. The trailing slash on the source controls whether the directory itself is included.

Mistake 3: --delete without dry run --delete removes files from destination that aren't in source. Always --dry-run first:

rsync -avn --delete /source/ /dest/   # dry run
rsync -av  --delete /source/ /dest/   # actual sync

Mistake 4: cp not preserving permissions

cp -r /etc/nginx/ /backup/    # may not preserve permissions
cp -a /etc/nginx/ /backup/    # preserves everything

Quick Reference

# cp
cp -r  source/ dest/      # recursive
cp -a  source/ dest/      # recursive + preserve all attributes
cp -av source/ dest/      # with verbose

# rsync local
rsync -av  source/  dest/            # sync
rsync -avP source/  dest/            # with progress
rsync -av --delete source/ dest/     # mirror (delete extras)
rsync -avn source/  dest/            # dry run

# rsync remote
rsync -avz source/ user@host:/dest/  # to remote
rsync -avz user@host:/source/ dest/  # from remote
rsync -avzP source/ user@host:/dest/ # with progress

Conclusion

Use cp -a for local copies where you want to preserve all file attributes. Use rsync -av when you need progress reporting, network transfers, or syncing (keeping destination updated without full copy). Always dry-run rsync with --delete before running for real.


Related: Linux Move Files Between Directories — moving instead of copying. How to Extract tar.gz Linux — another common file operation.