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:

Written by abhi_mohata in 775857021
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:

Written by abhi_mohata in 775857021
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.

Written by abhi_mohata in 775857021
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:

Written by abhi_mohata in 775857021
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:

Written by abhi_mohata in 775857021
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.

Written by abhi_mohata in 775857021
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

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