If you are searching for how to set static IP on Raspberry Pi 5, the first thing you need to know is that almost every tutorial written before late 2023 is wrong for your board. The Raspberry Pi 5 ships with Raspberry Pi OS Bookworm (Debian 12), which completely removed the dhcpcd daemon in favor of NetworkManager. If you try to edit /etc/dhcpcd.conf, you will either get a blank file or break your network stack.

The direct answer: To set a static IP on a headless Pi 5, use the nmcli command-line tool to modify the active NetworkManager profile. For a desktop environment, use the nm-connection-editor GUI. However, for 90% of home lab deployments, the most robust method is actually setting a DHCP reservation on your router rather than touching the Pi's OS configuration.

The Bookworm Shift: Why Old Static IP Tutorials Fail

In previous OS versions (Bullseye and earlier), networking was handled by dhcpcd. You would append a few lines to /etc/dhcpcd.conf, reboot, and your Pi would hold its address. With the Pi 5 and Bookworm, Raspberry Pi transitioned to NetworkManager to align with upstream Debian standards and improve support for complex routing, VPNs, and cellular modems.

NetworkManager uses connection profiles rather than interface names. This means you don't assign an IP to eth0; you assign it to a profile named "Wired connection 1". Understanding this distinction is the key to avoiding the most common configuration loops.

Decision Path: Which Method Should You Use?

Your ScenarioEnvironmentMethod to Use
Headless Pi in a home network with a standard routerHome Lab / MakerRouter DHCP Reservation (Simplifies Pi config)
Headless Pi on an isolated switch or direct-to-laptopIndustrial / Kiosknmcli (CLI) (Required, no router present)
Pi 5 connected to a monitor, mouse, and keyboardDesktop Workstationnm-connection-editor (GUI)

Terminating Default Pick: If you have access to your router's admin panel, use a Router DHCP Reservation mapped to the Pi's MAC address. It survives OS reimaging and prevents IP conflicts. If you are deploying a headless kiosk or an isolated sensor node where no router exists, use the nmcli method detailed below.

Hardware & Interface Mapping

While setting an IP is a software task, verifying network health on a headless Pi 5 often requires hardware feedback. Below is the parts list and pin mapping for a physical network-status indicator, which we will drive with Python later in this guide.

Parts List

  • Board: Raspberry Pi 5 (8GB variant, SKU SC1148) running Raspberry Pi OS Bookworm 64-bit.
  • Network: CAT6 Ethernet cable (for Gigabit stability) or Wi-Fi 5 (dual-band).
  • Indicators: 2x 5mm LEDs (Green for OK, Red for Fault), 2x 330Ω through-hole resistors.
  • Wiring: Breadboard and female-to-male jumper wires.

Hardware-to-Network Interface & Status Pin Mapping

Logical InterfaceNetworkManager Profile Name (Default)Physical HardwareGPIO Pin Mapping (Status LEDs)
eth0Wired connection 1Pi 5 Gigabit Ethernet JackGPIO 17 (Pin 11) -> Green LED (Link OK)
wlan0SSID_Name (e.g., HomeWiFi)Pi 5 onboard Wi-Fi/BT moduleGPIO 27 (Pin 13) -> Red LED (IP Fault/Fallback)
N/AN/ACommon GroundGND (Pin 9) -> LED Cathodes

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

This procedure assumes you are connected via SSH or a direct serial console. We will configure the Ethernet interface. Ensure you know your network's subnet (usually /24 or 255.255.255.0) and gateway before starting.

Verify Dead / Safety Check: If you are doing this over SSH and make a typo in the gateway or subnet mask, you will lock yourself out of the Pi. If you don't have physical access to pull the SD card or attach a monitor, use tmux or screen, or set a cron job to revert the network settings after 5 minutes while you test.

  1. Identify your active connection profile name.
    Run: nmcli connection show
    Look under the "NAME" column. For Ethernet, it is typically Wired connection 1. Do not use the "DEVICE" column name (eth0) for modification commands.
  2. Assign the static IP address and subnet mask.
    Run: sudo nmcli connection modify "Wired connection 1" ipv4.addresses 192.168.1.50/24
    Note: NetworkManager requires CIDR notation. Use /24 for a standard home subnet, not 255.255.255.0.
  3. Set the default gateway.
    Run: sudo nmcli connection modify "Wired connection 1" ipv4.gateway 192.168.1.1
  4. Configure DNS servers.
    Run: sudo nmcli connection modify "Wired connection 1" ipv4.dns "1.1.1.1 8.8.8.8"
    Separate multiple DNS servers with a space, not a comma.
  5. Switch the IPv4 method from DHCP to manual.
    Run: sudo nmcli connection modify "Wired connection 1" ipv4.method manual
  6. Apply the changes by restarting the connection.
    Run: sudo nmcli connection up "Wired connection 1"
  7. Verify the assignment.
    Run: ip -4 addr show eth0. You should see inet 192.168.1.50/24 listed.

Debugging: Exact Error Strings and Ranked Causes

When configuring NetworkManager via CLI, typos and legacy assumptions trigger specific errors. Here are the first three things to check when your connection fails to apply, mapped to the exact error strings you will see.

Error 1: Error: unknown connection 'eth0'

Ranked Causes:

  1. Using device name instead of profile name: You typed nmcli connection modify eth0. NetworkManager manages profiles, not raw interfaces. Fix: Use "Wired connection 1".
  2. Profile was deleted or renamed: Run nmcli connection show to verify the exact string in the NAME column.

