If you are trying to set a Raspberry Pi IP address static on modern Raspberry Pi OS (Bookworm or newer), the old tutorials are broken. The legacy dhcpcd daemon has been replaced by NetworkManager. Editing /etc/dhcpcd.conf will silently fail or cause boot-loop network drops. The direct, correct method for headless embedded builds is using the nmcli command-line tool to bind a fixed IPv4 address to your Ethernet or Wi-Fi interface.

The 2026 Reality: NetworkManager vs Legacy dhcpcd

Starting with Raspberry Pi OS Bookworm, the underlying networking stack shifted to align with standard Debian practices. NetworkManager is now the default. Before typing a single command, use this decision path to select the correct configuration method for your specific deployment.

Decision Tree: Choosing Your Static IP Method
Deployment Scenario OS Version Recommended Tool Verdict
Headless IoT Gateway / Server Bookworm / Trixie (2024-2026) nmcli (CLI) DEFAULT PICK: Use this for 95% of embedded projects.
Desktop Kiosk / Digital Signage Bookworm (Desktop) nm-connection-editor (GUI) Use only if a monitor and mouse are permanently attached.
Legacy Industrial Controller Bullseye / Buster /etc/dhcpcd.conf Use only if you are locked into an old OS image.

For the remainder of this guide, we are executing the Default Pick: configuring a headless Raspberry Pi 5 (4GB) via nmcli, paired with a physical GPIO status indicator so you can verify network state without a monitor.

Hardware Build: Headless Network Status Indicator

When a headless Pi drops off the network due to an IP conflict or DHCP timeout, SSH fails and you are left guessing. We will wire two LEDs to provide instant physical feedback on the static IP assignment status.

Parts List

  • Board: Raspberry Pi 5 (4GB variant) running Raspberry Pi OS Bookworm (64-bit)
  • LEDs: 2x 5mm Diffused LEDs (1x Green, 1x Red)
  • Resistors: 2x 330Ω through-hole resistors (for 3.3V GPIO current limiting)
  • Wiring: Half-size breadboard, 4x male-to-female jumper wires
  • Network: Cat6 Ethernet cable (Wi-Fi requires identical nmcli logic but different interface names)

Pin Mapping Table

Component Pi 5 GPIO Pin Physical Pin # Function
Green LED Anode (+) GPIO 17 11 Network OK (Static IP Bound)
Red LED Anode (+) GPIO 27 13 Network Fault (IP Missing/DHCP Fallback)
Both LED Cathodes (-) GND 9 & 14 Common Ground via 330Ω Resistors
⚠️ Hardware Warning: Never wire LEDs directly to GPIO pins without current-limiting resistors. A standard 5mm LED at 3.3V with a 330Ω resistor draws roughly 6mA, well within the Pi 5's 8mA per-pin safe limit.

Step-by-Step: Assigning the Static IP via nmcli

Connect to your Pi via temporary DHCP (or serial console) and execute these commands. We are targeting the onboard Ethernet. Note: On the Raspberry Pi 5, the onboard Ethernet interface is often named end0 instead of the legacy eth0 due to the PCIe bridge architecture.

  1. Identify your exact interface name:
    nmcli device status
    Look for the DEVICE column under the ethernet type. We will assume end0 for this build.
  2. Create a new static connection profile:
    sudo nmcli connection add type ethernet con-name 'static-lan' ifname 'end0' ipv4.addresses '192.168.1.50/24' ipv4.gateway '192.168.1.1' ipv4.dns '1.1.1.1,8.8.8.8' ipv4.method manual connection.autoconnect yes
  3. Disable the default DHCP connection (optional but recommended to prevent conflicts):
    sudo nmcli connection modify 'Wired connection 1' connection.autoconnect no
  4. Bring up the new static profile:
    sudo nmcli connection up 'static-lan'
  5. Verify the assignment:
    ip -4 addr show end0
    You should see inet 192.168.1.50/24 listed.

Python Verification Script

Save the following code as net_monitor.py. This script uses the psutil library to read the network interfaces and gpiozero to drive the status LEDs. It includes robust error handling for missing interfaces.

#!/usr/bin/env python3
import time
import psutil
from gpiozero import LED
from signal import pause

# Pin definitions matching our hardware build
GREEN_LED = LED(17)
RED_LED = LED(27)

TARGET_IP = '192.168.1.50'
TARGET_INTERFACE = 'end0' # Change to 'eth0' for Pi 4, 'wlan0' for Wi-Fi

def check_static_ip():
    try:
        addrs = psutil.net_if_addrs()
        if TARGET_INTERFACE not in addrs:
            print(f'Error: Interface {TARGET_INTERFACE} not found.')
            return False
        
        for addr in addrs[TARGET_INTERFACE]:
            # AF_INET is IPv4
            if addr.family == 2 and addr.address == TARGET_IP:
                return True
        return False
    except Exception as e:
        print(f'Unexpected error reading network: {e}')
        return False

def main():
    print(f'Monitoring {TARGET_INTERFACE} for static IP {TARGET_IP}...')
    try:
        while True:
            if check_static_ip():
                GREEN_LED.on()
                RED_LED.off()
            else:
                GREEN_LED.off()
                RED_LED.on()
            time.sleep(5)
    except KeyboardInterrupt:
        print('Monitor stopped.')
    finally:
        GREEN_LED.off()
        RED_LED.off()

if __name__ == '__main__':
    main()

Run it in the background using nohup python3 net_monitor.py & or set it up as a systemd service for boot persistence.

Troubleshooting: Exact Errors and Ranked Fixes

When configuring NetworkManager via CLI, syntax errors are common. Here are the exact error strings you will encounter and how to fix them.

First Three Things to Check When It Fails

  1. Interface Name Mismatch: Are you typing eth0 when the Pi 5 expects end0? Always run nmcli device first.
  2. CIDR vs Subnet Mask: nmcli requires CIDR notation (/24). If you type 255.255.255.0, the command will silently reject the IP or fail to apply the route.
  3. Ghost dhcpcd Service: If you upgraded from Bullseye to Bookworm in-place (rather than a fresh flash), dhcpcd might still be running and fighting NetworkManager. Run sudo systemctl disable --now dhcpcd.

Ranked Error Causes

Exact Error String Root Cause Fix
Error: unknown connection 'end0' You are trying to modify the physical device name instead of the NetworkManager connection profile name. Use the con-name you created (e.g., sudo nmcli connection modify 'static-lan' ...).
Warning: end0 is not a valid interface name Typo in the interface name, or you are targeting a USB-to-Ethernet adapter that enumerated as eth1. Verify exact spelling with ip link show.
RTNETLINK answers: File exists The static IP is already assigned to another interface, or a manual ip route command created a conflicting routing table entry. Flush the interface (sudo ip addr flush dev end0) and restart NetworkManager (sudo systemctl restart NetworkManager).

Extending and Simplifying the Build

To Simplify: If you are building a one-off desktop kiosk and have a monitor attached, abandon the CLI entirely. Open the terminal and type nm-connection-editor. This launches a full GUI where you can click through the IPv4 settings, select 'Manual', and type in your address and gateway without memorizing nmcli syntax flags.

To Extend: For industrial IoT deployments, a static IP is only half the battle. Extend the Python script above to publish the Pi's current IP and uptime to an MQTT broker (like Mosquitto) running on the same device. By binding the MQTT broker to 0.0.0.0 and using the static IP for your external sensor nodes to connect to, you create a robust, self-healing local telemetry gateway that survives router reboots and DHCP lease expirations.

For further reading on the underlying Debian networking stack, refer to the official Raspberry Pi NetworkManager documentation and the upstream nmcli manual.