skip to content

On macOS, what is the difference between a Launch Agent and a Launch Daemon, and how does that choice change what the job is able to do?

level: juniorimportance: must knowfreq 80%

answer

  1. one of them runs before anyone logs in
  2. session or no session
  3. root in the system domain
  4. per-login-session copies for the other
  5. LaunchDaemons versus LaunchAgents directories

basics

~20 s

A Launch Daemon runs in the system domain from boot, normally as root, with no login session and no access to a user's screen. A Launch Agent runs inside a logged-in user's session, as that user, and can reach the GUI.

solid answer

~40 s

Both are launchd jobs described by a plist; the difference is the domain launchd loads them into. A Launch Daemon lives in `/Library/LaunchDaemons` (Apple's own in `/System/Library/LaunchDaemons`), is loaded into the system domain at boot before anyone logs in, and runs as root unless the plist sets `UserName`. Having no login session, it cannot talk to the window server, so a daemon that tries to show UI just fails. A Launch Agent lives in `/Library/LaunchAgents` (for every user) or `~/Library/LaunchAgents` (for one user), and launchd starts a separate copy per login session, running as that user with their environment, keychain and GUI access. Rule of thumb: must run with nobody logged in, or needs root, means daemon; needs the user's identity or screen means agent.

go deeper

for a junior

Be able to say plainly that daemons are system-wide and start at boot as root, while agents run per logged-in user in that user's session, and name the two /Library directories.

for a middle

Explain the domain model behind the split — system domain versus a per-user domain that exists only between login and logout — and why a daemon has no window server connection.

for a senior

Show the production judgment: pick a service account with UserName instead of leaving a helper as root, and use the daemon-plus-agent split with XPC when a feature needs both privilege and a user prompt.

for a principal

Own the tradeoff for a fleet: which components genuinely need system-domain privilege, how helpers are registered and updated (installer-dropped plists versus SMAppService), and how user-toggleable background items on recent macOS affect deployability.

## Domains, not directories `launchd` is PID 1 on macOS: the kernel starts it and it starts everything else. It does not keep one global job list — it keeps *domains*, and a job only exists inside one. The two you meet constantly are the **system domain**, which exists from boot until shutdown, and a **per-user domain**, created when a user logs in and torn down when they log out. "Launch Daemon" and "Launch Agent" are simply the names for a job loaded into one or the other. The directory a plist sits in is how launchd decides which domain to use; it is a consequence of the distinction, not the distinction itself. ## Where the plists live A job is a property list whose filename is conventionally its `Label` plus `.plist`. - `/System/Library/LaunchDaemons` and `/System/Library/LaunchAgents` — Apple's own jobs. These are protected by System Integrity Protection; you do not edit them. - `/Library/LaunchDaemons` — third-party system-wide daemons. Loaded at boot, before login. Owned by root. - `/Library/LaunchAgents` — third-party agents that should exist for *every* user; one instance per login session. - `~/Library/LaunchAgents` — agents for that one user only. ## What each can actually do A daemon starts early, so it can hold a privileged port, manage a device, or run a background service that must survive logout and fast user switching. The price is isolation: there is no window server connection, no user keychain, no `$HOME` belonging to a human, and no Aqua session. Attempts to draw UI or read a user's login keychain from a daemon fail, and that is by design rather than a bug you can configure away. An agent is the mirror image. It runs as the user, inside their session, so it may show a window, post a user notification, read the login keychain, and see the user's environment. It cannot do anything the user cannot do, and it does not exist while that user is logged out. A daemon runs as root unless you set `UserName` and `GroupName` in the plist. Running a third-party daemon as root when it does not need root is the classic security finding in a macOS review — an unprivileged service account is almost always the right answer. ## The split-helper pattern When a feature needs both privilege and UI — a VPN client, a backup tool, an updater — the shipping shape is two jobs: a Launch Daemon doing the privileged work and a Launch Agent doing the talking to the user, communicating over XPC or a Mach service the daemon advertises. Trying to collapse that into one job is how people end up with a root process poking at a user's session, which is exactly the boundary macOS is drawing. ```xml <key>Label</key><string>com.example.sync</string> <key>UserName</key><string>_examplesync</string> ``` ## The modern wrinkle On macOS 13 and later, third-party daemons and agents surface to the user as background items they can switch off, so "I installed the plist and it never ran" now has a plausible answer that has nothing to do with your plist being wrong. Apple also offers `SMAppService` as the supported way for an app to register its own helper rather than dropping files into `/Library` from an installer script. ## The interview point The question is really testing whether you understand that macOS ties privilege and session together deliberately: root without a session (daemon), or a session without root (agent). Candidates who answer "the folder name is different" have memorised the surface; candidates who say "a daemon has no window server connection, so it cannot ask the user anything" have actually shipped one.

  • A privileged helper needs to ask the logged-in user to approve something. How do you structure that?
    Ship two jobs: a Launch Daemon that holds the privilege, and a Launch Agent running in the user's session that shows the UI. The daemon advertises a Mach service (a `MachServices` entry) and the agent connects to it over XPC. The daemon never tries to draw anything itself.
  • If two users are logged in simultaneously, how many copies of an agent in /Library/LaunchAgents are running?
    One per login session, so two — each running as its own user in its own per-user domain. That matters for anything that assumes exclusive ownership of a file, a port or a lock: an agent must tolerate a sibling copy running as somebody else at the same time.
  • What user does a Launch Daemon run as if the plist says nothing about it?
    root. Set `UserName` (and usually `GroupName`) to drop it to a dedicated service account unless the job genuinely needs root — for example to bind a low port, touch system state, or manage a device. An unnecessary root daemon is a standard audit finding.

saying these in an interview costs you the question

  • Thinks a Launch Daemon can show a dialog to the user
  • Says agents and daemons differ only by folder name
  • Believes a Launch Agent starts at boot before login
  • Assumes a job in ~/Library/LaunchAgents runs as root
  • Expects one agent instance no matter how many users log in

context