guideonce.
← All guides

Reference · Jamf Pro · macOS

Jamf Binary Command Reference and Recovery Sequences

The day-to-day commands worth knowing, the diagnostic pattern for "is this device actually managed?", and the recovery sequence for when the framework is broken and you need to rebuild it.

The jamf binary on a managed Mac is the operational tool you reach for when something is not behaving as it should. Most of what Jamf Pro does is initiated from the server side, but when a device is silent, not checking in, refusing a policy, or shows the wrong state in the console, the answer almost always lives in what the local binary thinks is true. This post is the reference for that, plus the recovery sequences for when the framework is broken badly enough that the binary itself is part of the problem. Same template-with-variables pattern as the macOS MDM command reference if you want the broader macOS administration toolkit.

Where the Binary Lives and What to Know First

The jamf binary on a managed Mac lives at /usr/local/jamf/bin/jamf, with a symlink at /usr/local/bin/jamf for convenience. Historically (Jamf Pro before about 9.82) it lived at /usr/sbin/jamf, which you may still find referenced in older scripts. If which jamf finds nothing, the binary is genuinely missing, which is itself useful information: the device is either not enrolled, was enrolled and then offboarded, or has a broken framework.

Template
# Confirm the binary is present and which version
which jamf
jamf -version

# Get the help for any verb
jamf help
jamf help <verb>

Almost everything from this point on needs sudo, because the jamf binary writes to system locations, reads protected data, and interacts with the system MDM stack. Running without sudo will fail silently or produce confusing errors.

Important Jamf file locations

Useful to know when investigating: /Library/Application Support/JAMF/ (framework binaries and state), /Library/LaunchDaemons/com.jamfsoftware.*.plist (the daemons that run check-ins), /var/log/jamf.log (the local Jamf log), and /Library/Preferences/com.jamfsoftware.jamf.plist (the JSS URL and per-device config).

Triggering Inventory and Check-In

Two of the most common things you will trigger from the local binary are an inventory submission (so the Jamf console sees the current state of the device) and an MDM check-in (so any pending MDM commands actually arrive).

Templates
# Force an inventory submission (Jamf's word for it is "recon")
sudo jamf recon

# Confirm the JSS is reachable
sudo jamf checkJSSConnection

# Trigger a recurring check-in immediately
sudo jamf policy

# Manage the framework (re-applies management files, re-runs login/logout hooks etc)
sudo jamf manage

jamf recon is the workhorse. It collects hardware, software, scripted extension attribute results, and usage data and submits all of it. If a smart group or scope rule depends on something Jamf collects at inventory, the device will not appear in scope until after recon has run. If you have just made a change and the device is not picking up the policy you expect, your first move is almost always to run recon.

jamf checkJSSConnection verifies the device can reach the JSS URL. Output of "The JSS is available" confirms basic network reachability before you start blaming anything else.

Running Policies and Custom Triggers

The jamf policy verb signals to the Jamf server that something has happened, and pulls down whatever policies are scoped to fire on that event. Without arguments it runs the recurring check-in. With an event flag it runs the specific built-in trigger. With a custom trigger name it runs whatever policies are scoped to that custom trigger.

Templates
# Recurring check-in
sudo jamf policy

# Built-in event triggers
sudo jamf policy -event startup
sudo jamf policy -event login
sudo jamf policy -event logout
sudo jamf policy -event enrollmentcomplete
sudo jamf policy -event networkstatechange

# Custom trigger (the most useful pattern in practice)
sudo jamf policy -trigger <custom-trigger-name>

# Run a specific policy by ID
sudo jamf policy -id <policy-id-number>
Examples
# Re-run a deployment scoped to a custom trigger called "install-zoom"
sudo jamf policy -trigger install-zoom

# Trigger something that has been set up to run on login
sudo jamf policy -event login

# Useful diagnostic: see verbose output for what just happened
sudo jamf policy -verbose
Custom triggers are a design pattern, not just an option

