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
Concrete Pick: If you are building a standalone project, lab server, or do not have admin access to the router's DHCP table, terminate your decision here: Use Pi-side Static IP via 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.

  1. Identify your active connection name:
    nmcli connection show --active
    Look under the NAME column. It is usually Wired connection 1.
  2. Identify your interface hardware name:
    ip link show
    Note the name (e.g., end0).
  3. Assign the static IP and subnet mask (CIDR notation):
    sudo nmcli con mod "Wired connection 1" ipv4.addresses 192.168.1.50/24
  4. Set the default gateway (your router's IP):
    sudo nmcli con mod "Wired connection 1" ipv4.gateway 192.168.1.1
  5. 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"
  6. Change the IPv4 method from auto (DHCP) to manual (Static):
    sudo nmcli con mod "Wired connection 1" ipv4.method manual
  7. 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 String: 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 eth0 to end0), 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 String: 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 show to 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.
Error String: RTNETLINK answers: File exists (Seen when checking routes or pinging)
  • Cause 1: You have a duplicate default gateway defined, or an old dhcpcd service is still running in the background and fighting NetworkManager for the routing table.
  • Fix: Disable the legacy daemon completely: sudo systemctl disable dhcpcd and sudo 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:

  1. Interface Name Mismatch: Run ip a. Ensure the IP is bound to the active interface (usually end0), not the loopback (lo) or a dormant USB-Ethernet adapter.
  2. Subnet Mask Math: Did you use /24? If your router is 192.168.1.1 and you set the Pi to 192.168.50.10/24 without a /16 mask, they are on different logical networks and cannot talk. Ensure the first three octets match your gateway.
  3. DNS Resolution vs. Routing: Run ping 8.8.8.8. If this works, but ping google.com fails, 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.