What does it mean for a macOS launchd job to be launched on demand through its Sockets dictionary, and how does the program end up holding the listening socket?
answer
- someone else opens the door for you
- the port exists before the program does
- the plist key is how you ask for it
- no more service start ordering
- the inetd job launchd took over
basics
~20 slaunchd creates and binds the listening socket itself at load time and watches it. The job's process is started only when a connection arrives, and it receives the already-bound file descriptors from launchd by checking in with launch_activate_socket, keyed by the name used in the plist.
solid answer
~50 sA `Sockets` dictionary tells launchd to own the listening endpoint on the job's behalf. At load time launchd creates and binds the socket described there — service name or port, socket type, family, or a Unix socket path — and simply listens. The job's program is not running. When a client connects, launchd starts the program and hands it the already-bound descriptors; the program calls `launch_activate_socket()` with the same key it used in the plist to collect them, then accepts as normal. Two things fall out. The port is reserved from boot, so clients never get connection refused during a startup race, which removes most of the need for dependency ordering between services. And an idle service can exit and cost nothing until the next connection. This is the `inetd` role that launchd absorbed, and it is the same idea XPC services use with Mach ports instead of sockets.
code
c · 13 lines#include <launch.h>
#include <stdlib.h>
int listener_fds(int **fds, size_t *count)
{
/* "Listener" must match the key in the plist's Sockets dictionary. */
int err = launch_activate_socket("Listener", fds, count);
if (err != 0) {
return err;
}
/* accept() on (*fds)[i]; free(*fds) when finished. */
return 0;
}go deeper
Know that launchd can open a listening socket on a job's behalf and start the program only when a client connects, so the service is not running while nothing is using it.
Explain the check-in step: the plist names the socket, launchd binds it at load, and the program retrieves the descriptors by that name rather than binding anything itself.
Show the consequences you would design around — no connection-refused race, ordering dependencies dissolved, idle memory reclaimed — and name the KeepAlive mistake that throws all of it away.
Own the model choice across a set of helpers: which are resident and which are demand-launched, what that means for latency on first use versus steady-state memory and battery, and where an XPC split buys real isolation.
## The idea Normally a server binds its own listening socket, which means the socket does not exist until the server is up. That single fact causes two familiar problems: a client that connects during startup gets refused, and every service that depends on another needs to be ordered after it. launchd inverts the ownership. You describe the endpoint in the plist, launchd binds it at load time, and the endpoint exists from boot whether or not your code is running. ```xml <key>Sockets</key> <dict> <key>Listener</key> <dict> <key>SockServiceName</key><string>12345</string> <key>SockType</key><string>stream</string> </dict> </dict> ``` `Listener` here is just a name you choose — the key by which your program later asks for the descriptors. The inner dictionary describes the socket: `SockServiceName` for a port or service name, `SockType` for stream or datagram, `SockFamily`, `SockNodeName` to bind a specific address, or `SockPathName` for a Unix-domain socket. ## Check-in When a connection arrives on a socket launchd holds, launchd starts the job. The program does not bind anything; it asks launchd for what it already has: ```c int *fds = NULL; size_t count = 0; int err = launch_activate_socket("Listener", &fds, &count); ``` On success you get an array of file descriptors — more than one is normal, for example IPv4 and IPv6 — which you then `accept()` on like any other listening socket. The string must match the plist key. A program that ignores check-in and calls `bind()` itself will fail with the address already in use, because launchd is holding it: the two models cannot be mixed. ## What you get for it - **No connection-refused race.** The endpoint is up from boot. A client connecting to a service whose process has never run simply blocks briefly while launchd starts it. - **Dependency ordering becomes unnecessary.** Because demand is what starts things, you do not need to sequence service A after service B; A connects, and B is started because of it. This is the core reason launchd has no runlevels or ordered start scripts. - **Idle services cost nothing.** A job that exits when it has been idle for a while frees its memory, and launchd relaunches it on the next connection. On a laptop that is a real saving, not a theoretical one. ## The mistake that undoes it Adding `KeepAlive` to a socket-activated job. launchd restarts it the moment it exits, so the process is permanently resident and you have kept all the complexity of check-in while losing the benefit. Either the job is on-demand and exits when idle, or it is a resident supervised daemon — not both. Similarly, a job that never exits when idle is on-demand only for the first connection after boot. ## The Mach and XPC side Sockets are the network-facing case. The same pattern exists for Mach IPC: a `MachServices` dictionary in the plist has launchd register named Mach ports for the job, and the job is started when a client sends it a message. That is the mechanism underneath XPC. An XPC service is a small helper bundle inside an application, launched on demand by launchd when the app opens a connection to it, running in its own process with its own sandbox and entitlements. Nobody starts it manually and its lifetime is managed for them — which is exactly why applications split parsing of untrusted data into an XPC service: a crash or a compromise lands in a separate, restartable, tightly sandboxed process rather than in the app. ## The interview point The question is checking whether you understand that on-demand launch is an *architectural* choice, not a config flag: ownership of the endpoint moves to the init system, and in exchange the ordering problem disappears and idle services stop costing memory. Candidates who can also say why `KeepAlive` ruins it have understood the mechanism rather than memorised the key.
- Why does socket activation remove most of the need to order service startup?Because the endpoint exists before the provider does. A consumer connects, launchd starts the provider because of that connection, and the consumer's connect simply takes slightly longer instead of failing. Ordering only matters when a client can observe a missing endpoint, and launchd makes sure it cannot.
- What happens if a socket-activated job also sets KeepAlive to true?launchd restarts it as soon as it exits, so it becomes permanently resident. You keep the check-in complexity and lose the on-demand benefit entirely — no memory reclaimed when idle. Decide which model the service is: on-demand and exiting when idle, or a supervised long-running daemon.
- How does this relate to XPC services inside an application?Same mechanism with Mach ports instead of sockets. launchd registers the service's named port and starts the helper process when the app opens a connection to it. The helper runs in its own process with its own sandbox, which is why apps push risky work such as parsing untrusted input into an XPC service.
saying these in an interview costs you the question
- Has the program bind the port itself as well
- Thinks launchd starts the job at boot and just holds the port open
- Expects exactly one descriptor from check-in
- Adds KeepAlive to a job meant to exit when idle
- Believes clients get connection refused before the job first runs