If you find yourself repeatedly running a policy by hand to debug it, give the policy a custom trigger like reinstall-app-x or refresh-config-y, then you (and any other admin) can re-run it on any device with one command: sudo jamf policy -trigger reinstall-app-x. Combined with the API or remote shell, this is the foundation of operational scripting against Jamf-managed fleets.

Reading What the Device Thinks Is True

Before trying to fix anything, work out what the device currently believes about its own management. The configuration file and the framework log between them will usually tell you.

Templates
# Read the JSS URL the device thinks it is enrolled to
defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url

# Full Jamf preference dump (useful for spotting unexpected values)
defaults read /Library/Preferences/com.jamfsoftware.jamf.plist

# Show recent activity from the framework
sudo tail -n 100 /var/log/jamf.log
sudo tail -f  /var/log/jamf.log    # follow live during a check-in

# Are the Jamf daemons loaded and running
sudo launchctl list | grep jamfsoftware

The jss_url read is the single highest-value diagnostic: if a device is silently reporting to a stale or wrong JSS URL (for example after a Jamf tenancy migration where some devices never picked up the new URL), it will look completely disconnected from the new server and entirely normal on the old one. defaults read exposes this immediately. The same pattern, dropping jss_url for any other key, lets you read any specific Jamf configuration value.

The launchctl check is the next thing to look at if commands seem to do nothing. Jamf relies on its launch daemons being loaded; if they are not, scheduled check-ins will not happen even though the binary itself is fine. The expected daemons include com.jamfsoftware.jamf.daemon and com.jamfsoftware.task.* entries.

Forcing an MDM Command Sync

The Jamf framework and macOS's MDM stack are related but separate. Jamf policies (software installs, scripts, configuration profiles delivered via the Jamf binary) come down through the Jamf framework. Configuration profiles delivered via MDM commands (which is most of what you actually set in modern Jamf Pro) arrive through macOS's MDM stack. If a profile in the Jamf console shows as Pending but never lands on the device, the issue is the MDM stack rather than Jamf.

Templates
# Jamf-side: tell the device to ask Jamf for any pending MDM commands
sudo jamf mdm

# macOS-side: poke the MDM stack to ask Apple's push service for queued commands
sudo profiles renew -type enrollment

# Confirm enrolment status from macOS's point of view
sudo profiles status -type enrollment

If you push a configuration profile in Jamf and it does not arrive, run both. jamf mdm tells the Jamf framework to look for pending MDM operations to relay; profiles renew tells macOS to check Apple's push service for delivered commands. Between them they cover the most common reason something is "stuck" in Pending.

Self Service and Policy Debugging

When a Self Service policy is not showing up for a user, or shows but does not run when clicked, the diagnostic flow is straightforward.

  1. Confirm the device is in scope. In the Jamf console, look at the policy's Scope tab and confirm the device or smart group is listed. If the device joined a smart group based on a recent change, it may not actually be in scope yet (smart group membership is calculated when inventory comes in, so run sudo jamf recon first).
  2. Confirm the user is in scope. Self Service policies often have user-level scope (LDAP/Entra group, specific user). If the device is in scope but the user is not, the policy will not show.
  3. Confirm Self Service is the latest version. Old Self Service builds can lose sync with new server features. Reinstalling Self Service from the console fixes this.
  4. Run the policy directly with the custom trigger. If the policy has a custom trigger configured, sudo jamf policy -trigger <name> tests the policy itself in isolation from Self Service. If this works, the issue is Self Service or scope; if it fails, the issue is the policy.
  5. Watch the log live. In another terminal: sudo tail -f /var/log/jamf.log. The framework log shows exactly what happened, including the script output and exit codes.

Logs and Diagnostic Information

