Secure Transfers with Rsync
Every rsync transfer over a network should be encrypted. Rsync uses SSH as its transport layer by default, which means your file data, credentials, and metadata are protected with the same encryption used for SSH connections.
On modern systems (OpenSSH 7.0+), rsync over SSH is the default behavior. You don't need to explicitly add -e ssh — but understanding SSH configuration gives you control over ports, keys, and access restrictions.
How Rsync + SSH Works
flowchart LR
SRC["Source Server<br/>rsync client"] -->|"SSH tunnel<br/>(encrypted)"| DST["Destination Server<br/>rsync server process"]
subgraph "SSH Layer"
AUTH["Authentication<br/>(keys or password)"]
ENC["AES-256 Encryption<br/>(data in transit)"]
end
When you run rsync -avz user@server:/path/, rsync:
- Opens an SSH connection to the remote server
- Starts an rsync process on the remote end
- All file data flows through the encrypted SSH tunnel
SSH Key Authentication Setup
Key-based authentication is essential for automated rsync operations (cron jobs, scripts). It's also more secure than password authentication.
Step 1: Generate an SSH Key Pair
# Create a dedicated key for backup operations
ssh-keygen -t ed25519 -C "rsync-backup-key" -f ~/.ssh/rsync_backup_key
# Or use RSA for compatibility with older systems
ssh-keygen -t rsa -b 4096 -C "rsync-backup-key" -f ~/.ssh/rsync_backup_key
Use a dedicated key pair for backup operations rather than your personal SSH key. This allows you to revoke backup access without affecting your login access.
Step 2: Copy the Public Key to the Remote Server
ssh-copy-id -i ~/.ssh/rsync_backup_key.pub user@backup-server
# For non-standard SSH port
ssh-copy-id -i ~/.ssh/rsync_backup_key.pub -p 2222 user@backup-server
Step 3: Use the Key with Rsync
rsync -avz -e "ssh -i ~/.ssh/rsync_backup_key" \
/var/www/html/ user@backup-server:/backups/www/
Step 4: Simplify with SSH Config
Host backup-server
HostName 198.51.100.5
User backupuser
Port 2222
IdentityFile ~/.ssh/rsync_backup_key
# Optional: Disable forwarding for security
ForwardAgent no
ForwardX11 no
Now rsync commands are clean:
rsync -avz /var/www/html/ backup-server:/backups/www/
Custom SSH Port
If your SSH server runs on a non-standard port:
# Using -e flag
rsync -avz -e "ssh -p 2222" /var/www/ user@server:/backup/www/
# Using SSH config (cleaner)
# Add Port 2222 to the Host entry in ~/.ssh/config
rsync -avz /var/www/ myserver:/backup/www/
Restricting Rsync Access
For production backup systems, limit what the rsync user can do on the remote server.
Option 1: Force a Specific Command in authorized_keys
Restrict an SSH key to only run rsync:
command="rsync --server --sender -avz . /backups/",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... rsync-backup-key
This ensures:
- The key can only run rsync (not login interactively)
- No port forwarding or X11 forwarding allowed
- Restricted to a specific directory
Option 2: Dedicated Rsync User
Create a user specifically for backup operations:
# On the backup server
sudo useradd -m -s /bin/bash rsyncuser
sudo mkdir -p /backups
sudo chown rsyncuser:rsyncuser /backups
# Copy your public key
ssh-copy-id -i ~/.ssh/rsync_backup_key.pub rsyncuser@backup-server
Option 3: Using --rsync-path with sudo
When the rsync user needs root-level read access:
rsync -avz --rsync-path="sudo rsync" \
user@server:/etc/ /backup/config/
Add a sudoers rule so the user can only run rsync:
rsyncuser ALL=(root) NOPASSWD: /usr/bin/rsync --server *
SSH Hardening for Rsync
Server-Side SSH Configuration
# Use a non-standard port
Port 2222
# Disable root login
PermitRootLogin no
# Allow only specific users
AllowUsers deployuser rsyncuser
# Disable password authentication (require keys)
PasswordAuthentication no
PubkeyAuthentication yes
# Limit authentication attempts
MaxAuthTries 3
# Idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
After editing, restart SSH:
sudo systemctl restart ssh
Before disabling password authentication, make sure your SSH key works. Test with:
ssh -i ~/.ssh/rsync_backup_key user@server
If key authentication fails after disabling passwords, you'll be locked out.
Firewall Integration
# UFW: Allow SSH only on custom port
sudo ufw allow 2222/tcp
sudo ufw deny 22/tcp
# Or allow SSH only from specific IPs
sudo ufw allow from 203.0.113.10 to any port 2222 proto tcp
Protecting Sensitive Files During Transfer
Exclude Secrets from Sync
Always exclude environment files, private keys, and credentials:
rsync -avz \
--exclude='.env' \
--exclude='*.pem' \
--exclude='*.key' \
--exclude='id_rsa*' \
--exclude='wp-config.php' \
/var/www/app/ user@staging:/var/www/app/
Encrypt Backup Data at Rest
For offsite backups, encrypt before transfer:
# Encrypt database dump with GPG before rsyncing
mysqldump -u root --single-transaction --all-databases \
| gzip | gpg --symmetric --cipher-algo AES256 \
> db_backup.sql.gz.gpg
rsync -avzP db_backup.sql.gz.gpg user@backup:/offsite/db/
# To decrypt:
gpg --decrypt db_backup.sql.gz.gpg | gunzip | mysql -u root -p
Automated Secure Backup Script
#!/bin/bash
# secure-backup.sh — Encrypted rsync backup with key auth
set -e
REMOTE="backup-server" # Defined in SSH config
TIMESTAMP=$(date +%F)
LOG="/var/log/rsync-backup.log"
echo "[$TIMESTAMP] Starting secure backup" >> "$LOG"
# Sync application files
rsync -avz \
--exclude='.env' \
--exclude='cache/' \
--exclude='*.log' \
/var/www/html/ "$REMOTE:/backups/$TIMESTAMP/www/" \
>> "$LOG" 2>&1
# Sync database dump
rsync -avzP /var/backups/db/ "$REMOTE:/backups/$TIMESTAMP/db/" \
>> "$LOG" 2>&1
echo "[$TIMESTAMP] Backup complete" >> "$LOG"
Schedule with cron (SSH config handles the key automatically):
0 3 * * * /usr/local/bin/secure-backup.sh
Troubleshooting Secure Connections
| Error | Cause | Fix |
|---|---|---|
Permission denied (publickey) | SSH key not accepted | Check key permissions: chmod 600 ~/.ssh/id_rsa |
Connection refused | SSH not on expected port | Verify port: ssh -p 2222 user@server |
Host key verification failed | Server key changed | Update ~/.ssh/known_hosts or verify server identity |
rsync: connection unexpectedly closed | Firewall blocking, SSH timeout | Check firewall rules, increase ClientAliveInterval |
Permission denied on remote files | Rsync user can't access target | Use --rsync-path="sudo rsync" or fix permissions |
Common Pitfalls
| Pitfall | Risk | Prevention |
|---|---|---|
| Using password auth for automated backups | Credentials in scripts or exposed via expect | Use SSH key pairs |
| Not restricting backup SSH keys | Compromised key gives full server access | Use command= restriction in authorized_keys |
| Running rsync as root | Excessive privileges if script is compromised | Use dedicated user with minimal permissions |
| No firewall on SSH port | Exposed to brute force attacks | Use UFW/iptables + Fail2Ban |
Syncing .env / wp-config.php to staging | Exposes production credentials | Always --exclude sensitive files |
| Not encrypting offsite backups | Data exposed if backup server is compromised | GPG-encrypt before transfer |
Quick Reference
# Generate backup SSH key
ssh-keygen -t ed25519 -f ~/.ssh/rsync_backup_key
# Copy key to remote
ssh-copy-id -i ~/.ssh/rsync_backup_key.pub user@server
# Rsync with specific key
rsync -avz -e "ssh -i ~/.ssh/rsync_backup_key" /src/ user@server:/dest/
# Custom SSH port
rsync -avz -e "ssh -p 2222" /src/ user@server:/dest/
# Rsync with sudo on remote
rsync -avz --rsync-path="sudo rsync" user@server:/etc/ /backup/
# Exclude secrets
rsync -avz --exclude='.env' --exclude='*.key' /app/ user@server:/app/
What's Next
- Ownership and Permissions — Control file metadata during sync
- Cron Automation — Set up automated secure backups
- Backup Strategies — Complete backup architecture