If you are still trying to configure your Raspberry Pi network by editing /etc/wpa_supplicant/wpa_supplicant.conf or /etc/dhcpcd.conf, stop. As of Raspberry Pi OS Bookworm and the current 2026 Trixie releases, the OS has fully migrated to NetworkManager. The direct answer for modern headless Raspberry Pi network config is to use the nmcli command-line tool for Wi-Fi and Ethernet management, paired with a hardware-level GPIO indicator to confirm link state without needing a monitor.
This guide targets the Raspberry Pi 5 (4GB variant) running 64-bit Raspberry Pi OS. We will build a headless network node, map GPIO pins for physical status LEDs, write a robust Python monitoring script, and debug the exact NetworkManager errors that stall headless deployments.
The 2026 Raspberry Pi Network Config Decision Tree
Before flashing an SD card or NVMe drive, choose your network topology. Use this decision matrix to lock in your configuration strategy.
| Deployment Scenario | Primary Interface | IP Assignment | Concrete Pick / Default Action |
|---|---|---|---|
| High-bandwidth local server (NAS, Pi-hole) | Gigabit Ethernet (eth0) | Static IPv4 | Pick: Ethernet + nmcli static IP (/24 subnet) |
| Remote IoT sensor / mobile robot | Wi-Fi 5 (wlan0) | DHCP | Pick: 5GHz Wi-Fi + DHCP with MAC reservation on router |
| Industrial / ceiling-mounted node | PoE+ (802.3at) | Static IPv4 | Pick: Waveshare PoE HAT + Ethernet static IP |
Hardware & Parts List: Pi 5 Headless Network Node
This build assumes a headless deployment where physical feedback is required to diagnose boot and network issues without plugging in an HDMI display.
| Component | Exact Variant / Model | Estimated Price (2026) |
|---|---|---|
| Compute Board | Raspberry Pi 5 (4GB RAM) | $60.00 |
| Storage | Samsung 980 256GB NVMe M.2 (2230 or 2242) | $32.00 |
| Enclosure | Argon ONE V3 M.2 NVMe Case (Active/Passive cooling) | $35.00 |
| Power Supply | Official Raspberry Pi 27W USB-C PD Power Supply | $12.00 |
| Indicator LEDs | 3mm Diffused LEDs (Red, Green, Blue) + 330Ω resistors | $2.00 |
Pin Mapping: GPIO Network Status LEDs
To give our headless Pi physical network awareness, we will wire three LEDs to the GPIO header. This allows you to glance at the board and instantly know if the physical link is up and if an IP address has been assigned.
| LED Color | Function | BCM GPIO Pin | Physical Pin (40-pin Header) | Wiring Note |
|---|---|---|---|---|
| Red | No Link / Error | GPIO 17 | Pin 11 | Anode to Pin 11, Cathode to 330Ω resistor, then to GND (Pin 9) |
| Green | Physical Link Up | GPIO 27 | Pin 13 | Anode to Pin 13, Cathode to 330Ω resistor, then to GND (Pin 14) |
| Blue | IP Address Assigned | GPIO 22 | Pin 15 | Anode to Pin 15, Cathode to 330Ω resistor, then to GND (Pin 20) |
Step-by-Step: NetworkManager CLI Configuration
With the hardware assembled and the Pi booted headless (accessed via a temporary monitor or USB-C serial console), use nmcli to configure the network. We will set a static IP on Ethernet (eth0).
- Check current NetworkManager status:
nmcli general status
Verify it returns 'connected' or 'disconnected', not 'unmanaged'. - Create a new static Ethernet connection profile:
sudo nmcli con add type ethernet ifname eth0 con-name eth0-static autoconnect yes - Assign the static IP, Gateway, and DNS:
sudo nmcli con mod eth0-static ipv4.addresses 192.168.1.50/24
sudo nmcli con mod eth0-static ipv4.gateway 192.168.1.1
sudo nmcli con mod eth0-static ipv4.dns "1.1.1.1 8.8.8.8"
sudo nmcli con mod eth0-static ipv4.method manual - Activate the profile:
sudo nmcli con up eth0-static - Verify the assignment:
ip -4 addr show eth0
Python Network Monitor Script (With Error Handling)
This script targets the Raspberry Pi 5 (4GB) running 64-bit OS. It uses the gpiozero library to drive our status LEDs and subprocess to query NetworkManager. It includes robust error handling for interface dropouts.
import time
import socket
import subprocess
from gpiozero import LED
# BCM Pin Definitions
PIN_RED = 17
PIN_GREEN = 27
PIN_BLUE = 22
# Initialize LEDs
led_red = LED(PIN_RED)
led_green = LED(PIN_GREEN)
led_blue = LED(PIN_BLUE)
INTERFACE = 'eth0'
def check_physical_link():
"""Checks if the physical Ethernet cable is plugged in via ip link."""
try:
result = subprocess.run(
['ip', 'link', 'show', INTERFACE],
capture_output=True, text=True, timeout=2
)
return 'state UP' in result.stdout or 'LOWER_UP' in result.stdout
except Exception as e:
print(f'Link check error: {e}')
return False
def check_ip_assigned():
"""Checks if a valid IPv4 address (not 169.254.x.x) is assigned."""
try:
result = subprocess.run(
['ip', '-4', 'addr', 'show', INTERFACE],
capture_output=True, text=True, timeout=2
)
for line in result.stdout.split('\n'):
if 'inet ' in line and '169.254.' not in line:
return True
return False
except Exception as e:
print(f'IP check error: {e}')
return False
def set_led_state(link_up, ip_assigned):
"""Drives the GPIO LEDs based on network state."""
if not link_up:
led_red.on()
led_green.off()
led_blue.off()
elif link_up and not ip_assigned:
led_red.off()
led_green.on()
led_blue.blink(on_time=0.5, off_time=0.5, background=False)
else:
led_red.off()
led_green.on()
led_blue.on()
if __name__ == '__main__':
print('Starting Pi 5 Network Monitor...')
try:
while True:
link = check_physical_link()
ip_ok = check_ip_assigned()
set_led_state(link, ip_ok)
time.sleep(2)
except KeyboardInterrupt:
print('Monitor stopped by user.')
finally:
led_red.off()
led_green.off()
led_blue.off()
Debugging: Exact Errors & The First Three Checks
When headless network configs fail, you are usually flying blind. Here are the exact error strings NetworkManager throws, what they mean, and how to fix them.
Error 1: The Wi-Fi Secret Failure
Exact Error String: Error: Connection activation failed: (7) Secrets were required, but not provided.
Ranked Causes:
- Missing interactive prompt: You ran
nmcli dev wifi connect SSID password 'xyz'but the password contained unescaped shell characters (like!or$). Fix: Use single quotes around the password, or use the--askflag to let nmcli prompt you securely. - WPA3 vs WPA2 mismatch: The router enforces WPA3-SAE, but the Pi's NetworkManager profile defaulted to WPA2. Fix: Edit the connection with
nmcli con mod SSID wifi-sec.key-mgmt sae.
Error 2: The DNS Blackhole
Exact Error String: ping: google.com: Temporary failure in name resolution
Ranked Causes:
- Static IP missing DNS: You set a static IP via
nmclibut forgot to defineipv4.dns. Fix: Runsudo nmcli con mod eth0-static ipv4.dns "1.1.1.1"and restart the connection. - systemd-resolved conflict: A leftover
/etc/resolv.confsymlink is broken. Fix: Ensure NetworkManager is managing DNS by checkingsudo nmcli general status(DNS field should not be empty).
The "First Three Checks" When It Fails
If your Pi drops off the network and the Red LED turns on, run these three diagnostics in order via serial console or local keyboard:
- Check NetworkManager State:
nmcli general status(Look for 'connected' vs 'disconnected'). - Check Interface IP:
ip a show eth0(Look for a valid subnet IP, not just a link-local 169.254.x.x address). - Check the Logs:
journalctl -u NetworkManager -n 50 --no-pager(This will reveal DHCP timeouts or authentication rejections).
Extending or Simplifying the Build
Depending on your deployment scale, you should either strip this build down to its bare essentials or scale it up for fleet management.
To Simplify (Single Node):
Skip the CLI entirely for initial provisioning. Use the Raspberry Pi Imager on your desktop. In the OS Customisation settings (the gear icon), input your Wi-Fi SSID, password, and check the box to set a static IP or hostname. The Imager writes a custom.toml file to the boot partition, which the Pi parses on first boot to configure NetworkManager automatically before you even plug it in.
To Extend (Fleet Monitoring):
If you are deploying five or more Pi 5 nodes, GPIO LEDs are insufficient. Extend this build by installing prometheus-node-exporter via sudo apt install prometheus-node-exporter. This exposes network interface metrics (bytes sent/received, error rates, drop counts) on port 9100. You can then scrape these metrics into a central Grafana dashboard, allowing you to monitor the exact moment a Wi-Fi interface drops packets across your entire fleet without writing custom Python polling scripts.






