Canonfig
How-to Guides

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 --scheduled

A 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:00

On 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:30

Selecting 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/canonfig

Otherwise 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, --timezone binds 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-changed instead of a predicted next run and refuses schedule set until you reboot the Mac and sign in again. The selected calendar remains recorded; check schedule status after 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 set scans 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

PlatformRuns while logged inRuns while logged out
Linuxyesonly with lingering: loginctl enable-linger "$USER"
macOSyes, in the desktop session with the login Keychain unlockedno
Windowsyesno

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 status

Status 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:

StateMeaningWhat to do
not-selectedNo schedule decision on this followerschedule set <calendar> or schedule set --default
currentThe native job matches the chosen schedulenothing
driftedThe calendar or the runtime binding differsschedule set <calendar>; binding-only drift after an upgrade is re-rendered by the next sync --apply
timezone-changedmacOS changed zones since boot; launchd may keep the previous zonereboot, sign in to the desktop session, then check schedule status
overriddenA native override (such as a systemd drop-in) changes the jobremove the override and run systemctl --user daemon-reload, or schedule remove and manage the job yourself
disabled, inactive, not-installedThe job was disabled, stopped, or deleted outside Canonfigsee below
unmanagedA native job remains without an active follower decision, including after unenrollmentIf 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 remove

schedule 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 remove

Removing a schedule that is already absent is an unchanged result.

On this page