Where things live
PathWhat it contains
/var/log/jamf.logFramework activity, policy runs, script output, errors. The first log to look at.
/var/log/jamf-self-service.logSelf Service-specific activity (if present).
/Library/Application Support/JAMF/Receipts/Receipts of policies and packages that have already run on this device.
/Library/Application Support/JAMF/tmp/Working directory the framework uses during policy execution. Failed packages can leave artefacts here.
/Library/Preferences/com.jamfsoftware.jamf.plistThe device's own Jamf configuration: JSS URL, check-in interval, etc.
/Library/LaunchDaemons/com.jamfsoftware.*The launch daemons that drive scheduled check-ins.
Templates
# Watch the framework log while you trigger something
sudo tail -f /var/log/jamf.log

# Look at what policies have already run on this device
ls -la "/Library/Application Support/JAMF/Receipts/"

# Increase Jamf log verbosity for one command
sudo jamf policy -trigger <trigger-name> -verbose

Scripts and Extension Attributes

When a policy script or extension attribute is misbehaving, you usually want to run it locally to see exactly what it does in your environment.

Templates
# Run a script the way Jamf does (positional parameters mountPoint, computerName, username)
sudo bash <path-to-script>.sh "/" "$(scutil --get ComputerName)" "$(stat -f '%Su' /dev/console)"

# Read the result of an extension attribute (the way Jamf reads it)
sudo <path-to-extension-attribute-script>

Jamf passes three positional parameters to policy scripts by default: the mount point (usually /), the computer name, and the currently logged-in user. Knowing this lets you reproduce a script run exactly as the policy will, which is invaluable when debugging "works on my machine" issues.

Removing the Framework (Destructive)

This is genuinely destructive

The commands in this section uninstall the Jamf framework from the device. They are the right answer for stuck-state recovery, leaver workflows, and migration scenarios, but they are not reversible without re-enrolling the device. Always make sure you have a way to re-enrol the device (MDM redeploy, QuickAdd, or a planned re-enrollment workflow) before running these, or you will be left with a Mac that is hard to bring back into management.

Templates
# Remove the Jamf framework but leave the MDM profile in place
sudo jamf removeFramework

# Remove the MDM profile (separate operation, separate verb)
sudo jamf removeMdmProfile

# Delete a management account created by Jamf
sudo jamf deleteAccount -username <account-name> -deleteHomeDirectory

The order matters. removeMdmProfile first if you want a clean removal, then removeFramework. If you do removeFramework first, the binary is gone and you cannot then run removeMdmProfile through it, leaving the MDM profile orphaned on the device. (This is recoverable but is the most common self-inflicted situation in this whole post.)

When the framework is so broken the binary itself is gone

If jamf as a command is not found and you cannot get a working binary onto the device, the recovery path is to remove the orphaned MDM profile via profiles directly:

# List the profiles to find the Jamf MDM profile identifier
sudo profiles -P

# Remove a specific profile by identifier
sudo profiles -R -p "<profile-identifier>"

# Or remove all profiles installed by MDM (heavy hammer)
sudo profiles -D -f

If the device was enrolled via DEP/Automated Device Enrolment, you cannot fully remove the MDM profile this way: the device will re-enrol on next reboot. For DEP devices, the offboarding sequence has to happen in Apple Business Manager (release the device from your MDM server), or the device needs to be erased and re-set-up if you want a fully unmanaged state.

Rebuilding the Framework

If a device is still MDM-enrolled but the Jamf framework is broken (binary missing, daemons not loading, framework corrupted), the cleanest fix is to redeploy the framework via the API. This relies on a working MDM enrolment, since it uses an MDM "Install Application" command to push the framework binary back to the device.

API endpoint
POST /api/v1/jamf-management-framework/redeploy/<computer-jss-id>

