Running Caddy as a service on macOS X server

Installing CaddyCaddy logo

Download Caddy from the official website.

Give the executable root permissions1:

$ chown root ./caddy
$ chmod +s ./caddy

Caddyfile

Caddy reads it configuration for the Caddyfile. More information can be found in the official documentation.

Launchd config

Create a launchd plist file: ~/Library/LaunchAgents/com.caddyserver.web.plist. This file should look like this:

<?xml version=”1.0" encoding=”UTF-8"?>
<!DOCTYPE plist PUBLIC “-//Apple Computer//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version=”1.0">
    <dict>
        <key>Label</key>
        <string>com.caddyserver.web</string>
        <key>EnvironmentVariables</key>
        <dict>
            <key>HOME</key>
            <string>/Users/mathias</string>
        </dict>
        <key>ProgramArguments</key>
        <array>
            <string>sh</string>
            <string>-c</string>
            <string>launchctl unload -w /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons/com.apple.serviceproxy.plist; ulimit -n 8192; cd /Users/mathias/Sites; ./caddy -agree -email my_email@domain.com -conf=/Users/mathias/Sites/Caddyfile</string>
        </array>
        <key>UserName</key>
        <string>www</string>
        <key>RunAtLoad</key>
        <true/>
        <key>KeepAlive</key>
        <true/>
        <key>WorkingDirectory</key>
        <string>/Users/mathias/Sites</string>
        <key>StandardOutPath</key>
        <string>/Users/mathias/Sites/caddy.log</string>
        <key>StandardErrorPath</key>
        <string>/Users/mathias/Sites/caddy_error.log</string>
    </dict>
</plist>

The launchd script will stop the built-in Apache server on macOS X, set the ulimit and execute Caddy. The Caddy command line arguments are quite trivial:

  1. -agree: agree to the CA’s (Let’s Encrypt) Subscriber Agreement.
  2. -email: your email address used for the certificates authority.
  3. -conf: full path to your Caddyfile.

The launchd file will make sure that Caddy is started at boot and kept alive.

Start and Stop Caddy launchd service using the following commands:

$ launchctl load ~/Library/LaunchAgents/com.caddyserver.web.plist
$ launchctl unload ~/Library/LaunchAgents/com.caddyserver.web.plist

  1. Yes, I know, it’s not good practice to run a service as root. But there is no other decent way on OS X. setcap doesn’t exist on macOS. Since Caddy won’t be touching any files, and Caddy doesn’t execute shell script, there is not a lot that can go wrong in this case. If you feel uncomfortable running Caddy as root, you could use portforwarding in your firewall and listen on a port >= 1024, for which you won’t need root privileges. This can be done using this command:
    echo "
    rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080
    " | sudo pfctl -ef -