Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

MacOS Application as a daemon or in non-interaction mode

We are building a 'server' application that can either run as a daemon or can run in background without showing any GUI. Basically, the end user can either configure this to run as a daemon so that it can be tied to the user's session or will launch the process which user will start and quit as needed.

I wanted to understand what is the recommended mechanism for such an application from Apple -

  1. Should this application be built as a macOS Bundle ? Apple documentation also says that we should not daemonize the process by calling fork. Hence if we create a unix-style executable, will I not need to fork to make it run in a detached state when I launch the executable via double-click ? [Reference Link]
  2. Is it fine to have an application on macOS which is a bundle but does not show any UI when launched by double click on the app-icon or via 'open'? While we have been able to achieve this by using NSApplicationMain and not showing the UI, was wondering if using CFRunLoop is best for this case as it is a non-gui application.

If we can get the right documentation link or recommendations on how we should build such an application which can run in a non-gui mode and also in a daemonized manner, it will help us.

Should the application be always built as a macos bundle or should it be a unix-style executable to support both the cases - by the same application/product and how should we look at the distribution of such applications.

Answered by DTS Engineer in 827778022

First up, terminology. On Apple platforms an application, or app for short, is a GUI program that the user launches. I’m going to use the term server program for what you’re calling an “application”.

The best way to handle this is to create a standard macOS app — with a GUI that you can launch in the Finder — and then embed your server program within that. See Placing Content in a Bundle for advice on how to structure this.

How you proceed from there depends on the specific details of your product. My preferred option would be:

  • Add a status and control UI in the GUI app.

  • In that UI, use SMAppService to install your server program as either a daemon or an agent.

  • And similarly for uninstall.

However, other approaches might make sense. For more specific advice I need to know what you mean by:

Basically, the end user can either configure this

If by “end user” you mean a normal user installing your product on their own Mac then the above approach is definitely the best option. OTOH, if the “end user” is a site admin in a managed environment, who configures your product for all members of their organisation, other options might make sense.

As to whether your server program needs to be in a bundle structure, in many cases that’s not necessary. The one case where it’s absolutely necessary is if it uses restricted entitlements. See Signing a daemon with a restricted entitlement. In that case, the nesting looks like this:

MyApp.app/
    Contents/
        MacOS/
            MyApp
            MyServer.app/
                Contents/
                    MacOS/
                        MyServer
                    … other stuff …
        … other stuff …

Coming back to your specific questions:

will I not need to fork to make it run in a detached state when I launch the executable via double-click?

No, you’re way off in the weeds here. If you want your server program to run in the background, set things up so that it’s started by launchd. You can choose to have it run as either a daemon (in the global context) or an agent (in a user context).

SMAppService makes this easy. There are other options that are older and less pleasant, but more flexible.

Is it fine to have an application [that] does not show any UI when launched … ?

At a technical level, yes. The issue is with the user experience. Mac users do not expect apps to just ‘disappear’ in the background when you launch them. That’s a horrible UE.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

First up, terminology. On Apple platforms an application, or app for short, is a GUI program that the user launches. I’m going to use the term server program for what you’re calling an “application”.

The best way to handle this is to create a standard macOS app — with a GUI that you can launch in the Finder — and then embed your server program within that. See Placing Content in a Bundle for advice on how to structure this.

How you proceed from there depends on the specific details of your product. My preferred option would be:

  • Add a status and control UI in the GUI app.

  • In that UI, use SMAppService to install your server program as either a daemon or an agent.

  • And similarly for uninstall.

However, other approaches might make sense. For more specific advice I need to know what you mean by:

Basically, the end user can either configure this

If by “end user” you mean a normal user installing your product on their own Mac then the above approach is definitely the best option. OTOH, if the “end user” is a site admin in a managed environment, who configures your product for all members of their organisation, other options might make sense.

As to whether your server program needs to be in a bundle structure, in many cases that’s not necessary. The one case where it’s absolutely necessary is if it uses restricted entitlements. See Signing a daemon with a restricted entitlement. In that case, the nesting looks like this:

MyApp.app/
    Contents/
        MacOS/
            MyApp
            MyServer.app/
                Contents/
                    MacOS/
                        MyServer
                    … other stuff …
        … other stuff …

Coming back to your specific questions:

will I not need to fork to make it run in a detached state when I launch the executable via double-click?

No, you’re way off in the weeds here. If you want your server program to run in the background, set things up so that it’s started by launchd. You can choose to have it run as either a daemon (in the global context) or an agent (in a user context).

SMAppService makes this easy. There are other options that are older and less pleasant, but more flexible.

Is it fine to have an application [that] does not show any UI when launched … ?

At a technical level, yes. The issue is with the user experience. Mac users do not expect apps to just ‘disappear’ in the background when you launch them. That’s a horrible UE.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It’s better to reply as a reply, rather than in the comments; see Quinn’s Top Ten DevForums Tips for this and other titbits.

