If you are running Raspberry Pi OS Bookworm or newer on a Raspberry Pi 5, the direct answer is to use nmcli (NetworkManager Command Line Interface). Do not edit /etc/dhcpcd.conf—the dhcpcd daemon is deprecated, disabled by default in 2026, and your changes will be silently ignored.
Setting a static IP is critical for headless servers, Home Assistant nodes, and MQTT brokers where DNS resolution or DHCP lease expirations can break your automation. Below is the exact procedure to lock in your IP, wire a physical GPIO network-status LED, and debug the exact error strings NetworkManager throws when things go wrong.
Decision Tree: Router Reservation vs. Pi-Side Static IP
Before touching the terminal, decide where the IP assignment should live. Use this decision matrix to pick your approach:
| Scenario | Network Environment | Recommended Approach |
|---|---|---|
| Headless IoT Sensor Node | Managed home router (UniFi, pfSense) | Router-side DHCP Reservation |
| Standalone Server (NAS, Pi-hole) | Direct switch or unmanaged router | Pi-side Static IP via NetworkManager |
| Mobile/Field Deployment | Changing networks (hotspots, 4G routers) | Pi-side Fallback Static + DHCP |
nmcli on the primary Ethernet interface.
Hardware & Parts List
This guide targets the Raspberry Pi 5 (8GB variant) running the 64-bit Raspberry Pi OS (Bookworm or later). We are also adding a physical GPIO status LED. When running headless, a physical LED saves you from plugging in a monitor just to check if the Pi successfully grabbed its static IP or fell back to a link-local address.
- Board: Raspberry Pi 5 (8GB)
- Power: Official 27W USB-C Power Supply (prevents brownout warnings on the PCIe bus)
- Network: Cat6 Ethernet Cable (Gigabit)
- Indicator: 5mm Green LED
- Current Limiting: 330Ω through-hole resistor
- Wiring: Half-size breadboard and 2x male-to-female jumper wires
Pin Mapping Table
Wire the status LED to GPIO 17. This pin is safe to use (it does not conflict with the Pi 5's new PCIe or UART debug defaults).
| Component | Pi 5 Physical Pin | BCM GPIO Number | Function |
|---|---|---|---|
| 330Ω Resistor (Input) | Pin 11 | GPIO 17 | PWM/Digital High (3.3V) |
| LED Anode (+) | N/A (Inline) | N/A | Connects to Resistor Output |
| LED Cathode (-) | Pin 9 | GND | System Ground |
Step-by-Step: Configuring Static IP via nmcli
NetworkManager identifies connections by their name, not just the interface hardware. On the Pi 5, the Ethernet interface is frequently renamed from eth0 to end0 or enp1s0 due to predictable network interface naming.
- Identify your active connection name:
nmcli connection show --active
Look under the NAME column. It is usuallyWired connection 1. - Identify your interface hardware name:
ip link show
Note the name (e.g.,end0). - Assign the static IP and subnet mask (CIDR notation):
sudo nmcli con mod "Wired connection 1" ipv4.addresses 192.168.1.50/24 - Set the default gateway (your router's IP):
sudo nmcli con mod "Wired connection 1" ipv4.gateway 192.168.1.1 - Set DNS servers (using Cloudflare and Google as reliable fallbacks):
sudo nmcli con mod "Wired connection 1" ipv4.dns "1.1.1.1 8.8.8.8" - Change the IPv4 method from auto (DHCP) to manual (Static):
sudo nmcli con mod "Wired connection 1" ipv4.method manual - Apply the changes by restarting the connection:
sudo nmcli con up "Wired connection 1"
Verify the assignment with ip a show end0. You should see inet 192.168.1.50/24.
Python Verification Script with GPIO Fallback
The following Python script targets the Raspberry Pi 5 running Bookworm. It uses the gpiozero library (which natively supports the Pi 5's lgpio backend) to check the assigned IP. If the Pi successfully holds the 192.168.1.50 static IP, the LED stays solid. If it falls back to DHCP or a link-local address (169.254.x.x), the LED blinks rapidly to alert you.
import socket
import time
from gpiozero import LED
# Pin definition matching our hardware table
STATUS_LED = LED(17)
TARGET_STATIC_IP = '192.168.1.50'
def get_current_ip():
"""Fetches the primary IP address bound to the default route."""
try:
# Create a UDP socket to an external address to find the active interface IP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(2.0)
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
s.close()
return ip
except Exception as e:
print(f"Network socket error: {e}")
return None
def main():
print(f"Monitoring network for target IP: {TARGET_STATIC_IP}")
try:
while True:
current_ip = get_current_ip()
if current_ip == TARGET_STATIC_IP:
# Static IP confirmed: Solid LED
STATUS_LED.on()
time.sleep(5)
else:
# Fallback/DHCP/No Network: Rapid blink warning
print(f"Warning: IP is {current_ip}, expected {TARGET_STATIC_IP}")
STATUS_LED.blink(on_time=0.2, off_time=0.2, n=5, background=False)
time.sleep(2)
except KeyboardInterrupt:
print("\nExiting monitor. Cleaning up GPIO.")
STATUS_LED.off()
if __name__ == '__main__':
main()
Debugging: Exact Error Strings and Ranked Fixes
NetworkManager is strict about syntax and interface states. If your connection drops or fails to apply, match your terminal output to these exact error strings.
Error: Connection 'Wired connection 1' is not available on the device end0.
- Cause 1 (Most Likely): The interface hardware name changed after a kernel update (e.g., from
eth0toend0), and NetworkManager locked the connection profile to the old MAC address or device name. - Fix: Clear the interface binding by running
sudo nmcli con mod "Wired connection 1" connection.interface-name "", then bring it up again.
Error: unknown connection 'Wired connection 1'
- Cause 1: You are using a fresh Pi OS Lite image that hasn't generated the default Ethernet profile yet, or you mistyped the name.
- Fix: Run
nmcli con showto find the exact string. If missing, create it:sudo nmcli con add type ethernet ifname end0 con-name "StaticEth" ipv4.method manual ipv4.addresses 192.168.1.50/24 ipv4.gateway 192.168.1.1.
RTNETLINK answers: File exists (Seen when checking routes or pinging)
- Cause 1: You have a duplicate default gateway defined, or an old
dhcpcdservice is still running in the background and fighting NetworkManager for the routing table. - Fix: Disable the legacy daemon completely:
sudo systemctl disable dhcpcdandsudo systemctl stop dhcpcd, then reboot.
The First Three Things to Check When It Fails
If you cannot SSH into the Pi after applying the static IP, plug in a monitor and keyboard and check these three items in order:
- Interface Name Mismatch: Run
ip a. Ensure the IP is bound to the active interface (usuallyend0), not the loopback (lo) or a dormant USB-Ethernet adapter. - Subnet Mask Math: Did you use
/24? If your router is192.168.1.1and you set the Pi to192.168.50.10/24without a/16mask, they are on different logical networks and cannot talk. Ensure the first three octets match your gateway. - DNS Resolution vs. Routing: Run
ping 8.8.8.8. If this works, butping google.comfails, your IP and gateway are fine, but your DNS step (Step 5 above) failed or was omitted.
Extending and Simplifying the Build
To Simplify: If you do not want to memorize nmcli flags, Raspberry Pi OS includes nmtui (NetworkManager Text User Interface). Simply type sudo nmtui in the terminal to open a visual, arrow-key-driven menu where you can select "Edit a connection" and type the IP addresses into standard text boxes. It writes the exact same backend configuration files.
To Extend: For a robust IoT deployment, extend the Python script above to publish the IP address to an MQTT broker on boot. By adding the paho-mqtt library, the Pi can announce its new static IP to your Home Assistant dashboard the moment it connects, eliminating the need to scan the network with tools like Nmap or Fing to find your headless nodes.
For deeper reading on the underlying daemon managing your network stack, refer to the official Raspberry Pi NetworkManager documentation and the upstream NetworkManager nmcli reference.






