Introduction to macOS Network Configuration
macOS, like its BSD Unix ancestor, ships with a powerful suite of command-line networking tools. While the System Settings GUI exposes the most common options, developers and system administrators often need finer-grained control over interfaces, routing, and packet filtering. Three tools form the backbone of macOS network configuration: ifconfig for interface management, networksetup for persistent system-level configuration, and pfctl for the OpenBSD-derived Packet Filter firewall. This tutorial walks through each tool, explains when to use which, and provides practical examples you can run on your own machine.
Why This Matters for Developers
Understanding these tools matters for several reasons. First, many development workflows — local DNS overrides, container networking, VPN tunnels, proxy interception — require changes the GUI cannot express. Second, automated provisioning scripts (think Ansible, Chef, or shell-based setup) need idempotent commands rather than click-through dialogs. Third, security-conscious developers need to know how to inspect and restrict inbound traffic on a workstation that may run web servers, databases, or other listening services. Finally, debugging connectivity issues is dramatically faster from the terminal than from nested preference panes.
ifconfig: Interface Inspection and Control
What it Is
ifconfig is the classic BSD utility for configuring network interfaces. On macOS it reads and modifies the live kernel state of interfaces — assigning IP addresses, bringing links up or down, and querying hardware details. Changes made with ifconfig are not persistent across reboots; for persistent changes use networksetup.
Listing Interfaces
# Show all interfaces and their status
ifconfig -a
# Show a specific interface
ifconfig en0
# Show only interfaces that are currently UP
ifconfig -u
The output includes the MAC address (ether), assigned IPv4 and IPv6 addresses, MTU, and link status. Common interface names on macOS include en0 (primary Wi-Fi or Ethernet), en1, utun0 (user tunnels, often used by VPNs), lo0 (loopback), and bridge0 (the shared networking bridge used by virtualization frameworks).
Assigning an IP Address
# Bring the interface down first
sudo ifconfig en0 down
# Assign a static IPv4 address with a /24 netmask
sudo ifconfig en0 inet 192.168.50.100 netmask 255.255.255.0
# Bring it back up
sudo ifconfig en0 up
# Add a secondary address (alias)
sudo ifconfig en0 inet 192.168.50.101 netmask 255.255.255.255 alias
# Remove the alias
sudo ifconfig en0 inet 192.168.50.101 netmask 255.255.255.255 -alias
Changing the MAC Address
For testing DHCP behavior or bypassing naive network filters, you can spoof the hardware address. This change is temporary and resets on reboot or interface reset.
sudo ifconfig en0 ether 00:11:22:33:44:55
Adjusting MTU
# Set a 9000-byte jumbo frame MTU on a wired interface
sudo ifconfig en5 mtu 9000
# Verify
ifconfig en5 | grep mtu
networksetup: Persistent System Configuration
What it Is
networksetup is a macOS-specific tool that bridges the command line and the System Configuration framework — the same backend used by System Settings. Unlike ifconfig, changes made with networksetup are persistent across reboots and are reflected in the GUI. It is the right tool for setting default gateways, DNS servers, proxies, and service order.
Listing Network Services
# List all configured network services with their hardware ports
networksetup -listallnetworkservices
# List all hardware ports and device names
networksetup -listallhardwareports
The first command returns names like "Wi-Fi", "Ethernet", "Thunderbolt Bridge", which you then pass to other networksetup subcommands. The second maps those friendly names to BSD device names like en0.
Setting DNS Servers
# Set custom DNS servers for Wi-Fi
sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 1.0.0.1
# Clear custom DNS and revert to DHCP-provided servers
sudo networksetup -setdnsservers Wi-Fi empty
# Show current DNS
networksetup -getdnsservers Wi-Fi
Configuring a Static IP
# Set manual IP, subnet mask, and router for Ethernet
sudo networksetup -setmanual Ethernet 192.168.50.100 255.255.255.0 192.168.50.1
# Revert to DHCP
sudo networksetup -setdhcp Ethernet
# Show current configuration
networksetup -getinfo Ethernet
Web and SOCKS Proxies
# Enable an HTTP proxy on port 8080
sudo networksetup -setwebproxy Wi-Fi 127.0.0.1 8080
# Enable a SOCKS proxy (useful for SSH dynamic tunnels)
sudo networksetup -setsocksfirewallproxy Wi-Fi 127.0.0.1 1080
# Disable the HTTP proxy
sudo networksetup -setwebproxystate Wi-Fi off
# Show proxy status
networksetup -getwebproxy Wi-Fi
Reordering Network Services
Service order determines which interface is preferred for default routing. This is critical when both Wi-Fi and Ethernet are active.
# Display current service order
networksetup -listnetworkserviceorder
# Reorder: Ethernet first, then Wi-Fi
sudo networksetup -ordernetworkservices "Ethernet" "Wi-Fi"
pfctl: The Packet Filter Firewall
What it Is
macOS includes a port of OpenBSD's pf (Packet Filter), controlled via pfctl. The configuration lives in /etc/pf.conf. Despite being present, pf is not enabled by default on macOS. Apple relies on the Application Firewall (socketfilterfw) for user-facing protection, but pf gives developers far more granular control — port blocking, traffic shaping, NAT, and redirection.
Checking pf Status
# Is pf enabled?
sudo pfctl -s info | grep Status
# Show the currently loaded ruleset
sudo pfctl -sr
# Show all anchors
sudo pfctl -s anchors
Enabling and Disabling pf
# Enable pf (loads /etc/pf.conf)
sudo pfctl -e
# Disable pf
sudo pfctl -d
A Minimal Configuration File
Create or edit /etc/pf.conf. The default macOS file includes several anchors; you can append your own rules. Here is a minimal developer-friendly ruleset that blocks inbound SSH except from a specific subnet, and blocks all other inbound connections to listening services while allowing all outbound traffic.
# /etc/pf.conf (append below existing content)
# Define the external interface dynamically
ext_if = "en0"
# Allow loopback
set skip on lo0
# Default block policy
block in all
# Allow inbound SSH only from the local 192.168.50.0/24 subnet
pass in on $ext_if inet proto tcp from 192.168.50.0/24 to any port 22
# Allow established connections and outbound traffic
pass out all
pass in inet proto tcp from any to any flags S/SA keep state
Loading and Testing Rules
# Validate syntax without loading
sudo pfctl -vnf /etc/pf.conf
# Load the ruleset (replaces current rules)
sudo pfctl -f /etc/pf.conf
# Flush all rules
sudo pfctl -F all
# Flush only filter rules
sudo pfctl -F rules
Port Forwarding with pf
A common developer use case is redirecting traffic from one port to another — for example, forwarding port 80 to a development server on 8080 without running the server as root.
# Add a redirection rule
echo "rdr pass on lo0 inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080" | sudo pfctl -ef -
This loads an inline ruleset that redirects all loopback traffic destined for port 80 to port 8080. To undo it, simply disable pf with sudo pfctl -d or reload your main config with sudo pfctl -f /etc/pf.conf.
Blocking a Specific Outbound Host
# Block all traffic to a specific IP
echo "block out quick on en0 to any 203.0.113.99" | sudo pfctl -ef -
# Or append to your existing ruleset and reload
sudo pfctl -f /etc/pf.conf
Combining the Three Tools
In practice these tools complement each other. A typical developer scenario: you want to run a local DNS server on port 53, but macOS already binds mDNSResponder to that port on loopback. You might use ifconfig to add a secondary loopback alias, networksetup to point your DNS at that alias persistently, and pfctl to redirect port 53 traffic.
# 1. Add a loopback alias
sudo ifconfig lo0 alias 127.0.0.53
# 2. Redirect port 53 on the alias to your DNS server on 5353
echo "rdr pass on lo0 inet proto udp from any to 127.0.0.53 port 53 -> 127.0.0.53 port 5353" | sudo pfctl -ef -
# 3. Set the alias as your DNS server (persistent)
sudo networksetup -setdnsservers Wi-Fi 127.0.0.53
# 4. Verify
networksetup -getdnsservers Wi-Fi
dig @127.0.0.53 example.com
Best Practices
- Prefer
networksetupfor persistence. Useifconfigfor temporary testing andnetworksetupwhen you want changes to survive a reboot. - Always validate pf rules before loading. Run
sudo pfctl -vnf /etc/pf.conffirst. A malformed ruleset can lock you out of remote sessions. - Keep a backup of
/etc/pf.conf. macOS updates occasionally overwrite system config files; store your custom rules in a separate file and include it from the main config. - Use anchors for modular rules. Instead of dumping everything into the main ruleset, define anchors and load rules into them independently. This makes scripts idempotent and avoids clobbering unrelated rules.
- Document service names in scripts. Hard-coding "Wi-Fi" breaks on machines where the language is set differently. Consider detecting the active service programmatically with
networksetup -listnetworkserviceorderand parsing the output. - Test firewall changes locally first. If you manage a remote Mac over SSH, a bad pf rule can sever your connection. Add a cron job or
attask that disables pf after 5 minutes as a safety net while you test. - Flush before reloading. When iterating on pf rules, use
sudo pfctl -F all -f /etc/pf.confto ensure stale rules do not linger. - Understand the difference between pf and the Application Firewall.
socketfilterfwoperates at the application level and is what most users think of as "the macOS firewall." pf operates at the packet level and is far more powerful but also more dangerous to misconfigure.
Conclusion
macOS exposes a robust set of networking primitives through ifconfig, networksetup, and pfctl. ifconfig gives you immediate, ephemeral control over interfaces; networksetup provides persistent, GUI-aligned configuration of DNS, proxies, and IP settings; and pfctl delivers enterprise-grade packet filtering, redirection, and NAT. By understanding the boundaries between these tools and following the best practices above, you can automate network provisioning, build secure local development environments, and debug connectivity issues with confidence — all from the terminal.