macOS SSL certificate with Lego and LaunchDaemon
Lego runs on the Mac, fetches the certificate from FairSSL, and a sync script puts it in place for the service and restarts it.
How it works
- Install Lego The install command fetches Lego for Intel or Apple Silicon and places it in /usr/local/bin/lego.
- Create the sync script fairssl-lego-sync.sh runs as a deploy hook. Set the path and restart command for the service, for example nginx or Apache.
- Issue and schedule Run lego run against https://fairssl.dk/acme with the EAB keys and FairSSL AutoDNS, and let a LaunchDaemon run Lego daily. ARI drives renewal.
- Check Test the LaunchDaemon with launchctl, read /var/log/lego.log and lego.err, and test with FairSSL's SSL scanner.
macOS is rarely a public web server, but when it is, Lego plus a LaunchDaemon works beautifully. The whole setup is one Go binary plus two scripts, and it works on both Intel and Apple Silicon. We use Lego v5.0.4 or later for ARI support.
Supported versions
| Platform | Versions | Runs on | ACME client |
|---|---|---|---|
| macOS | VersionsThe guide sets no version limit | Runs onmacOS | ACME clientlego 5.0.4+ |
Setup
Step 1: Install Lego
The following command fetches the latest Lego release, detects the architecture and installs the binary
to /usr/local/bin/lego. It also creates the Lego storage folders.
sudo mkdir -p /usr/local/bin
arch=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')
version=$(curl -fsSL https://api.github.com/repos/go-acme/lego/releases/latest \
| sed -n 's/.*"tag_name": "\(v[^"]*\)".*/\1/p' | head -1)
curl -fL "https://github.com/go-acme/lego/releases/download/${version}/lego_${version}_darwin_${arch}.tar.gz" -o lego.tar.gz
tar -xzf lego.tar.gz lego
sudo install -m 0755 lego /usr/local/bin/lego
rm -f lego lego.tar.gz
sudo install -d -m 0755 /usr/local/etc/lego
sudo install -d -m 0700 /usr/local/etc/lego/accounts /usr/local/etc/lego/certificates Step 2: Create the sync script
Lego runs this script via --deploy-hook after every successful issuance or renewal. Adjust
the 4 variables at the top to match your environment. The restart command can be sudo nginx -s reload,
sudo apachectl -k graceful, or something more specific to whatever service you run.
sudo tee /usr/local/bin/fairssl-lego-sync.sh > /dev/null << 'SCRIPT'
#!/bin/bash
set -euo pipefail
TARGET="/usr/local/etc/ssl"
CERT_FILE="cert.pem"
KEY_FILE="key.pem"
RESTART_CMD="sudo nginx -s reload"
lego_name="$${1:-}"
cert_path="$${LEGO_HOOK_CERT_PATH:-}"
key_path="$${LEGO_HOOK_CERT_KEY_PATH:-}"
if { [ -z "$cert_path" ] || [ -z "$key_path" ]; } && [ -n "$lego_name" ]; then
cert_path="/usr/local/etc/lego/certificates/${lego_name}.crt"
key_path="/usr/local/etc/lego/certificates/${lego_name}.key"
fi
if [ -z "$cert_path" ] || [ -z "$key_path" ] || [ ! -s "$cert_path" ] || [ ! -s "$key_path" ]; then
echo "Missing Lego certificate files. Upgrade to Lego v5.0.4+ and check /usr/local/etc/lego/certificates."
exit 1
fi
sudo install -d -m 0755 "$TARGET"
sudo install -m 0644 "$cert_path" "$TARGET/$CERT_FILE"
sudo install -m 0600 "$key_path" "$TARGET/$KEY_FILE"
$RESTART_CMD
SCRIPT
sudo chmod +x /usr/local/bin/fairssl-lego-sync.sh Step 3: Issue the certificate
Make sure the domain is configured for FairSSL AutoDNS. Grab the EAB keys from the ACME tab in your FairSSL control panel and run Lego.
sudo /usr/local/bin/lego run \
--domains "www.example.com" \
--server https://fairssl.dk/acme \
--eab --eab.kid YOUR_EAB_KID --eab.hmac YOUR_EAB_HMAC \
--accept-tos --email acme-client@fairssl.dk \
--http --http.webroot "/Library/WebServer/Documents" \
--path "/usr/local/etc/lego" \
--deploy-hook "/usr/local/bin/fairssl-lego-sync.sh www-example-com" Step 4: Schedule renewal with LaunchDaemon
macOS uses LaunchDaemons rather than cron. Drop in this plist and Lego will run daily at a randomised
time. ARI decides when an actual renewal happens; --renew-days 7 is the fallback.
sudo tee /Library/LaunchDaemons/com.fairssl.lego-www-example-com.plist > /dev/null << 'EOF'
<?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>com.fairssl.lego-www-example-com</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>/usr/local/bin/lego run --domains "www.example.com" --server https://fairssl.dk/acme --accept-tos --email acme-client@fairssl.dk --http --http.webroot "/Library/WebServer/Documents" --path "/usr/local/etc/lego" --renew-days 7 --deploy-hook "/usr/local/bin/fairssl-lego-sync.sh www-example-com"</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>4</integer>
<key>Minute</key>
<integer>17</integer>
</dict>
<key>StandardOutPath</key>
<string>/var/log/lego.log</string>
<key>StandardErrorPath</key>
<string>/var/log/lego.err</string>
<key>RunAtLoad</key>
<false/>
</dict>
</plist>
EOF
sudo chown root:wheel /Library/LaunchDaemons/com.fairssl.lego-www-example-com.plist
sudo chmod 644 /Library/LaunchDaemons/com.fairssl.lego-www-example-com.plist
sudo launchctl load -w /Library/LaunchDaemons/com.fairssl.lego-www-example-com.plist Step 5: Verify and debug
- Test the LaunchDaemon manually:
sudo launchctl start com.fairssl.lego-www-example-com - Check logs:
tail -f /var/log/lego.log /var/log/lego.err - Verify the certificate with FairSSL's SSL scanner.
- To debug the ACME flow itself, prefix the command with
LEGO_DEBUG_ACME_HTTP_CLIENT=true.
Certificates on many servers and appliances
A publicly trusted SSL certificate can be valid for at most 199 days now, 99 days by 15 March 2027 and 46 days by 15 March 2029. Each certificate then has to be replaced at least twice a year now and at least eight times a year from 2029, on every server, firewall and load balancer that uses it. sslbrain from FairSSL runs as an appliance in your own network and renews and installs the certificates from one place, including on devices without an agent.
sslbrain compared with Certbot, win-acme and lego(opens sslbrain.com in a new tab)Frequently asked questions
Find answers to the most common questions about SSL certificates and FairSSL.
launchd via a LaunchDaemon is the recommended way to schedule background jobs that need to run regardless of user session. Our example installs a LaunchDaemon in /Library/LaunchDaemons that runs Lego daily at a randomised time.uname -m) and downloads the correct Lego binary. Lego supports both amd64 (Intel) and arm64 (Apple Silicon).fairssl-lego-sync.sh) runs via Lego's --deploy-hook after every successful issuance or renewal. Adjust the script with the path your service expects and a restart command. The script ships example restart commands for nginx, Apache and generic services.renew command is gone. lego run checks ARI and only renews if needed. You can force renewal with --renew-days 7, which our example uses as a fallback if ARI does not respond./usr/local/etc/lego/accounts, and future renewals reuse it without EAB. Keep the keys somewhere safe as a backup in case you ever need to rebuild the setup.Ready to automate macOS certificates?
Create a free account and order your first certificate. A DV certificate is issued in under 2 minutes.