-
Notifications
You must be signed in to change notification settings - Fork 0
Handoff to Munki
BootstrapMate is a bootstrapper, not a config-management system. It exists to get a freshly enrolled Mac to the point where Munki can take over, and then to get out of the way. This page covers what it leaves behind, how the handoff is arranged, and how to verify it happened.
After a run, the device has:
- Whatever your manifest installed — in a typical deployment, the Munki tools package and a
script that writes
/Library/Preferences/ManagedInstalls.plistwithSoftwareRepoURLandClientIdentifier. -
/Applications/Utilities/BootstrapMate.app, still installed. -
/Library/Managed Bootstrap/logs/<yyyy-MM-dd>/<HHmmss>/for the run, holdingbootstrap.log,events.jsonlandsession.json, plus/Library/Managed Bootstrap/status.jsonand/Library/Preferences/com.github.bootstrapmate.plist. -
No LaunchDaemon.
/Library/LaunchDaemons/com.github.bootstrapmate.plistis deleted and the job booted out at the end of every run, so launchd never fires it again. - Downloaded payloads, still on disk at each item's
filepath. BootstrapMate does not clean them up.
From that point on there is no BootstrapMate process, no timer, and no agent. Everything ongoing is Munki's.
Munki has a built-in mode for exactly this situation: an unattended, repeated
managedsoftwareupdate run at startup that keeps going until the device's manifest is fully
satisfied, showing the setup UI rather than the normal background behaviour. It is triggered
by the presence of one file:
/Users/Shared/.com.googlecode.munki.checkandinstallatstartup
While that file exists, Munki runs its check-and-install cycle at startup. Munki removes it itself once there is nothing left to install, and the device drops into its normal periodic schedule.
This is Munki's contract, not BootstrapMate's. Creating that file is the handoff.
BootstrapMate does not create the marker file. There is no preference key for it, no
manifest field for it, and no implicit behaviour. Nothing in BootstrapMate references
checkandinstallatstartup. If you want Munki bootstrap mode, a manifest item must create it.
The usual shape is a rootscript in the setupassistant stage, running after the Munki tools
package and after whatever configures ManagedInstalls.plist. Because the stage runs as root
before any user exists, /Users/Shared is writable and the script needs no elevation logic —
it touches the marker and exits.
A minimal manifest fragment, with your own hash and URL:
setupassistant:
- file: /Library/Application Support/BootstrapMate/munkitools.pkg
hash: <sha256>
url: https://example.com/bootstrap/packages/munkitools.pkg
name: Munki Tools
type: package
packageid: com.googlecode.munki.core
- file: /Library/Application Support/BootstrapMate/configure-munki.sh
hash: <sha256>
url: https://example.com/bootstrap/scripts/configure-munki.sh
name: Configure Munki
type: rootscriptPut the marker creation in configure-munki.sh, alongside writing SoftwareRepoURL and
ClientIdentifier, so the device is never in bootstrap mode without a repo to talk to.
Some deployments instead run managedsoftwareupdate directly from a manifest item rather than
relying on startup mode. If you do that in the userland stage, set donotwait: true on the
item: the daemon plist sets AbandonProcessGroup, which is specifically what keeps a
fire-and-forget child alive after BootstrapMate boots itself out. Without donotwait the run
blocks until the Munki run finishes.
Ordering, retries and skipping are covered in Stages, Retries and Timeouts and Conditions and Skipping.
Check the marker exists before the reboot that is meant to consume it:
ls -la /Users/Shared/.com.googlecode.munki.checkandinstallatstartup
Check Munki actually got configured, since the marker on its own does nothing useful:
/usr/libexec/PlistBuddy -c "Print :SoftwareRepoURL" /Library/Preferences/ManagedInstalls.plist
/usr/libexec/PlistBuddy -c "Print :ClientIdentifier" /Library/Preferences/ManagedInstalls.plist
Check that Munki has pulled manifests from the repo:
ls /Library/Managed\ Installs/manifests
Check what BootstrapMate itself thought happened, in the run log:
grep '\[OUTPUT\]' /Library/Managed\ Bootstrap/logs/*/*/bootstrap.log
The [OUTPUT] <scriptname>: lines are your configure script's own stdout and stderr, captured
into the BootstrapMate log — stdout at INFO, stderr at WARN. If your script logs what it did,
this is where you read it back.
Finally, confirm BootstrapMate is done rather than stuck: the absence of
/Library/LaunchDaemons/com.github.bootstrapmate.plist means a run completed and tore itself
down. That is the normal end state, not a problem.
Because the daemon is one-shot, a Mac that is already in production will not re-run
BootstrapMate on reboot. The preflight stage is the intended gate for the opposite case:
a single rootscript that inspects ManagedInstalls.plist (and BootstrapMate's own
/Library/Preferences/com.github.bootstrapmate.plist) and exits 0 when the device is already
on a production manifest, which skips the whole bootstrap. examples/preflight.sh in the
repository is a worked implementation of that check.
The same handoff on Windows targets Cimian instead of Munki: https://github.com/bootstrapmate/bootstrapmate-windows/wiki/Handoff-to-Cimian