← All writing
 ·  1 min read

Quick Fix: Nginx PID Errors on macOS (Apple Silicon + Homebrew)

If you run Nginx on an Apple Silicon Mac using Homebrew, you may see errors after a reboot caused by a stale /opt/homebrew/var/run/nginx.pid. I wrote a small ZSH script to remove the stale PID, restart Nginx, and confirm it’s actually serving traffic.

Quick Fix: Nginx PID Errors on macOS (Apple Silicon + Homebrew)

If you’re running macOS on an Apple Silicon Mac (M1, M2, M3) with Nginx installed via Homebrew, you might have seen this after a reboot:

text
nginx: [error] invalid PID number "" in "/opt/homebrew/var/run/nginx.pid"

This happens because Nginx keeps a PID file for its running process. Sometimes, after a reboot, that file is left behind, and Nginx refuses to start or reload.

I hit this enough times that I scripted a fix.

This script is tuned for Nginx installed via Homebrew on Apple Silicon. If you installed Nginx differently (for example, compiled from source or using a different path), you’ll need to modify the script — especially the path to the nginx.pid file.

Script Commands

  • nginxkill – Removes the stale nginx.pid file (/opt/homebrew/var/run/nginx.pid).
  • nginxreload – Reloads Nginx configuration and removes stale PID files if needed, then checks if Nginx is listening on port 80.
  • nginxrestart 🔥 – Stops and restarts Nginx, removes stale PID files if needed, then checks if Nginx is listening on port 80.
  • spinner_wait – For fun, it displays a simple spinner while waiting (used inside nginxrestart). You can replace this with a simple sleep 3 if you like. Nginx needs about 3 seconds to await the restart.

How to Use
Add these functions to your ~/.zshrc, reload your shell, then run nginxrestart whenever Nginx fails to start after a reboot.

bash
# nginx
function nginxkill() { 
    sudo rm /opt/homebrew/var/run/nginx.pid 
}

function nginxreload() {
    echo "Reloading Nginx"

    output=$(sudo nginx -s reload 2>&1)
    echo "$output"

    local GREEN="\033[0;32m"
    local NC="\033[0m" # No Color

    # More robust regex match (handles spacing and newlines)
    if [[ "$output" =~ nginx\.pid && "$output" =~ error ]]; then
        echo "PID file error detected. Removing stale nginx.pid..."
        nginxkill
        spinner_wait 3   # Wait 3 seconds for Nginx to fully start
    fi

    # --- Check if Nginx is actually serving traffic on port 80 ---
    if sudo lsof -nP -iTCP:80 -sTCP:LISTEN | grep -q nginx; then
        # Replace spinner with "Done!" in green
        echo "Nginx is online and listening on port 80"
        echo "${GREEN}PID error fixed!${NC}"
    else
        echo "Nginx failed to start or is not bound to port 80 (check logs)"
        echo "Try running 'nginxkill' and then restarting Nginx again."
    fi
}

function nginxrestart() {
    echo "Restarting Nginx"

    # Stop and start separately, capturing all output
    stop_output=$(sudo nginx -s stop 2>&1)
    start_output=$(sudo nginx 2>&1)
    output="$stop_output"$'\n'"$start_output"
    local GREEN="\033[0;32m"
    local NC="\033[0m" # No Color

    echo "$output"

    # Use =~ regex match (handles variable spacing and order)
    if [[ "$output" =~ nginx\.pid && "$output" =~ error ]]; then
        echo "PID file error detected. Removing stale nginx.pid..."
        nginxkill
        spinner_wait 3   # Wait 3 seconds for Nginx to fully start
    fi

    # --- Check if Nginx is actually serving traffic on port 80 ---
    if sudo lsof -nP -iTCP:80 -sTCP:LISTEN | grep -q nginx; then
        echo "Nginx is online and listening on port 80"
        echo "${GREEN}PID error fixed!${NC}"
    else
        echo "Nginx failed to start or is not bound to port 80 (check logs)"
        echo "Try running 'nginxkill' and then restarting Nginx again."
    fi
}

# spin for ~3 seconds
# spinner_wait 3 "Optional message"
spinner_wait() {
    local duration=$1
    local message=${2:-"Waiting... "}
    local end=$((SECONDS + duration))
    local chars=( "/" "-" "\\" "|" )
    local i=0
    local GREEN="\033[0;32m"
    local NC="\033[0m" # No Color

    # Print message and leave a space for spinner
    echo -n "$message "

    while (( SECONDS < end )); do
        # \r returns to start of line, then reprint message and spinner
        echo -ne "\r$message ${chars[i]}"
        sleep 0.2
        (( i = (i + 1) % ${#chars[@]} ))
    done

    # Replace spinner with "Done!" in green
    echo -e "\r$message ${GREEN}Done!${NC}"
}

Why This Helps

  • Automatically removes stale nginx.pid files.
  • Confirms Nginx is actually listening on port 80.
  • Makes reboot issues on macOS less annoying.

Put this in your ~/.zshrc and you’re set, and do not forget to reload your ZSH/bash source.