You can call this from the Jamf API documentation page (https://<your-jamf-server>/api/doc/#/jamf-management-framework): authenticate, enter the device's JSS ID, and execute. A successful response means the MDM command has been queued; the device will pick it up on its next push notification and the binary will be re-installed. From the device side, check after about 5-10 minutes that which jamf returns a path and that sudo jamf checkJSSConnection succeeds.

If the device has no working MDM enrolment either, the only path forward is a fresh enrolment via QuickAdd package or a re-enrolment workflow (re-running through ADE, or redirecting via DEP if the device is supervised).

Common Scenarios and What to Try First

Device appears in the Jamf console but inventory is days out of date

Run sudo jamf recon on the device. If that fails, run sudo jamf checkJSSConnection to confirm reachability. If reachable but recon still fails, look at /var/log/jamf.log for the specific error. If unreachable, the issue is network or the device is pointing at a stale JSS URL (check defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url).

Policy is scoped to the device but never runs

Three possible causes in order of likelihood:

  1. The device is not in scope yet (smart group membership recalculates at inventory time). Run sudo jamf recon and try again.
  2. The policy has a frequency that already counts as completed for this device (Once per computer for example). Check the Jamf console for the policy's execution log.
  3. The policy is set to run only on a trigger you have not triggered. Run with the right event or custom trigger.

Self Service is empty or showing the wrong items

Sign out and back in to Self Service. If still wrong, the device's user is not in scope or the device itself is not. Confirm via console, run recon, retry.

Configuration profile pushed from Jamf but not arriving

This is an MDM-stack issue, not a Jamf framework issue. Run both sudo jamf mdm and sudo profiles renew -type enrollment. If still not arriving, check sudo profiles status -type enrollment for the MDM enrolment state; a "not enrolled" or "broken" status means the underlying enrolment is broken and pushing more profiles will not help until it is repaired.

jamf binary is missing

If which jamf finds nothing on a device that should be managed, the framework is gone but the MDM enrolment may still be intact. Redeploy the framework via the API (see Rebuilding the Framework). If the MDM enrolment is also broken, you will need a fresh enrolment.

Framework removed but profiles still present

This is the "removeFramework before removeMdmProfile" mistake. The fix: re-install the framework (QuickAdd package, or API redeploy if MDM is still in place), then run sudo jamf removeMdmProfile cleanly, then sudo jamf removeFramework in the correct order.


Quick-Action Checklist

For when something is wrong and you need to triage fast. Each step links back to the section above with the detail.

  1. Confirm the binary exists: which jamf. If missing, jump straight to rebuilding the framework.
  2. Confirm reachability: sudo jamf checkJSSConnection. "JSS is available" is the green light.
  3. Confirm you are talking to the right server: defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url. Stale URLs are common after tenancy migrations.
  4. For inventory issues, run sudo jamf recon. This is required for any scope or smart group based on something Jamf collects at inventory.
  5. For "policy not running" issues, run the policy directly with its custom trigger: sudo jamf policy -trigger <name>, with -verbose if you want detail. Watch /var/log/jamf.log live in another terminal.
  6. For "configuration profile stuck Pending" issues, run both sudo jamf mdm and sudo profiles renew -type enrollment. If still stuck, the MDM enrolment itself is the problem.
  7. For Self Service issues, confirm device and user scope before anything else, then sign out and back in.
  8. Before removing the framework, make sure you have a route back: API redeploy is available, or you have a QuickAdd package. Always remove the MDM profile first, then the framework.
  9. If you have already removed the framework first by mistake, reinstall it (QuickAdd or API redeploy), then do the removal in the correct order.
  10. When nothing else helps, look at /var/log/jamf.log and /Library/Application Support/JAMF/Receipts/. They will tell you what actually happened, not what was supposed to happen.

Closing Thoughts

The jamf binary is the kind of tool that feels intimidating until you spend a week using it daily, after which it feels like the most boringly reliable thing in your fleet management toolkit. The verbs are predictable, the logs are clear, the file locations are stable, and the recovery sequences (with one important caveat about ordering) work. The most useful habit is to keep a terminal open with sudo tail -f /var/log/jamf.log while you do anything Jamf-related; you will see exactly what the framework thinks, exactly when it thinks it.

← All guides