I would also want to build, deploy and distribute them 'separately'. Is this approach okay?

Well, kinda. There’s nothing to stop you splitting this functionality into two separate components but, if you ship MyServer as a separate product, how is the user going to:

  • Install it?

  • Manage it? [1]

  • Uninstall it?

There are two common ways to ship daemon-ish functionality on the Mac:

  • Embedded it in a container app, as I’ve described above

  • As an installer package

In the second case, the installer takes care of:

  • Installing the daemon in the right place

  • Installing the appropriate launchd property list

  • Telling launchd about the new job

  • Installing the management app, if appropriate, in /Applications

That’s a bunch of extra hassle but it certainly does make sense in some situations. For example, site managers really like installer packages because they’re easy to deploy via MDM systems.

If the user installs MyServer.app independently, now MyServer can also be launched from Finder.

Right. Standard practice is for your installer to put the daemon into a location that’s not immediately obvious to the user, like /Library/Application Support/XYZ or /usr/local/xyz, where XYZ is something that’s unique to your company or your product. That way the user is unlikely to launch it from the Finder.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] You said you want to give the user the option to run it as a daemon or an agent, but how does the user choose that? And how do they stop and start it?

Thanks Quinn. This has been immensely helpful and insightful.

I still did not have a view on how the user would be managing the service component, and your response has led to a good provocation.

I have a few followup questions so that I can take an informed decision on which path I should take for my product(s).

  1. What about someone using MacOS as a ‘server machine’ and administering it using ‘ssh’ only (i.e. there is no GUI session to this machine right now) ? What kind of application build will allow that to work ? Our observation is that if a bundled application i.e. a GUI program is run on an ssh environment without having a GUI session, the application crashes. The reason of the crash as per my understanding has been that in a non-gui environment, if any 'UI Code' (in this case, if any AppKit API) gets invoked, it crashes.
  2. What about possible ‘headless systems’ where MacOS is the ‘preferred OS to act as a server machine’ ? While MacServer is a discontinued product line, are these no longer considered ‘possible environments’ ? In such environments, would we STILL use a ‘Bundled App’ ?

Basically, the same 'server program' is expected to be used by the user in the above setups or may have a GUI macOS environment which is more common to have.

Also, while Server Program embedded in the GUI Program is seeming a more recommended path by using SMAppService. However, SMAppService is supported from macOS 13+ as per documentation. For my product, we are targeting macOS 11 as our minimum supported version. So would the 'installer package' option be our only choice then (apart from the above questions which can also impact the choice).

Thanks!

Regards, Abhishek

Let’s start with the concept of a headless machine. If you want to support those, you need to create a launchd daemon. These run in the global context, that’s created at system startup and continues until the system shuts down.

Such daemons can’t use GUI frameworks. When you think about it, that makes sense. If multiple GUI users are logged in, or no GUI users are logged in, what would the GUI frameworks do?

I talk about this idea in detail in the super-old-but-still-surprisingly-relevant TN2083 Daemons and Agents.

In terms of installation:

  • If the system ever has a GUI login then it’s fine to install and manage the daemon with a GUI app.

  • If you’re targeting systems where the user never logs in on the GUI then you typically want to install your daemon using an installer package.

You can, of course, support both.

In terms of management, there are two common ways to do that on a completely headless system:

  • Provide a command-line tool, à la apachectl.

  • Embed a webserver, à la cupsd.

And, again, you can do both.

Oh, and there’s also the time-honour Unix tradition of requiring the user to manually edit complex text files and then send obscure signals to your daemon to have it re-read its configuration. I dunno about you, but I’m not a fan of that tradition |-:

SMAppService is supported from macOS 13+

Right. Its direct predecessor was SMJobBless, but it’s probably not a good match for your requirements.

SMJobBless installs a launchd daemon, but it does that by copying the daemon’s executable from your app bundle to a fixed location (/Library/PrivilegedHelperTools). This makes sense for folks using a launchd daemon for privilege escalation, but it doesn’t make a lot of sense for folks who want a more full-featured daemon. Such a program would likely require frameworks, shared libraries, resources, and so on. SMJobBless doesn’t make it easy to have those [1].

would the 'installer package' option be our only choice

It’s not the only choice — you could use SMJobBless, you could have the usually the installation manually, you could use SMJobBless to bootstrap the privilege necessary to install the rest of your product, and so on — but it’s probably the easiest option. It’s also an option that makes sense to the sorts of users who have headless machines.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] With SMAppService you typically configure the service to run your daemon directly from your app bundle. That works on macOS 13 and later because the app bundle is protected from un-mediated modification (see app bundle protection in Trusted Execution Resources). SMJobBless runs on old systems, without this feature, so it has to copy the executable to a known protected location.

MacOS Application as a daemon or in non-interaction mode
 
 
Q