How-to: manage schedules
Install, inspect, and repair native daily or weekly follower synchronization, including time zone and daylight-saving behavior.
Canonfig uses the operating system's user scheduler: a systemd user timer on Linux, a launchd user agent on macOS, and Task Scheduler on Windows. There is no resident Follower Machine daemon. Every native job runs:
canonfig sync --apply --no-input --scheduledA follower has no schedule until you choose one on that follower. Applying a profile never installs, re-enables, or recreates a scheduled job.
Set a schedule
Synchronize every day at 09:00 in this machine's time zone:
canonfig schedule set daily@09:00On Linux, synchronize on chosen weekdays in a named IANA time zone:
canonfig schedule set weekly:Mon@12:30 --timezone Europe/Paris
canonfig schedule set weekly:Mon,Thu@12:30Selecting a calendar requires an enrolled follower, including an explicit daily
or weekly calendar. A fresh machine or Source-only installation is refused
before any native job is installed. Pending enrollment, including credential
rotation that retained an older calendar, must be finalized first; status reports
enrollment-pending and doctor does not treat that calendar as ready.
Only Linux supports --timezone. Named IANA zones are canonicalized (for example,
europe/berlin becomes Europe/Berlin); numeric offsets such as +05:30 are
rejected before saving the schedule choice. macOS and Windows reject an explicit named
zone because their native calendar jobs cannot preserve that binding.
Use a daily or weekly schedule without --timezone on those platforms.
It follows the machine's local time zone.
Weekdays are Mon, Tue, Wed, Thu, Fri, Sat, and Sun; the time is
24-hour HH:mm. Cron and platform-native expressions are not accepted.
schedule set prints the resolved timezone, the nextRun time in UTC, and any
warnings. Setting the same schedule again is idempotent.
If the scheduler must run a specific Canonfig installation, pass it:
canonfig schedule set daily@09:00 --executable /opt/canonfigOtherwise the job runs the Node.js runtime and entrypoint of the Canonfig you
ran. On macOS with Homebrew Node, the job uses the stable
<brew prefix>/opt/<formula>/bin/node link rather than a version-specific
Cellar path, so a Node upgrade does not break it.
Accept the profile's suggested schedule
A Machine Profile may carry a scheduleDefault. It is only a suggestion: sync
never installs it. When the follower has made no schedule decision, sync --plan, sync --apply, and schedule status report it, for example
"schedule available: the profile suggests daily@04:30 in this machine's time
zone; run canonfig schedule set --default to install it". To accept it:
canonfig schedule set --default--default takes no calendar, --timezone, or --executable. It fails with a
usage error when the selected profile has no default or nothing has been
applied yet.
Time zones, daylight saving, and missed runs
-
Local time follows the machine. Without
--timezone, the job runs at that wall-clock time in the machine's current time zone. On Linux,--timezonebinds that time to the named zone instead. macOS and Windows use local time and reject--timezone; they do not silently discard it. -
A time that has already passed today does not run now. The next run is the next occurrence. (Earlier releases started a catch-up run immediately.)
-
Missed runs. On Linux, a run missed while the machine was off or the user manager was not running starts once when the timer starts again, not once per missed day. launchd and Task Scheduler do not catch up when you run
schedule set. -
System time zone changes. systemd recomputes the next run. If the new zone's occurrence falls between the last run and now, it runs once immediately. This is native systemd behavior; the apply it starts is idempotent.
-
macOS time zone changes during a login session. launchd may keep the previous zone even if you reinstall the job. Canonfig reports
timezone-changedinstead of a predicted next run and refusesschedule setuntil you reboot the Mac and sign in again. The selected calendar remains recorded; checkschedule statusafter the reboot. This does not describe daylight-saving clock changes within the same time zone. -
Accuracy. systemd timers use its default accuracy of one minute, so a run can start up to a minute after the scheduled time.
-
Daylight-saving gaps. A local time can be missing on the day clocks move forward, for example 02:30 in Europe/Madrid on 2027-03-28, or 00:00 in America/Santiago on the first Sunday of September.
schedule setscans the next 12 months of your scheduled days and warns: "<HH:mm>does not exist in<zone>on<dates>(daylight-saving change)". It also says what the native scheduler does:- systemd skips the run on those days and runs again on the next scheduled day;
- launchd does not document this case, so the run may be skipped or start after the clock change;
- Task Scheduler leaves this case unspecified, so the run may be skipped or start after the clock change.
Choose a time outside the change window, such as 04:00 or later, to run on every scheduled day.
-
Repeated hours on the day clocks move back are not checked or warned about, and Canonfig makes no claim about how each native scheduler handles them.
Make sure the job can run
| Platform | Runs while logged in | Runs while logged out |
|---|---|---|
| Linux | yes | only with lingering: loginctl enable-linger "$USER" |
| macOS | yes, in the desktop session with the login Keychain unlocked | no |
| Windows | yes | no |
On Linux, doctor and status warn when lingering is off: "the systemd user
manager stops when you log out (Linger=no), so the job only runs while you are
logged in; run loginctl enable-linger <user> to run it while logged out".
On Windows, the task uses an interactive logon, and doctor and status always
warn: "the Task Scheduler task uses an interactive logon, so it only runs while
you are logged on to Windows; a run due while you are logged off or the machine
is off is skipped, not caught up". When the most recent due run did not start,
they add a warning naming it: "the scheduled run at <time> did not start
(last start <time>): the cause is not established from task history". The state stays current because the task is still
armed for its next run. Run canonfig sync --apply to catch up by hand.
Windows tasks can start on battery, and unplugging AC power does not stop an
active sync. Existing tasks with restrictive battery settings are reported as
drifted; run canonfig sync --apply manually to repair the definition. This does
not prevent sleep or shutdown and does not remove the interactive-logon requirement.
A follower that reaches its Source through a managed tunnel also needs that tunnel. A scheduled run restarts a tunnel that went down once before fetching. See Run Canonfig unattended.
Inspect a schedule
canonfig schedule statusStatus reads the effective native state (for systemd, systemctl --user show,
including drop-ins), not only the files Canonfig wrote. It reports the
timezone, nextRun, the last fire, and one of these states:
| State | Meaning | What to do |
|---|---|---|
not-selected | No schedule decision on this follower | schedule set <calendar> or schedule set --default |
current | The native job matches the chosen schedule | nothing |
drifted | The calendar or the runtime binding differs | schedule set <calendar>; binding-only drift after an upgrade is re-rendered by the next sync --apply |
timezone-changed | macOS changed zones since boot; launchd may keep the previous zone | reboot, sign in to the desktop session, then check schedule status |
overridden | A native override (such as a systemd drop-in) changes the job | remove the override and run systemctl --user daemon-reload, or schedule remove and manage the job yourself |
disabled, inactive, not-installed | The job was disabled, stopped, or deleted outside Canonfig | see below |
unmanaged | A native job remains without an active follower decision, including after unenrollment | If unenrolled, enroll and apply a profile first, or use schedule remove. An enrolled follower can use schedule set --default or schedule set <calendar> |
doctor fails the scheduler probe for every state that means the job will not
run as chosen, and reports the last unattended run: "the last unattended run
failed at <time>: <reason>; the last completed one was at <time>". The
status completion receipt uses the same text, so a failed scheduled run is
visible without reading scheduler logs.
Disabled or deleted outside Canonfig
Canonfig respects an out-of-band disable. If you disable, stop, or delete the native job yourself, status and doctor say "automation disabled outside Canonfig" and name the cause. Sync leaves the job as it is. Then either:
canonfig schedule set daily@09:00
canonfig schedule removeschedule set restores the job. schedule remove confirms manual operation and
records that decision, so later applies keep it removed. schedule set refuses
while a native override exists.
Remove a schedule
canonfig schedule removeRemoving a schedule that is already absent is an unchanged result.
Reconcile deleted source skills
Keep authored skill resources aligned with observed Source Machine deletions without publishing automatically.
How-to: run Canonfig unattended
Keep the Source serving and followers syncing without a person at the keyboard, including after a reboot, on each supported platform.