Error 2: bash: /etc/dhcpcd.conf: No such file or directory

Ranked Causes:

  1. Following outdated tutorials: You are on Bookworm (Debian 12) or later. dhcpcd is uninstalled by default. Fix: Stop editing text files and use nmcli as shown above.
  2. Custom minimal OS image: If you are using DietPi or a custom Yocto build, networking might be handled by systemd-networkd. Check with systemctl status systemd-networkd.

Error 3: RTNETLINK answers: File exists

Ranked Causes:

  1. Duplicate IP routing: You manually added a route using the ip route command, and now NetworkManager is trying to add the same route upon bringing the interface up. Fix: Flush the manual routes with sudo ip route flush dev eth0 before running nmcli connection up.
  2. IP Conflict on the LAN: Another device already holds 192.168.1.50. While this usually results in a silent failure or ARP warnings, strict network namespaces can throw routing errors. Fix: Ping the target IP from another machine before assigning it.

Python Network Verification Script (GPIO Feedback)

To ensure your Pi 5 maintains its static IP and hasn't fallen back to an APIPA address (169.254.x.x) after a network hiccup, we can use a Python script. This script queries the network state and drives the GPIO pins mapped in our hardware table.

This code targets the Raspberry Pi 5 8GB running Bookworm 64-bit. It uses the pre-installed gpiozero library and standard Python socket modules to avoid pip dependency issues.

#!/usr/bin/env python3
"""
Pi 5 Network Status Monitor
Targets: Raspberry Pi 5 (Bookworm 64-bit)
Hardware: Green LED on GPIO 17, Red LED on GPIO 27
"""

import socket
import subprocess
import time
import sys
from gpiozero import LED

# --- PIN DEFINITIONS ---
PIN_LED_OK = 17    # Green LED: Valid Static/DHCP IP assigned
PIN_LED_FAULT = 27 # Red LED: Fallback IP (169.254.x.x) or disconnected

# Initialize GPIO
green_led = LED(PIN_LED_OK)
red_led = LED(PIN_LED_FAULT)

def get_ip_address(interface='eth0'):
    """Fetches the IPv4 address of the specified interface using iproute2."""
    try:
        # Using subprocess to avoid deprecated netifaces library issues on Bookworm
        cmd = f"ip -4 addr show {interface} | grep -oP '(?<=inet\s)\d+(\.\d+){{3}}'"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        ip = result.stdout.strip()
        return ip if ip else None
    except subprocess.CalledProcessError:
        return None

def check_network_health(ip_addr):
    """Returns True if IP is valid (not APIPA/fallback), False otherwise."""
    if not ip_addr:
        return False
    # APIPA / Link-Local fallback range indicates DHCP/Static failure
    if ip_addr.startswith("169.254."):
        return False
    return True

def main():
    print("Starting Pi 5 Network Monitor...")
    print(f"Monitoring interface: eth0")
    
    try:
        while True:
            current_ip = get_ip_address('eth0')
            is_healthy = check_network_health(current_ip)
            
            if is_healthy:
                green_led.on()
                red_led.off()
                print(f"[OK] Link stable. IP: {current_ip}")
            else:
                green_led.off()
                red_led.blink(on_time=0.5, off_time=0.5)
                print(f"[FAULT] Network degraded. IP: {current_ip or 'None'}")
                
            time.sleep(10) # Poll every 10 seconds
            
    except KeyboardInterrupt:
        print("\nMonitor stopped by user.")
    except Exception as e:
        print(f"Critical Error: {e}")
        red_led.on() # Solid red on script crash
    finally:
        # Clean up GPIO states on exit
        green_led.off()
        red_led.off()

if __name__ == "__main__":
    main()

First three things to check when this script fails to run:

  1. GPIO Permissions: Bookworm handles GPIO via the gpio group, not root. Ensure your user is in the group: sudo usermod -aG gpio $USER, then log out and back in.
  2. Interface Name Mismatch: If you are testing over Wi-Fi, change get_ip_address('eth0') to get_ip_address('wlan0').
  3. Missing iproute2: The script relies on the ip command. If you stripped down your OS, install it via sudo apt install iproute2.

Extending and Simplifying the Build

How to Simplify: The Router DHCP Reservation

If you are building a home automation server, a Pi-hole, or a media center, do not set the static IP on the Pi itself. Instead, log into your router (e.g., UniFi, pfSense, or standard ISP gateway), find the DHCP lease table, locate the Pi 5's MAC address, and assign a fixed IP there. This keeps the Pi's OS configuration default (DHCP), meaning if you ever swap SD cards or reinstall the OS, your network configuration survives intact without needing to re-run nmcli commands.

How to Extend: Ansible and Fleet Management

If you are deploying multiple Pi 5 nodes (e.g., a Kubernetes cluster or a sensor array), manually running nmcli via SSH does not scale. Extend this build by using Ansible's community.general.nmcli module. You can define your static IP matrix in a YAML inventory file and push the NetworkManager profiles to 50 Pi 5 boards simultaneously, complete with rollback handlers if the gateway ping fails.

For advanced edge deployments where the Pi 5 is connected to multiple VLANs via a managed switch, you can extend the nmcli configuration to include 802.1Q VLAN tagging directly on the "Wired connection 1" profile, allowing a single physical Gigabit port to service isolated IoT and management networks concurrently.