[Bug]: Laravel Herd using wrong user group in php-fpm config
Platform
macOS
Operating system version
MacOS Tahoe 26.0.1
System architecture
Intel (x86)
Herd Version
1.22.3
PHP Version
No response
Bug description
By default, PHP-FPM assumes that the current macOS user belongs to the system group called staff. However, in some environments — especially managed or MDM-enrolled Macs — users are not part of the staff group. This causes 502 Bad Gateway errors in Nginx because PHP-FPM sockets can’t be accessed by the running user.
You can manually fix this by editing the PHP-FPM config for each PHP version as described here beyondcode/herd-community#10 (comment)
The problem is that Herd regenerates these config files every time you switch PHP versions, which means the manual changes are lost and have to be redone.
It would be great if Laravel Herd handled this automatically — it’s a small issue but a major headache for affected users.
Until that’s built in, here’s a simple workaround (courtesy of ChatGPT 😄) that automatically patches the PHP-FPM configs whenever Herd regenerates them.
🧰 Herd FPM Admin Patch — Fix PHP-FPM Group Issues Automatically on macOS
📖 Overview
This small utility fixes a recurring problem with Laravel Herd where PHP-FPM sockets are created with the wrong group permissions, resulting in 502 Bad Gateway errors when switching PHP versions.
It installs two simple components:
- A Bash script (
herd-fpm-admin-patch) — fixesgrouppermissions in Herd PHP-FPM config files. - A LaunchAgent — automatically runs the patch every time Herd regenerates its config.
After installation, your setup will self-heal whenever Herd changes PHP versions — no more manual edits.
🧠 The problem
When you switch PHP versions in Herd, it regenerates FPM pool configs such as:
~/Library/Application Support/Herd/config/fpm/8.4-fpm.conf
These contain:
group = staff
listen.group = staff
listen.mode = 0660
On many macOS setups — especially managed or MDM-enrolled Macs — your user account is not in the staff group.
When this happens, PHP-FPM fails to create its socket, and Nginx returns:
502 Bad Gateway
connect() to unix:/Users/.../herd84.sock failed (61: Connection refused)
💡 What this fix does
The patch ensures all Herd PHP-FPM configs use a valid group your user belongs to (e.g., admin).
Specifically, it:
- Changes
→group = staff listen.group = staffgroup = admin listen.group = admin - Keeps
listen.mode = 0660 - Restarts Herd so changes take effect.
It’s safe, idempotent, and automatic.
🛠️ How to install
Copy and paste the following command block into your terminal:
# 1️⃣ Create the patch script
sudo tee /usr/local/bin/herd-fpm-admin-patch >/dev/null <<'SH'
#!/bin/sh
# herd-fpm-admin-patch (macOS)
# Patches Herd PHP-FPM pool files to use a specific socket group & mode, then restarts Herd.
#
# Env overrides:
# HERD_GROUP=admin # which group should own the FPM socket
# HERD_MODE=0660 # socket mode
#
# Usage:
# HERD_GROUP=admin HERD_MODE=0660 sh herd-fpm-admin-patch
#
# Description:
# - Scans all Herd PHP-FPM pool config files (e.g., 8.3-fpm.conf, 8.4-fpm.conf)
# - Replaces "group" and "listen.group" with HERD_GROUP
# - Ensures "listen.mode" matches HERD_MODE
# - Restarts Herd after applying changes
#
# Works around an issue where Herd sets group=staff by default, breaking sockets
# for users who aren’t in the staff group.
set -eu
HERD_GROUP="${HERD_GROUP:-admin}"
HERD_MODE="${HERD_MODE:-0660}"
HERD_BIN="$HOME/Library/Application Support/Herd/bin/herd"
echo "Scanning for FPM pool files…"
LIST="$(mktemp)"
find "$HOME/Library/Application Support/Herd" "/Library/Application Support/Herd" -maxdepth 6 -type f -name "*fpm*.conf" 2>/dev/null | sort -u > "$LIST" || true
[ -s "$LIST" ] || { echo "No *fpm*.conf files found."; rm -f "$LIST"; exit 0; }
CHANGED=0
while IFS= read -r f; do
[ -f "$f" ] || continue
echo "Checking: $f"
tmp="${f}.tmp.$$"
/usr/bin/awk -v G="$HERD_GROUP" -v M="$HERD_MODE" '
BEGIN { g=0; lg=0; lm=0 }
/^[[:space:]]*group[[:space:]]*=/ { print "group = " G; g=1; next }
/^[[:space:]]*listen\.group[[:space:]]*=/ { print "listen.group = " G; lg=1; next }
/^[[:space:]]*listen\.mode[[:space:]]*=/ { print "listen.mode = " M; lm=1; next }
{ print }
END {
if (!g) print "group = " G;
if (!lg) print "listen.group = " G;
if (!lm) print "listen.mode = " M;
}' "$f" > "$tmp"
if ! cmp -s "$f" "$tmp"; then
[ -w "$f" ] && mv "$tmp" "$f" || { echo " (not writable; using sudo)"; sudo mv "$tmp" "$f"; }
echo " → Patched"
CHANGED=1
else
rm -f "$tmp"
echo " (no changes)"
fi
done < "$LIST"
rm -f "$LIST"
if [ "$CHANGED" -eq 1 ]; then
echo "Changes applied. Restarting Herd services…"
if [ -x "$HERD_BIN" ]; then
"$HERD_BIN" stop && "$HERD_BIN" start || echo "Patched configs; restart via the Herd app."
else
echo "Herd CLI not found at: $HERD_BIN"
open -g -a Herd || true
fi
else
echo "No changes needed."
fi
SH
sudo chmod +x /usr/local/bin/herd-fpm-admin-patch
# 2️⃣ Create a LaunchAgent that auto-runs when Herd changes configs
mkdir -p "$HOME/Library/Logs" "$HOME/Library/LaunchAgents"
cat > "$HOME/Library/LaunchAgents/com.herd.fpm-admin-patch.plist" <<PLIST
<?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.herd.fpm-admin-patch</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-lc</string>
<string>/usr/local/bin/herd-fpm-admin-patch</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>WatchPaths</key>
<array>
<string>$HOME/Library/Application Support/Herd/config/fpm</string>
<string>$HOME/Library/Application Support/Herd/fpm</string>
</array>
<key>StandardOutPath</key>
<string>$HOME/Library/Logs/herd-fpm-admin-patch.out</string>
<key>StandardErrorPath</key>
<string>$HOME/Library/Logs/herd-fpm-admin-patch.err</string>
</dict>
</plist>
PLIST
launchctl unload "$HOME/Library/LaunchAgents/com.herd.fpm-admin-patch.plist" 2>/dev/null || true
launchctl load "$HOME/Library/LaunchAgents/com.herd.fpm-admin-patch.plist"
echo "✅ Installed herd-fpm-admin-patch. It will now auto-fix FPM configs whenever Herd rewrites them."
🧾 Verify the installation
Run:
launchctl list | grep herd-fpm
You should see:
com.herd.fpm-admin-patch
Check logs:
tail -n 20 ~/Library/Logs/herd-fpm-admin-patch.out
🧹 To uninstall
launchctl unload ~/Library/LaunchAgents/com.herd.fpm-admin-patch.plist
rm -f ~/Library/LaunchAgents/com.herd.fpm-admin-patch.plist
sudo rm -f /usr/local/bin/herd-fpm-admin-patch
💡 Recommendations for Herd maintainers
- Detect if the user belongs to
staffbefore settinggroup = staff. - Default to the user’s primary group if not.
- Add a configurable override (e.g. “Preferred FPM Group”) in settings.
- Introduce
herd restart --allto restart all PHP-FPM pools.
✅ Result
After installation:
- No more 502 Bad Gateway errors after PHP version changes.
- PHP-FPM sockets always use the correct group (
admin). - Fully automatic — patches itself whenever Herd regenerates configs.
Steps to reproduce
No response
Relevant log output
I guess the group (admin) is not static and depends on your specific setup. I'll have to see how we can safely test this once we make adjustments to the group detection.