Skip to content

Instantly share code, notes, and snippets.

@jczaplew
Last active June 6, 2021 05:41
Show Gist options
  • Save jczaplew/bf3a33c5a82a2d93bba4 to your computer and use it in GitHub Desktop.
Save jczaplew/bf3a33c5a82a2d93bba4 to your computer and use it in GitHub Desktop.
Setting up a LaunchDaemon with pm2

Setting up a LaunchDaemon with pm2

If you have a Node.js app running on an OS X server, you probably:

  • Want it to start automatically when the system boots
  • Want to use something like pm2 or forever to monitor the status of the app, log errors, and make sure it stays up

While pm2 has the ability to generate startup scripts for use with Ubunutu, Centos, and systemd, it does not work with OS X. The best approach is to use launchd, an OS X-specific daemon manager that replaces cron. It uses XML-based files for configuration. This guide will walk you through the process. Note: "xyz" is used as a placeholder for the application name throughout this guide.

Instructions

  1. Create a new LaunchDaemon config file for your app - sudo vi /Library/LaunchDaemons/org.node.xyz.plist
  2. Add the contents of org.node.xyz.plist, altering it with your configuration, and save (:wq) and exit
  3. sudo chown root:wheel /Library/LaunchDaemons/org.node.xyz.plist
  4. sudo chmod 644 /Library/LaunchDaemons/org.node.xyz.plist
  5. Find your path by typing echo $PATH
  6. sudo vi /etc/launchd.conf
  7. Add the contents of launchd.conf, but use whatever $PATH you just echoed, then save and exit
  8. sudo shutdown -r now - this will reboot the system. If you skip this step, the changes to launchd.conf will not take effect.
  9. Log back into the machine
  10. sudo launchctl load /Library/LaunchDaemons/org.node.xyz.plist
  11. Verify that it loaded correctly - sudo launchctl list | grep xyz. Three columns will be returned - PID, status, and label. If there is no value for PID, but there is a value for status, something went wrong.
  12. If everything went according to plan, you should now be able to pm2 list and see your Node app running. If you get an error, you might need to sudo chmod 777 /Users/<<me>>/.pm2/pm2.log.
setenv PATH /bin:/sbin:/usr/bin:/usr/sbin:/opt/local/bin:.:/opt/local/bin/:/usr/local/bin/
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.node.xyz</string>
<key>UserName</key>
<string>me</string>
<key>WorkingDirectory</key>
<string>/Library/WebServer/xyz</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/pm2</string>
<string>start</string>
<string>server.js</string>
<string>--name</string>
<string>xyz</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
@ffried
Copy link

ffried commented Feb 7, 2016

Great gist, thanks!
Just one little thing: instead of using /etc/launchd.conf to modify the PATH env-var I'd suggest using the solution described here: http://serverfault.com/a/146142/336252

@raine
Copy link

raine commented Feb 17, 2016

Just sudo pm2 startup darwin seems to work for me.

@larryebaum
Copy link

larryebaum commented Jul 15, 2016

Working with this project: https://github.com/Hackworth/VeraHomeKitBridge

I've switched from trying to get this working with forever over to pm2 (1.1.3) and just not getting any process listed for pm2. If I manually start with pm2 start ~/projects/VeraHomeKitBridge/app.js --name="Vera" I get the expected process but the app fails to work correctly. My plist is below.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>org.node.verahomekitbridge</string>
        <key>UserName</key>
        <string>MYUSERNAME</string>
        <key>WorkingDirectory</key>
        <string>/Users/MYUSERNAME/projects/VeraHomeKitBridge</string>
        <key>ProgramArguments</key>
        <array>
            <string>/usr/local/bin/pm2</string>
            <string>start</string>
            <string>app.js</string>
            <string>--name</string>
            <string>VeraHomeKitBridge</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
    </dict>
</plist>

If I stop the app.js within pm2 and manually start it with forever forever start app.js from within the project directory, the app works fine. Go figure...

So ideally I'd want the plist to work for forever, but no matter how I edit it, I can't seem to get it to start in forever...

Ideas?

@cunneen
Copy link

cunneen commented Dec 22, 2016

I had to edit /usr/local/bin/pm2 to change the first line:

#!/usr/bin/env node

To this:

#!/usr/local/bin/node

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment