Configuration
lazyrsync stores everything under $XDG_CONFIG_HOME/lazyrsync/ (typically ~/.config/lazyrsync/):
profiles.toml— your profiles and taskssettings.toml— preferences (theme, hints, confirmation prompts)
If XDG_CONFIG_HOME is unset, the path falls back to ~/.config/lazyrsync/profiles.toml.
settings.toml
skip_delete_warning = false # skip the alert when you enable a task's delete flag
skip_run_confirm = false # skip the confirmation before a run starts
skip_remove_confirm = false # skip the confirmation before removing a profile or task
hints = true # show the keybinding hints
rsync_path = "rsync" # which rsync binary to run
[theme]
accent = "lightblue" # focus, selection, active field
on_accent = "black" # text on the selection bar
secondary = "lightcyan"
border = "gray" # unfocused panel borders
muted = "darkgray" # labels, dim text
added = "green" # new files, done, fresh tasks
modified = "yellow"
deleted = "red" # deletes, failures, warnings
warn = "yellow"
bytes = "blue" # byte counts in the diff summaryEvery prompt has its own opt-out, all false by default. skip_run_confirm removes the last prompt before a transfer, including for tasks that use --delete.
Each theme value is an ANSI color name ("lightblue", "gray", …) or a hex string ("#5fafff"). ANSI names inherit your terminal's palette; hex pins an exact color.
You can also toggle hints live with Ctrl+g. lazyrsync writes a last_profile key here too, to reopen on the profile you used last.
Hand edits are safe. When the TUI saves, it rewrites only hints and last_profile and leaves every other key — plus your comments and formatting — exactly as it found them.
Unknown keys are an error here, the same as in profiles.toml: a misspelled setting stops lazyrsync with the offending key named, rather than being silently ignored and defaulted.
Choosing which rsync runs
By default lazyrsync runs the first rsync on your $PATH. Point rsync_path at a specific binary to override that:
rsync_path = "/opt/homebrew/bin/rsync"Two cases where you want it. On macOS 15.4 and later, /usr/bin/rsync is openrsync and may come ahead of Homebrew's rsync. And in any launch context with a minimal $PATH — cron especially — where the rsync your shell resolves isn't the one lazyrsync would find.
The configured binary is what lazyrsync spawns, and what the resolved command shown in the TUI and by lazyrsync list reports.
profiles.toml
The TUI writes this file for you, but it is a supported hand-editable format — useful for provisioning with Ansible or a dotfiles repo alongside headless runs. Every key below is shown with its default:
[[profile]]
name = "backups" # required
description = "nightly" # ""
[[profile.task]]
label = "photos" # required
source = "/home/me/Pictures/" # required
dest = "/mnt/nas/pics/{now:%Y-%m-%d}/"
id = "photos-3f2a" # auto-generated from label + source/dest
action = "sync" # sync | snapshot
[profile.task.flags]
archive = true
compress = true
verbose = true
human = true
progress = true
partial = true
delete = false # destructive — mirrors deletions to dest
delete_excluded = false # destructive — deletes excluded files at dest
backup = false
update = false
checksum = false
size_only = false
existing = false
ignore_existing = false
bwlimit_kbps = 0 # 0 = unlimited
hardlinks = false
acls = false
xattrs = false
[profile.task.filters]
excludes = []
includes = []
exclude_from = ""
include_from = ""
files_from = ""
filter = []
[profile.task.ssh]
port = 22
keyfile = ""
extra = ""
[profile.task.advanced]
raw_args = ""A profile holds one or more tasks; every section except [[profile]], label and source may be omitted. lazyrsync also writes created and last_files bookkeeping keys, which you can leave out.
With action = "snapshot", dest is the parent directory: lazyrsync picks the next numbered subdirectory and builds the --link-dest chain to the previous one at run time. See Sync vs Snapshot.
Unknown keys are an error
A misspelled key would otherwise be dropped in silence and its default used in place — for excludes that means running with no exclusions at all, which on a delete = true task mirrors away everything you meant to skip. lazyrsync instead refuses to load the file and names the offending key:
$ lazyrsync run backups
error: parsing /home/me/.config/lazyrsync/profiles.toml: TOML parse error at line 13, column 1
|
13 | exclude = ["node_modules/"]
| ^^^^^^^
unknown field `exclude`, expected one of `excludes`, `includes`, `exclude_from`, `include_from`, `files_from`, `filter`One consequence: a profiles.toml written by a newer lazyrsync may fail to load on an older binary rather than being partially ignored.
Dynamic paths
Source and destination paths can contain placeholders, resolved every time the task runs — so one saved task can write to a new dated folder each night:
| Placeholder | Expands to |
|---|---|
{now} | today's date, 2026-07-27 |
{now:FORMAT} | any strftime format, e.g. {now:%Y/%m/%d} or {now:%H%M} |
{utcnow}, {utcnow:FORMAT} | the same in UTC |
{hostname} | this machine's hostname |
{user} | the current user |
$VAR, ${VAR} | an environment variable |
~ | your home directory |
dest = "~/backups/{hostname}/{now:%Y-%m-%d}/"Unknown placeholders, unset variables and a bare % are left exactly as typed, and the dry-run preview always shows the resolved path before anything runs. Braces and $ are escaped by doubling them — is a folder literally named {now}, and $$HOME a folder named $HOME.
These resolve on every run, headless included.