The background agent
dotsync init installs a scheduled job that runs dotsync sync -q every 300 seconds. There’s no long-running daemon: each run starts, syncs, and exits. -q makes it silent, makes it exit at once if another sync is already running, and turns on desktop notifications.
| Platform | Mechanism | Defined in |
|---|---|---|
| macOS | launchd user agent {ref.defaults.agent_label} |
~/Library/LaunchAgents/{ref.defaults.agent_label}.plist |
| Linux with a systemd user session | user timer dotsync.timer |
~/.config/systemd/user/dotsync.service and dotsync.timer |
| Linux without one | a crontab line tagged {ref.defaults.cron_tag} |
your user crontab |
The job also runs when it’s loaded, which on macOS includes every login, and on systemd one minute after boot. Neither launchd nor systemd wakes a sleeping machine for it; syncing resumes when the machine wakes.
dotsync agent status # installed? loaded? running?dotsync agent install # install, or re-install after a changedotsync agent uninstallWhat gets installed
Section titled “What gets installed”<key>ProgramArguments</key><array> <string>/Users/you/.local/bin/dotsync</string> <string>sync</string> <string>-q</string></array><key>StartInterval</key><integer>300</integer><key>RunAtLoad</key><true/><key>ProcessType</key><string>Background</string><key>LowPriorityIO</key><true/><key>LowPriorityBackgroundIO</key><true/><key>Nice</key><integer>10</integer><key>StandardOutPath</key><string>/Users/you/.local/state/dotsync/agent.log</string><key>StandardErrorPath</key><string>/Users/you/.local/state/dotsync/agent.log</string><key>EnvironmentVariables</key><dict> <key>PATH</key><string>/opt/homebrew/bin:/usr/bin:/bin:…</string> <key>HOME</key><string>/Users/you</string></dict>[Unit]Description=dotsync: synchronize dotfilesAfter=network-online.target
[Service]Type=oneshotEnvironment=PATH=/home/you/.local/bin:/usr/local/bin:/usr/bin:/binEnvironment=HOME=/home/youExecStart=/home/you/.local/bin/dotsync sync -qNice=10CPUSchedulingPolicy=idleIOSchedulingClass=idle[Unit]Description=dotsync: periodic dotfile sync
[Timer]OnBootSec=1minOnUnitActiveSec=300sPersistent=true# A fixed schedule: no random delay. The 1-minute accuracy window lets systemd batch# this wake-up with others (fewer CPU wake-ups) at a fixed per-machine offset.RandomizedDelaySec=0AccuracySec=1min
[Install]WantedBy=timers.target*/5 * * * * PATH=/home/you/.local/bin:/usr/bin:/bin HOME=/home/you /home/you/.local/bin/dotsync sync -q >>/home/you/.local/state/dotsync/agent.log 2>&1 # dotsync-agentcron counts in whole minutes: an interval is rounded down to minutes (at least one), intervals of an hour or more run every whole number of hours, and a day or more runs daily at midnight.
The job runs ~/.local/bin/dotsync, the copy init installs, or the binary you ran agent install with if that copy doesn’t exist.
The job’s environment
Section titled “The job’s environment”launchd and cron don’t read your shell’s configuration, so dotsync writes the environment into the job itself when it installs it:
PATH: the directories of thePATHyou randotsyncfrom, followed by/usr/binand/bin, so the job finds the samegit,sshand credential helpers as your shellHOMEXDG_CONFIG_HOME,XDG_DATA_HOME,XDG_STATE_HOMEandXDG_CACHE_HOME, if you’ve set them
If you move git, or change any of these, run dotsync agent install again.
Keeping it current
Section titled “Keeping it current”dotsync update and the install script re-install the agent after replacing the binary, so its schedule, priority and environment always match the version you run. dotsync doctor warns when the installed definition was written by a different version (dotsync agent install fixes it). A different PATH alone doesn’t count.
Change the interval
Section titled “Change the interval”Set interval (in seconds) in ~/.config/dotsync/config.json, then run dotsync agent install. Or pass --interval when you first run init.
Resource use
Section titled “Resource use”The agent is built to go unnoticed on a laptop battery, and to be predictable.
When nothing changed, which is almost every run, a sync:
- runs 2 git processes: a
fetchand arev-parse - writes nothing except the state file’s modification time, which records when the last sync ran: no log line, no git index, reflog or
FETCH_HEAD, no state rewrite - reads no file contents: a file whose size, modification time, change time, inode and mode are unchanged since dotsync last hashed it isn’t read again. As in git, a file modified in the last two seconds is always re-read.
- takes around 65 ms, most of it the network round trip of the fetch, and peaks under 10 MiB of memory
A machine with a conflict or a blocked file logs that line on every sync, until you deal with it.
Priority. On macOS the job runs as a launchd Background process with low-priority I/O and Nice 10. On systemd it uses the idle CPU and I/O scheduling classes and Nice=10. The Go runtime runs on a single CPU (GOMAXPROCS=1) with a 64 MiB soft memory limit.
No randomness.
- Syncs run on a fixed interval, with no random delay.
- The systemd timer has
RandomizedDelaySec=0and a fixed one-minute accuracy window, so systemd can batch the wake-up with others. - When another machine pushes first, dotsync redoes its pass straight away, with no random back-off, up to 5 times.
- Housekeeping runs on a fixed calendar, not whenever git decides: backups are pruned at most once a day, the cache clone is fully reset at least once a day, and
git gcruns once a week. git’s own automatic gc and maintenance are turned off in the cache clone.
Git access without prompts
Section titled “Git access without prompts”The agent can’t type passwords or passphrases, so git ls-remote <your-url> must succeed without any prompt. dotsync runs git with GIT_TERMINAL_PROMPT=0, and ssh with BatchMode=yes and a 15-second connection timeout (unless you set GIT_SSH_COMMAND yourself), so a missing credential fails fast instead of hanging.
Store the key’s passphrase in the Keychain once, and let ssh load it automatically:
Host github.com AddKeysToAgent yes UseKeychain yes IdentityFile ~/.ssh/id_ed25519ssh-add --apple-use-keychain ~/.ssh/id_ed25519systemd user services and cron jobs usually can’t reach your desktop session’s ssh-agent. The simplest reliable option is a dedicated deploy key without a passphrase, allowed to write to this one repository only:
ssh-keygen -t ed25519 -N '' -f ~/.ssh/dotsync -C "dotsync@$(hostname)"Give the key its own host alias, so it’s only used for this repository:
Host dotsync-remote HostName github.com User git IdentityFile ~/.ssh/dotsync IdentitiesOnly yesAdd ~/.ssh/dotsync.pub as a deploy key with write access in the repository’s settings, and use the alias in the URL: dotsync init dotsync-remote:you/dotfiles.git.
Use a credential helper, and authenticate once interactively:
git config --global credential.helper osxkeychain # macOSgit config --global credential.helper libsecret # Linux desktopgh auth setup-git # or let the GitHub CLI do itIf you use gh auth setup-git and sync ~/.gitconfig, see tools that write absolute paths.
Check with dotsync doctor, which reaches the remote exactly as the agent does. When git fails in a way you need to fix, such as a rejected key, missing credentials or an unknown host key, doctor, status and the agent’s notification all name the problem and the fix.
Logs and notifications
Section titled “Logs and notifications”| Where | What |
|---|---|
~/.local/state/dotsync/sync.log |
every change, conflict and error, and one line for each sync that did something or whose outcome changed. Rotated to sync.log.1 at 1 MiB |
~/.local/state/dotsync/agent.log |
anything the job printed, with launchd and cron. dotsync prints nothing with -q unless a sync fails outright |
journalctl --user -u dotsync |
the same, with systemd |
The agent posts a desktop notification when a new conflict appears, and when git starts failing in a way you need to fix. It notifies once, not on every run. To turn notifications off on a machine, set "notifications": false in its configuration; the next sync respects it. The DOTSYNC_NO_NOTIFY environment variable also turns them off, for runs that inherit it.
- Linux: through
notify-send, with the dotsync icon. - macOS: through Notification Center, with the Script Editor icon. Install terminal-notifier (
brew install terminal-notifier) and dotsync uses it to show its own icon.