Stop Memorizing rsync Flags: Use a Wrapper Instead
`rsync-profiles` is a simple bash wrapper that replaces long, error-prone `rsync` commands with named profiles. Define your source, target, and exclusions in an INI-style config file, then sync with a single command like `rsync-profiles myprofile`. It supports remote servers via SSH, dry-run previews, custom rsync flags, and profile listing. Great for anyone tired of fat-fingering remote paths or digging through bash history to find that one sync command they ran three months ago.
I work frequently with remote servers, and one of my biggest challenges is keeping static assets synchronized—especially as the file exclusion list grows. While bash history helps mitigate this issue, I've accidentally triggered rsync to the wrong remote directory on more than one occasion.
rsync-profiles
rsync-profiles is a lightweight bash script that lets you create named sync profiles so you can mirror directories with a single command — rsync-profiles target — instead of typing out long rsync commands each time.
https://github.com/robertz/rsync-profiles/tree/main
Generate a starter config file with example profiles:
rsync-profiles --init
This creates the file at:
~/.config/rsync-profiles/profiles.conf
Open it in any text editor to define your own profiles. The --init command will prompt before overwriting an existing file.
Custom config location: Set the
RSYNC_PROFILES_CONFIG_DIRenvironment variable to use a different directory.export RSYNC_PROFILES_CONFIG_DIR=~/Dropbox/config/rsync-profiles
Config File Format
The config file uses a simple INI-style format. Each profile starts with a [profile-name] header and contains key-value pairs beneath it.
# Lines starting with # are comments and are ignored
[target]
source = ~/projects/target/
target = /backups/target
exclude = .git
exclude = node_modules
exclude = *.log
exclude = .env
Keys
| Key | Required | Description |
|---|---|---|
source | ✅ Yes | The directory to sync from |
target | ✅ Yes | The directory to sync to (local path or user@host:/path) |
exclude | No | A file or folder pattern to skip. Repeat for multiple exclusions |
rsync_opts | No | Extra rsync flags, space-separated (e.g., --compress) |
Source path: trailing slash matters
A trailing / on the source path syncs the contents of the directory rather than the directory itself. This is almost always what you want.
# Syncs the CONTENTS of website/ into /backups/website/
source = ~/projects/website/
# Syncs the FOLDER itself, creating /backups/website/website/
source = ~/projects/website
Syncing a Profile
rsync-profiles <profile-name>
Example:
rsync-profiles target
This runs rsync with the settings defined in the [target] profile. You'll see output like:
▸ Profile: target
▸ Source: /Users/rob/projects/target/
▸ Target: /backups/target
▸ Excludes: .git node_modules *.log .env
▸ Running: rsync -avh --delete --exclude .git --exclude node_modules ...
sending incremental file list
src/main.py
src/utils.py
sent 4.2K bytes received 89 bytes 8.58K bytes/sec
total size is 48.3K speedup is 10.98
✓ Sync complete for [target]
Dry run — preview changes without syncing
Pass --dry-run to see exactly what would be transferred before committing:
rsync-profiles target --dry-run
This is highly recommended the first time you run a new profile.
Default rsync behavior
The script runs rsync with -avh --delete by default:
| Flag | Effect |
|---|---|
-a | Archive mode — preserves permissions, timestamps, and symlinks |
-v | Verbose output showing transferred files |
-h | Human-readable file sizes |
--delete | Removes files from the target that no longer exist in the source |
⚠️
--deleteis on by default. Files deleted from the source will also be deleted from the target. Use--dry-runfirst if you're unsure.
Excluding Files and Folders
Add one exclude line per pattern in your profile. Patterns follow standard rsync exclude rules.
[target]
source = ~/projects/target/
target = /backups/target
exclude = .git # Exact directory name
exclude = node_modules # Exact directory name
exclude = *.log # Wildcard: any file ending in .log
exclude = .env # Exact filename
exclude = dist/ # Trailing slash: only match directories
exclude = **/.DS_Store # Double wildcard: match in any subdirectory
Common exclusions to consider:
.git— version control historynode_modules— JavaScript dependencies (reinstallable)__pycache__,*.pyc— Python bytecode.env— environment files containing secrets*.log— log filesdist/,build/— generated output
Syncing to a Remote Server
Set target to a remote path using standard SSH syntax:
[target]
source = ~/projects/target/
target = [email protected]:/var/www/target
exclude = .git
exclude = node_modules
rsync_opts = --compress
rsync uses SSH under the hood. For passwordless syncing, ensure your SSH key is added to the remote server's ~/.ssh/authorized_keys.
You can also sync from a remote server to a local path by swapping source and target:
[pull-backups]
source = [email protected]:/var/backups/
target = ~/local-backups
Listing Profiles
See all configured profiles at a glance:
rsync-profiles --list
Output:
Configured profiles:
target ~/projects/target/ → [email protected]:/var/www/target (3 exclusions)
All Options
Usage: rsync-profiles <profile-name> [options]
rsync-profiles --list
rsync-profiles --init
Options:
--dry-run Preview what would be transferred without making changes
--verbose Show more detailed rsync output
--list List all configured profiles
--init Create a starter config file
-h, --help Show this help message
Tips and Troubleshooting
Profile not found
Make sure the profile name matches the header in your config exactly (case-sensitive). Run rsync-profiles --list to confirm the name.
Always dry-run a new profile first
Before running a sync on important data, use --dry-run to verify the source, target, and exclusions look correct.
Automate with cron
Run a profile on a schedule by adding it to your crontab (crontab -e):
# Sync website every day at 2am
0 2 * * * /usr/local/bin/rsync-profiles website >> ~/logs/rsync.log 2>&1
Using a shared config across machines Store your config file in a synced folder (Dropbox, iCloud, etc.) and point the script at it:
# Add to ~/.bashrc or ~/.zshrc
export RSYNC_PROFILES_CONFIG_DIR=~/Dropbox/config/rsync-profiles
https://github.com/robertz/rsync-profiles/tree/main
Cover Photo by Ato Aikins on Unsplash