Scheduling¶
Dolly is designed to run unprivileged, under systemd --user, with all paths following the XDG base directory spec. There is no local state or lock file to manage — both live in the target LDAP — so scheduling is just "run dolly sync periodically."
systemd user timer (default)¶
dolly install writes these units, so you normally don't create them by hand. This is the default, a full sync of users and groups:
# ~/.config/systemd/user/dolly.service
[Unit]
Description=Dolly AD to LDAP sync
After=network-online.target
[Service]
Type=oneshot
TimeoutStartSec=1h
ExecStart=%h/.local/bin/dolly sync
# ~/.config/systemd/user/dolly.timer
[Unit]
Description=Run Dolly every 15 minutes
[Timer]
OnCalendar=*:0/15
RandomizedDelaySec=60
Persistent=true
[Install]
WantedBy=timers.target
dolly install --groups (or --users) bakes the flag into the service's ExecStart, for example for a groups-only deployment, and --config FILE adds the config's absolute path (a relative path is made absolute), which is also the path dolly install creates from the template if it's missing. Without --config, the service relies on the default config lookup order, since its working directory isn't where you ran dolly install:
dolly install --groups
# dolly.service then has:
# ExecStart=%h/.local/bin/dolly sync --groups
dolly install --groups --config ~/targets/ldap-a.yaml
# ExecStart=%h/.local/bin/dolly sync --groups --config /home/svc-dolly/targets/ldap-a.yaml
dolly install prints the resulting ExecStart= line, and re-running it with different flags rewrites the service. It doesn't enable the timer itself — it ends by printing the next steps (edit the config, dolly check, dolly sync --dry-run, then):
systemctl --user enable --now dolly.timer
loginctl enable-linger "$USER" # keep the timer running while you're logged out (may need an admin)
TimeoutStartSec=1h on the service keeps a hung run from blocking the timer forever. It is above the default run_timeout (45m), and dolly install warns if your config's run_timeout isn't below it. When it expires, systemd stops the service with SIGTERM (the default KillMode), which Dolly handles like run_timeout: it stops between operations, records the failure in cn=status, and releases the lock.
RandomizedDelaySec=60 on the timer spreads out runs when several hosts are on the same schedule, so they don't all fire in the same second.
Idempotent install¶
dolly install is safe to re-run:
- it never overwrites an existing
dolly.yaml(created with mode0600, in a0700directory), - it replaces the binary only when it differs from what's already installed (written to a temp file and renamed into place, mode
0755), - it only rewrites the systemd units when their content has actually changed,
- it runs
systemctl --user daemon-reloadon every install, so a re-run after a failed reload fixes it.
dolly uninstall is the undo: it stops and disables dolly.timer (fine if it isn't loaded), removes both units (and the links to them, when the user manager has another home, see below), reloads systemd, and removes ~/.local/bin/dolly — the config and any secrets next to it are kept.
Environment gaps in service-account setups¶
Service accounts reached via su - or sudo -iu often lack a full session environment. dolly install handles the common gaps:
XDG_RUNTIME_DIRunset. This is typical aftersu -orsudo -iu, and normally makessystemctl --userfail with "Failed to connect to bus." If/run/user/<uid>exists, Dolly setsXDG_RUNTIME_DIRandDBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<uid>/busfor its ownsystemctl --usercalls. If/run/user/<uid>doesn't exist, the account has no active session and no linger enabled: Dolly still installs the binary, config, and units, then exits1and tells you to runloginctl enable-linger <user>(may need an admin to run it for the service account) and re-rundolly install.~/.local/binmissing or not onPATH. Dolly creates the directory if needed, and warns (without failing) if it isn't onPATH. This doesn't matter for the timer itself, since the unit references the absolute path%h/.local/bin/dollyand never depends onPATH.- Login
$HOMEdiffers from the passwd home. On AD/SSSD hosts the login shell often hasHOME=/home/<user>while the passwd entry, and so thesystemd --usermanager, uses another directory such as/home/<DOMAIN>/<user>. The manager then looks for units in its own~/.config/systemd/user/and expands%hto its own home, so units written only under your$HOMEwould be "not found".$HOMEalways wins:dolly installasks the manager for itsHOMEandXDG_CONFIG_HOME(systemctl --user show-environment), keeps the binary, config, and units under your$HOMEas usual, and registers the two units withsystemctl --user link, so systemd creates symlinks in its own unit directory (Dolly writes nothing there itself).ExecStartthen uses absolute paths, the binary and--config(the default config's path if you didn't pass one), for exampleExecStart=/home/peter/.local/bin/dolly sync --config /home/peter/.config/dolly/dolly.yaml, and the install output has a note explaining the split. Re-runningdolly installrewrites the files in place (the links follow), re-creates a missing link, and leaves an existing regulardolly.serviceordolly.timerin the manager's directory alone with a warning.systemctl --user enable --now dolly.timerworks on linked units as usual.dolly uninstallremoves the links only if they point at Dolly's files. Ifshow-environmentfails, Dolly assumes the manager uses your$HOME. - Non-Linux hosts. Without systemd,
dolly installinstalls the binary and config, skips writing any units, and says so.
Dolly never edits shell startup files (.bashrc, .profile, and so on) to fix any of this.
Several targets from one host¶
There is no multi-target mode — each target LDAP server needs its own config file, state base, and run lock. To sync more than one target from a single host, keep one dolly.yaml per target and pass --config explicitly for each, either as separate dolly.service units (one per target, each with its own --config baked in by dolly install --config) or by wrapping calls to dolly sync --config /path/to/target-a.yaml and --config /path/to/target-b.yaml in your own scheduling. See Configuration.