Why This Exists

If you work in infrastructure, DevOps, or SRE, you’ve probably run into this: you need to quickly disconnect from VPN to test networking behavior, hit a public endpoint, debug DNS, or verify routing - but GlobalProtect requires digging through a GUI just to toggle the connection.

That’s friction.

  • test networking behavior
  • hit a public endpoint
  • debug DNS
  • access a local service
  • verify routing issues

But GlobalProtect requires digging through a GUI just to toggle the VPN.

That’s friction.

As system administrators, we prefer short commands that do one thing well.

Instead of remembering commands like this:

launchctl unload /Library/LaunchAgents/com.paloaltonetworks.gp.pangp*
launchctl load /Library/LaunchAgents/com.paloaltonetworks.gp.pangp*

…wrap it in a small utility:

gp stop
gp start
gp restart
gp toggle
gp kill

Fast. Predictable. Scriptable.

The Script

Create a new file:

/usr/local/bin/gp

Paste the following script:

#!/usr/bin/env bash
set -euo pipefail

SERVICE="com.paloaltonetworks.gp.pangp"
PLIST_GLOB="/Library/LaunchAgents/${SERVICE}*"
DOMAIN="gui/$(id -u)"

find_plists() {
  compgen -G "$PLIST_GLOB" || true
}

is_loaded() {
  launchctl print "${DOMAIN}/${SERVICE}" >/dev/null 2>&1
}

start_gp() {
  echo "Starting GlobalProtect..."
  for plist in $(find_plists); do
    sudo launchctl bootstrap "$DOMAIN" "$plist" 2>/dev/null || true
  done
}

stop_gp() {
  echo "Stopping GlobalProtect..."
  sudo launchctl bootout "${DOMAIN}/${SERVICE}" 2>/dev/null || true
}

kill_gp() {
  echo "Killing GlobalProtect completely..."

  stop_gp

  sudo pkill -9 -f PanGPS 2>/dev/null || true
  sudo pkill -9 -f PanGPA 2>/dev/null || true
  sudo pkill -9 -f pangps 2>/dev/null || true
  sudo pkill -9 -f GlobalProtect 2>/dev/null || true

  echo "GlobalProtect terminated."
}

restart_gp() {
  stop_gp
  sleep 2
  start_gp
}

status_gp() {
  launchctl list | grep -i palo || echo "No GlobalProtect services running."
}

toggle_gp() {
  if is_loaded; then
    stop_gp
  else
    start_gp
  fi
}

case "${1:-}" in
  start) start_gp ;;
  stop) stop_gp ;;
  restart) restart_gp ;;
  toggle) toggle_gp ;;
  kill) kill_gp ;;
  status) status_gp ;;
  *)
    echo "Usage: gp {start|stop|restart|toggle|kill|status}"
    ;;
esac

Install the CLI

Make the script executable and verify it’s on your PATH:

sudo chmod +x /usr/local/bin/gp
which gp
# /usr/local/bin/gp

Usage

CommandDescription
gp startStart the VPN
gp stopStop the VPN
gp restartStop then start
gp toggleStart if stopped, stop if running
gp statusList running GlobalProtect services
gp killForce-kill all processes

gp kill

gp kill

The kill command goes further than stop - it unloads all launch agents, force-kills running processes, and prevents automatic restart. Most useful when debugging networking or routing issues where the VPN interferes even after a normal stop.

How It Works

GlobalProtect installs launch agents under /Library/LaunchAgents. The relevant services are:

com.paloaltonetworks.gp.pangp
com.paloaltonetworks.gp.pangpa

macOS manages these via launchctl, the system service manager. By booting services out of the user launch domain (gui/<uid>), we can programmatically disable the VPN without touching the GUI.

Quick Toggle Alias

Add this to your .zshrc or .bashrc:

alias vpn="gp toggle"

Now toggle the VPN with a single word:

vpn

Final Thoughts

Good system administration often comes down to removing friction.

Small scripts. Short commands. Repeatable workflows.

Instead of digging through a GUI every time you need to toggle VPN access, you now have a simple CLI tool that does it instantly.