The 2026 Reality: NetworkManager vs. Legacy wpa_supplicant
If you are following a Raspberry Pi WiFi config tutorial from 2022 or earlier, it is likely broken. The Raspberry Pi Foundation completely deprecated wpa_supplicant and dhcpcd in Raspberry Pi OS 'Bookworm' and carried that change forward into 'Trixie'. Editing /etc/wpa_supplicant/wpa_supplicant.conf will silently fail on modern images because the daemon is no longer managing the wlan0 interface.
The modern standard is NetworkManager, controlled via the nmcli (command line) or nmtui (terminal UI) utilities. NetworkManager handles WPA3, enterprise 802.1X, and seamless roaming between access points natively, which the old stack struggled with.
| Scenario | OS Version | Tool | Verdict |
|---|---|---|---|
| Desktop GUI setup | Bookworm / Trixie | nm-applet | Use the top-right system tray icon. |
| Headless IoT / Server | Bookworm / Trixie | nmcli | Default Pick: Use nmcli for 95% of headless builds. |
| Legacy Pi Zero W (Buster) | Buster / Bullseye | wpa_cli | Use wpa_supplicant.conf (only for outdated OS). |
| Complex Visual Terminal | Bookworm / Trixie | nmtui | Use when typing long WPA2-Enterprise passwords via SSH. |
Hardware & Parts List: Target Board and Debug GPIOs
This guide targets the Raspberry Pi 5 (8GB) equipped with the dual-band 802.11ac WiFi module. While the Pi 4 Model B and Pi Zero 2 W share the same NetworkManager software stack, the Pi 5's PCIe architecture and power requirements demand specific hardware considerations for stable RF performance.
Required Components
| Component | Exact Variant / Spec | Notes |
|---|---|---|
| Microcontroller | Raspberry Pi 5 (8GB RAM) | Requires active cooling for sustained WiFi throughput. |
| Power Supply | Official 27W USB-C PD PSU | Under-voltage causes WiFi chip brownouts and disconnects. |
| Status LED | 5mm Green Diffused LED | Wired to GPIO 17 for connection status. |
| Current Limiter | 330Ω 1/4W Resistor | Protects GPIO 17 from overcurrent. |
| Fallback Button | 6x6mm Tactile Switch | Wired to GPIO 27 for hardware WiFi reset. |
GPIO Pin Mapping for Network Debugging
When running headless, you cannot see the desktop network applet. Mapping physical GPIO pins to network states allows you to debug connection drops from across the room.
| Physical Pin | BCM GPIO | Function | Wiring Target |
|---|---|---|---|
| 11 | GPIO 17 | WiFi Status LED | Anode (via 330Ω resistor) |
| 13 | GPIO 27 | Hardware Reset Trigger | Tactile Switch (to GND) |
| 9 / 14 | GND | Common Ground | LED Cathode & Switch |
Step-by-Step Headless Raspberry Pi WiFi Config
Assuming you are SSH'd into your Pi 5 or using a serial console, follow these exact steps to configure a persistent WiFi connection using nmcli.
- Scan for available networks:
nmcli device wifi rescan
nmcli device wifi list - Connect to the network and save the profile:
nmcli device wifi connect 'Your_SSID_Name' password 'YourPassword123' name 'HomeWiFi'
Note: Thenameparameter creates a reusable profile name, distinct from the SSID. - Verify the connection is set to auto-start on boot:
nmcli connection modify 'HomeWiFi' connection.autoconnect yes - Set connection priority (crucial for multi-AP environments):
nmcli connection modify 'HomeWiFi' connection.autoconnect-priority 10
Higher numbers connect first. Set your 5GHz network to 10 and 2.4GHz fallback to 0. - Reload and activate:
nmcli connection reload
nmcli connection up 'HomeWiFi'
Python Fallback Script: Auto-Reconnect and GPIO Status
NetworkManager handles standard reconnections, but in high-interference environments (like a workshop with VFD motors), the WiFi chip can lock up and require a soft reset. The following Python script monitors the wlan0 state via nmcli, drives the GPIO 17 status LED, and uses the GPIO 27 tactile button to force a NetworkManager interface restart if the network hangs.
Target Board: Raspberry Pi 5 (8GB) running Raspberry Pi OS Bookworm.
Dependencies: sudo apt install python3-gpiozero (pre-installed on desktop, may need manual install on Lite).
import subprocess
import time
import sys
from gpiozero import LED, Button
from signal import pause
# --- Pin Definitions ---
STATUS_LED = LED(17) # Physical Pin 11
RESET_BTN = Button(27, pull_up=True, bounce_time=0.2) # Physical Pin 13
PROFILE_NAME = 'HomeWiFi' # Must match the 'name' set in nmcli
def get_wifi_state():
"""Queries nmcli for the exact state of the wlan0 interface."""
try:
result = subprocess.run(
['nmcli', '-t', '-f', 'GENERAL.STATE', 'device', 'show', 'wlan0'],
capture_output=True, text=True, check=True
)
# Output looks like: GENERAL.STATE:100 (connected)
state_str = result.stdout.strip()
if '100' in state_str or 'connected' in state_str:
return 'connected'
return 'disconnected'
except subprocess.CalledProcessError:
return 'error'
except Exception as e:
print(f'Unexpected nmcli error: {e}')
return 'error'
def force_wifi_reset():
"""Hard resets the WiFi interface via NetworkManager."""
print('[ACTION] Hardware reset triggered. Bouncing wlan0...')
STATUS_LED.blink(0.2, 0.2) # Fast blink during reset
try:
subprocess.run(['nmcli', 'device', 'disconnect', 'wlan0'], check=True)
time.sleep(2)
subprocess.run(['nmcli', 'connection', 'up', PROFILE_NAME], check=True)
print('[SUCCESS] Interface reset complete.')
except subprocess.CalledProcessError as e:
print(f'[FAIL] Reset failed: {e}')
# Bind the hardware button to the reset function
RESET_BTN.when_pressed = force_wifi_reset
print(f'Monitoring WiFi profile: {PROFILE_NAME}')
print('Press the button on GPIO 27 to force a network reset.')
try:
while True:
state = get_wifi_state()
if state == 'connected':
STATUS_LED.on()
elif state == 'disconnected':
STATUS_LED.blink(1, 1) # Slow blink = disconnected
# Optional: Add automated software retry logic here
else:
STATUS_LED.off() # Off = interface missing or error
time.sleep(5)
except KeyboardInterrupt:
print('\nExiting monitor.')
STATUS_LED.off()
sys.exit(0)
Troubleshooting: Exact Error Strings and Ranked Fixes
When a Raspberry Pi WiFi config fails, the first three things to check are: (1) Are you using the correct nmcli syntax for Bookworm? (2) Is the 27W PSU delivering stable voltage (check vcgencmd get_throttled)? (3) Is the router enforcing MAC filtering or WPA3-Enterprise incorrectly?
If those pass, match your terminal output to these exact error strings.
Error: Connection activation failed: (7) Secrets were required, but not provided.
- Cause A (Most Likely): Incorrect password or special characters in the password were interpreted by the bash shell (e.g.,
$,&,!). - Fix: Wrap the password in single quotes, not double quotes:
nmcli device wifi connect 'SSID' password 'P@ssword!$'. - Cause B: The router is using WPA3-SAE and the Pi's
wpa_supplicantbackend (used internally by NM) is failing the handshake. - Fix: Force WPA2 transition mode on your router, or update the Pi:
sudo apt update && sudo apt upgrade.
OSError: [Errno 101] Network is unreachable (Seen in Python scripts)
- Cause A (Most Likely): The script executed before NetworkManager finished DHCP negotiation on boot.
- Fix: Do not use
time.sleep()hacks. Add a network-online dependency to your systemd service file:After=NetworkManager-wait-online.service. - Cause B: The
wlan0interface dropped its IP lease but remained 'connected' at the MAC layer. - Fix: Run
nmcli connection reloadandnmcli connection up 'HomeWiFi'.
wlan0: CTRL-EVENT-DISCONNECTED bssid=... reason=3 locally_generated=1 (Seen in journalctl -u NetworkManager)
- Cause A (Most Likely): Power delivery brownout causing the Cypress/Infineon WiFi chip to reset internally.
- Fix: Verify you are using the official 27W PD supply. Third-party phone chargers often drop voltage under the Pi 5's transient RF transmit loads.
- Cause B: USB 3.0 interference drowning out 2.4GHz WiFi.
- Fix: Switch the Pi to a 5GHz WiFi network, or use a shielded USB 3.0 hub for external peripherals.
Extending the Build: Static IPs and Enterprise WPA3
Once your baseline Raspberry Pi WiFi config is stable, you will likely need to lock down the IP address for port forwarding or MQTT broker hosting.
How to Extend: Assigning a Static IP via nmcli
Forget editing /etc/dhcpcd.conf—that file is ignored in modern Pi OS. Assign a static IP directly to the NetworkManager profile:
# Set static IP and Subnet Mask
nmcli connection modify 'HomeWiFi' ipv4.addresses 192.168.1.50/24
# Set Gateway
nmcli connection modify 'HomeWiFi' ipv4.gateway 192.168.1.1
# Set DNS (using Cloudflare and Google)
nmcli connection modify 'HomeWiFi' ipv4.dns '1.1.1.1 8.8.8.8'
# Force manual IP assignment (disables DHCP for this profile)
nmcli connection modify 'HomeWiFi' ipv4.method manual
# Apply changes
nmcli connection up 'HomeWiFi'
How to Simplify: Using nmtui for Complex Auth
If you are connecting to a university or corporate network requiring WPA2-Enterprise (PEAP/MSCHAPv2) with CA certificates, typing the nmcli flags is a nightmare. Simplify the build by launching the visual terminal interface:
sudo nmtui
Use the arrow keys to select 'Edit a connection', navigate the nested menus to input your identity, password, and certificate paths, and save. NetworkManager will handle the underlying 802.1X handshake automatically.
For deeper documentation on NetworkManager's capabilities on ARM boards, refer to the official Raspberry Pi networking documentation and the upstream NetworkManager nmcli reference. If you are expanding the Python monitoring script to control external relays based on network state, consult the GPIO Zero API documentation for advanced threading patterns.






