If you are trying to config Raspberry Pi WiFi using the old wpa_supplicant.conf method on a modern Raspberry Pi OS, your connection will fail. With the shift to Debian Bookworm (and continuing into 2026 releases), the Raspberry Pi Foundation completely replaced wpa_supplicant with NetworkManager. This guide provides the exact decision framework, nmcli commands, and a Python fallback script to ensure your headless Pi stays online.
The 2026 Reality: NetworkManager vs. Legacy wpa_supplicant
Before typing a single command, you must identify your OS version. Using the wrong configuration tool is the number one reason makers brick their headless network stacks. NetworkManager (controlled via the nmcli terminal tool) handles connection roaming, metric-based routing, and VPN integration natively—features wpa_supplicant struggled with.
OS Decision Tree: Which Tool to Use
| Condition | Tool to Use | Verdict |
|---|---|---|
| Running Pi OS Bullseye or older | wpa_supplicant.conf | Legacy (Do not use for new builds) |
| Running Pi OS Bookworm or newer | nmcli (NetworkManager) | DEFAULT PICK |
| Headless first-boot setup | Raspberry Pi Imager OS Customization | Best for initial flash |
Concrete Pick: For any Pi 4B or Pi 5 running an OS released from late 2023 onward, use nmcli.
Hardware Spec Sheet: Pi 5 and High-Gain WiFi Adapters
The built-in WiFi on the Raspberry Pi 5 (and 4B) is a dual-band 802.11ac (WiFi 5) module. For most indoor IoT sensor nodes, this is sufficient. However, if your Pi is in a metal enclosure or at the edge of your router's range, you need an external adapter. Here is the exact hardware list for a robust 2026 build.
| Component | Exact Variant | Specs & Notes | Est. Cost |
|---|---|---|---|
| Compute Board | Raspberry Pi 5 (8GB) | PCIe Gen 2, USB 3.0, built-in WiFi 5 | $80.00 |
| External WiFi (Optional) | Panda Wireless PAU09 | WiFi 6 (802.11ax), dual high-gain antennas, native Linux kernel support | $45.00 |
| Status Indicator | 5mm Green LED + 330Ω Resistor | For physical connection status feedback | $0.10 |
Step-by-Step: Configuring WiFi via nmcli
Assuming you are SSH'd into your Pi or using a serial console, follow these numbered steps to configure and prioritize your WiFi connection. This method writes directly to NetworkManager's connection profiles in /etc/NetworkManager/system-connections/.
- Scan for available networks:
nmcli device wifi list
Look for your SSID in the output. Note the exact spelling and capitalization. - Create the connection profile:
nmcli device wifi connect "YourSSID" password "YourPassword" name "HomeNetwork"
This creates a profile named 'HomeNetwork' and connects immediately. - Force the connection to auto-start on boot:
nmcli connection modify "HomeNetwork" connection.autoconnect yes - Set routing priority (Crucial for multi-homed Pis):
If your Pi is connected to both Ethernet and WiFi, and you want Ethernet to be the primary route, set the WiFi metric higher:
nmcli connection modify "HomeNetwork" ipv4.route-metric 600
Lower metric = higher priority. Ethernet defaults to 100. - Restart NetworkManager to apply changes cleanly:
sudo systemctl restart NetworkManager
Debugging: Exact Errors and the First Three Checks
When a headless Pi drops off the network, you need a systematic approach. Before rewriting your config, perform these first three checks:
- Check RF Kill Switches: Run
rfkill list. If 'Soft blocked' or 'Hard blocked' says 'yes' for wlan0, runsudo rfkill unblock wifi. - Check Interface State: Run
nmcli device status. If wlan0 shows as 'unmanaged', NetworkManager is ignoring it (check/etc/NetworkManager/NetworkManager.conf). If it shows 'disconnected', the radio is up but auth failed. - Isolate DNS vs. Layer 2: Run
ping -c 3 192.168.1.1(your router). If that works, butping -c 3 google.comfails, your WiFi is fine; your DNS resolver (systemd-resolved) is broken.
Ranked Causes for Common nmcli Errors
Error String: Error: No network with ID 'YourSSID' found.
- Cause 1 (Most Likely): Typo in the SSID or the Pi is out of range. Run
nmcli device wifi listto verify the exact string. - Cause 2: The router is broadcasting on a 5GHz DFS channel that the Pi's regional domain hasn't unlocked yet. Force the router to a standard channel (36-48).
Error String: Connection activation failed: (5) IP configuration could not be reserved.
- Cause 1: Router DHCP pool is exhausted. Check your router's admin panel.
- Cause 2: MAC address filtering is enabled on the router, and the Pi's randomized MAC feature is active. Disable MAC randomization in nmcli:
nmcli connection modify "HomeNetwork" wifi.cloned-mac-address preserve
Python Fallback Script: Auto-Reconnect and GPIO Status
NetworkManager is good, but in high-interference environments (like a workshop with heavy VFD motors), the WiFi stack can hang. This Python script monitors the connection, attempts a graceful nmcli reconnect, and drives a physical GPIO LED to indicate status.
Target Board: Raspberry Pi 5 (8GB) running Bookworm OS, Python 3.11+.
Required Library: gpiozero (pre-installed on Pi OS).
Pin Mapping Table
| Component | Pi 5 Pin (BCM) | Physical Pin | Wiring Notes |
|---|---|---|---|
| Status LED Anode | GPIO 17 | 11 | Via 330Ω current-limiting resistor |
| Status LED Cathode | GND | 9 | Common ground rail |
Complete Compilable Code
Save this as wifi_monitor.py and run it as a systemd service.
import subprocess
import time
import logging
from gpiozero import LED
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Hardware definition
STATUS_LED = LED(17)
CONNECTION_NAME = 'HomeNetwork'
CHECK_INTERVAL = 30 # Seconds between ping checks
def is_network_up():
"""Pings the default gateway to verify Layer 3 connectivity."""
try:
# -c 2 (2 packets), -W 2 (2 sec timeout)
result = subprocess.run(
['ping', '-c', '2', '-W', '2', '1.1.1.1'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
return result.returncode == 0
except Exception as e:
logging.error(f'Ping command failed: {e}')
return False
def reconnect_wifi():
"""Uses nmcli to bounce the specific connection profile."""
logging.warning('Network down. Attempting nmcli reconnect...')
STATUS_LED.blink(on_time=0.2, off_time=0.2) # Fast blink during reconnect
try:
subprocess.run(
['nmcli', 'connection', 'up', CONNECTION_NAME],
check=True,
capture_output=True,
text=True
)
logging.info('Reconnect command issued successfully.')
time.sleep(10) # Allow DHCP handshake
except subprocess.CalledProcessError as e:
logging.error(f'nmcli failed: {e.stderr.strip()}')
def main():
logging.info(f'Starting WiFi monitor for profile: {CONNECTION_NAME}')
STATUS_LED.off()
try:
while True:
if is_network_up():
STATUS_LED.on() # Solid ON = Good connection
else:
reconnect_wifi()
time.sleep(CHECK_INTERVAL)
except KeyboardInterrupt:
logging.info('Monitor stopped by user.')
STATUS_LED.off()
except Exception as e:
logging.critical(f'Unhandled exception: {e}')
STATUS_LED.blink(on_time=0.5, off_time=0.5) # Slow blink = Error
if __name__ == '__main__':
main()
Extending or Simplifying the Build
Depending on your deployment environment, you may need to adjust the complexity of this setup.
How to Simplify
If you are building a single desktop Pi and do not need automated recovery scripts or headless SSH access, skip the terminal entirely. Use the built-in raspi-config tool or the Raspberry Pi Imager's "OS Customization" menu (the gear icon) before flashing the SD card. The Imager injects the NetworkManager profiles directly into the boot partition, guaranteeing WiFi is live on the very first boot without requiring a monitor.
How to Extend: Adding Cellular Fallback
For remote agricultural sensors or off-grid weather stations where WiFi is unreliable, extend this build by adding a Waveshare SIM7600 4G HAT. NetworkManager natively supports ModemManager. By plugging in the HAT and inserting a nano-SIM, you can create a secondary nmcli connection profile for the cellular interface (wwan0). Set the cellular profile's ipv4.route-metric to 1000, and the WiFi metric to 100. If the WiFi drops, NetworkManager will automatically route traffic through the 4G HAT without dropping your active SSH session or interrupting your Python sensor logging.
For more details on NetworkManager's modem handling, refer to the official Raspberry Pi NetworkManager documentation and the gpiozero API reference for hardware interfacing.






