Skip to main content

Rsync Source and Destination

Getting the source and destination paths right is the single most important rsync skill. A misplaced trailing slash can copy files into the wrong location, and a wrong remote syntax can fail silently.

The Golden Rule

Rsync is path-sensitive. The difference between /var/www/html/ (with trailing slash) and /var/www/html (without) produces completely different results. Understanding this rule prevents the most common rsync mistakes.

Basic Syntax

Every rsync command follows this structure:

rsync [OPTIONS] SOURCE DESTINATION

Both SOURCE and DESTINATION can be:

  • A local path — absolute (/var/www/html/) or relative (./app/)
  • A remote path — specified as user@host:/path/

Transfer Directions

Rsync supports three transfer patterns:

DirectionSyntaxDescription
Local → Localrsync -av /src/ /dest/Copy within the same server
Local → Remote (push)rsync -avz /src/ user@server:/dest/Send files to a remote server
Remote → Local (pull)rsync -avz user@server:/src/ /dest/Download files from a remote server

Local to Local

Copy files on the same machine — useful for backups to mounted storage:

# Back up web application to local backup directory
rsync -av /var/www/html/ /backup/www/

# Back up database dumps to external drive
rsync -av /var/backups/db/ /mnt/external/db/

# Back up Nginx configuration
rsync -av /etc/nginx/ /backup/config/nginx/

Local to Remote (Push)

Send files from your local machine to a remote server over SSH:

# Deploy application to production
rsync -avz /var/www/html/ user@production:/var/www/html/

# Push database dump to backup server
rsync -avzP /var/backups/db/dump.sql.gz user@backup:/backups/db/

# Sync Docker Compose configs to another server
rsync -av docker-compose.yml user@server2:/opt/docker/

Remote to Local (Pull)

Download files from a remote server to your local machine:

# Pull production logs for analysis
rsync -avz user@production:/var/log/nginx/ ./logs/

# Download remote database dump
rsync -avzP user@server:/var/backups/db/latest.sql.gz ./

# Clone remote configuration for review
rsync -av user@server:/etc/nginx/ ./nginx-config-review/

The Trailing Slash Rule

This is the most critical concept in rsync path handling. It only applies to the source path — the trailing slash on the destination doesn't change behavior.

With Trailing Slash — Copy Contents

rsync -av /var/www/html/ /backup/www/

Result: Copies the contents of /var/www/html/ directly into /backup/www/:

/backup/www/
├── index.php
├── styles.css
└── images/

Without Trailing Slash — Copy the Directory Itself

rsync -av /var/www/html /backup/www/

Result: Copies the html directory itself into /backup/www/, creating a nested folder:

/backup/www/
└── html/
├── index.php
├── styles.css
└── images/

Visual Comparison

CommandWhat Gets Created at Destination
rsync -av app/ /backup//backup/index.php, /backup/assets/
rsync -av app /backup//backup/app/index.php, /backup/app/assets/
warning

This is the #1 source of rsync mistakes. When in doubt, always use --dry-run first to see exactly what rsync will do:

rsync -av --dry-run /var/www/html/ /backup/www/

Remote Path Syntax

For remote paths, rsync uses the format user@host:/path/. The colon (:) separates the hostname from the path.

SSH-Based Remote Paths

# Standard SSH connection
rsync -avz /local/path/ user@192.168.1.100:/remote/path/

# Using hostname from SSH config
rsync -avz /local/path/ myserver:/remote/path/

# Non-standard SSH port
rsync -avz -e "ssh -p 2222" /local/path/ user@server:/remote/path/

# Specific SSH key
rsync -avz -e "ssh -i ~/.ssh/backup_key" /local/path/ user@backup:/remote/path/

Using SSH Config for Cleaner Commands

If you configure your SSH hosts in ~/.ssh/config, rsync commands become much cleaner:

~/.ssh/config
Host production
HostName 203.0.113.10
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_key

Host backup-server
HostName 198.51.100.5
User backupuser
IdentityFile ~/.ssh/backup_key

Then your rsync commands simplify to:

# Instead of the full connection string:
rsync -avz -e "ssh -p 2222 -i ~/.ssh/deploy_key" /var/www/ deploy@203.0.113.10:/var/www/

# You can write:
rsync -avz /var/www/ production:/var/www/
rsync -avz /backup/ backup-server:/offsite/
tip

Using SSH config entries is strongly recommended for servers you sync to regularly. It reduces errors, simplifies scripts, and keeps connection details in one manageable place.

Path Types: Absolute vs Relative

Path TypeExampleBehavior
Absolute/var/www/html/Always resolves to the same location
Relative./html/ or html/Resolves relative to current working directory

Recommendations

  • Use absolute paths in cron jobs, systemd timers, and scripts — they won't break if the working directory changes
  • Relative paths are fine for interactive use where you can verify with pwd
# Good for scripts and cron — always resolves correctly
rsync -av /var/www/html/ /backup/www/

# Risky in scripts — depends on current directory
rsync -av ./html/ /backup/www/

Practical Examples

Web Application Deployment

# Push PHP/Laravel app to production
rsync -avz --exclude='.env' --exclude='storage/logs/' \
/var/www/myapp/ user@production:/var/www/myapp/

# Deploy static site build
rsync -avz --delete dist/ user@webserver:/var/www/html/

# Sync Python/Django project (exclude virtualenv)
rsync -avz --exclude='venv/' --exclude='__pycache__/' \
/var/www/django-app/ user@staging:/var/www/django-app/

Server Backup

# Full application backup
rsync -avz /var/www/html/ /backup/daily/www/

# Database dumps to remote backup
rsync -avzP /var/backups/mysql/ user@backup-server:/offsite/db/

# Configuration files
rsync -av /etc/nginx/ /backup/daily/config/nginx/
rsync -av /etc/php/ /backup/daily/config/php/

Server Migration

# Migrate entire web directory to new server
rsync -avzP --delete /var/www/ user@new-server:/var/www/

# Migrate database dumps
rsync -avzP /var/backups/ user@new-server:/var/backups/

# Migrate user home directories
rsync -avzP /home/ user@new-server:/home/

Best Practices

PracticeWhy
Always test with --dry-runSee what will happen before it happens
Use absolute paths in scriptsPrevents breakage from directory changes
Configure SSH keysEnables automation without password prompts
Use SSH config entriesSimplifies commands and reduces errors
Verify with pwd before relative pathsEnsures you're in the right directory
Add -P for large remote transfersShows progress and enables resume

Common Pitfalls

PitfallWhat HappensPrevention
Missing trailing slash on sourceCreates nested directory instead of syncing contentsBe explicit: use source/ to copy contents
Wrong remote syntaxrsync: connection unexpectedly closedCheck user@host:/path format, verify SSH access
Relative paths in cronTransfer targets wrong directory or fails silentlyAlways use absolute paths in automated jobs
Forgetting -e "ssh -p PORT"Connection refused on non-standard SSH portsUse SSH config or specify port explicitly
Swapping source and destinationOverwrites source with (possibly empty) destinationDouble-check direction before running

Quick Reference

# Local backup (contents only)
rsync -av /var/www/html/ /backup/www/

# Push to remote server
rsync -avz /var/www/html/ user@server:/var/www/html/

# Pull from remote server
rsync -avz user@server:/var/www/html/ /local/backup/

# Non-standard SSH port
rsync -avz -e "ssh -p 2222" /src/ user@server:/dest/

# Using SSH config hostname
rsync -avz /src/ myserver:/dest/

# Always preview first
rsync -av --dry-run /source/ /destination/

What's Next