If you are trying to figure out how to assign a static IP to Raspberry Pi boards running the latest OS, the very first thing you need to know is that the old methods are dead. In Raspberry Pi OS Bookworm (Debian 12) and newer, the dhcpcd daemon has been entirely replaced by NetworkManager. Editing /etc/dhcpcd.conf will do absolutely nothing. To assign a static IP on a modern Pi, you must use the nmcli command-line tool to modify the active connection profile, setting the IPv4 method to manual with your desired address, gateway, and DNS servers.
This guide targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS Bookworm (64-bit). We will cover the exact nmcli commands, map the hardware interfaces, provide a Python script to validate your network routing via a physical GPIO LED, and troubleshoot the exact error strings NetworkManager throws when things go wrong.
Hardware & Software Requirements
Before modifying network stacks, ensure your physical layer and OS baseline match the assumptions in this guide. NetworkManager behaves differently on Wi-Fi versus Ethernet, and power brownouts on the Pi 5 can corrupt network state files during reboots.
| Component | Exact Variant / Specification | Notes |
|---|---|---|
| Compute Board | Raspberry Pi 5 (8GB RAM) | Target board for this guide; 4GB works identically. |
| Power Supply | Official 27W USB-C PD PSU | Required to prevent USB/Ethernet PHY brownouts. |
| Storage | 32GB+ MicroSD (A2 Class) | Flashed with Pi OS Bookworm (64-bit, Desktop or Lite). |
| Network Link | Cat6 Shielded Ethernet Cable | Direct to router or unmanaged switch; avoid USB-Ethernet adapters. |
| Diagnostic LED | 5mm Green LED + 330Ω Resistor | For physical network status monitoring via GPIO 17. |
Difficulty Rating: Intermediate (Requires basic Linux CLI navigation and understanding of CIDR notation like /24).
Time Required: 15 minutes for configuration, plus 10 minutes for hardware validation.
The Bookworm Shift: Why dhcpcd.conf is Dead
For years, the standard answer to assigning a static IP on a Pi was appending a block of code to /etc/dhcpcd.conf. With the release of Raspberry Pi OS Bookworm, the foundation shifted to align with standard Debian 12 practices. The Pi Foundation adopted NetworkManager as the default network stack for both the desktop GUI and headless Lite images.
NetworkManager uses connection profiles stored in /etc/NetworkManager/system-connections/ rather than a single flat text file. This means IP assignments are tied to specific interface profiles (e.g., 'Wired connection 1' or 'preconfigured') rather than global interface names like eth0. This shift improves reliability when hot-swapping USB Wi-Fi dongles or dealing with MAC address randomization, but it breaks every tutorial written before late 2023.
Step-by-Step: Assigning the Static IP via nmcli
We will assign the static IP 192.168.1.50 to the Ethernet interface. We assume your router's gateway is 192.168.1.1 and your subnet is /24 (255.255.255.0).
- Identify the active connection name:
Runnmcli connection show. Look at the 'NAME' column. For a fresh Pi OS install plugged into Ethernet, it is usuallyWired connection 1. (Do not confuse the NAME with the DEVICE column, which will sayeth0orend0). - Modify the IPv4 parameters:
Execute the following command, replacing the connection name if yours differs:
sudo nmcli connection modify "Wired connection 1" 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 - Disable MAC address randomization (Optional but recommended for headless nodes):
sudo nmcli connection modify "Wired connection 1" ethernet.cloned-mac-address permanent - Apply the changes:
Bounce the connection to force NetworkManager to apply the new profile:
sudo nmcli connection up "Wired connection 1" - Verify the assignment:
Runip -4 addr show end0(oreth0). You should seeinet 192.168.1.50/24listed as a valid, non-tentative address.
wifi-sec.key-mgmt wpa-psk and wifi-sec.psk "yourpassword" during the modify step.
Hardware Interface & Diagnostic Pin Mapping
When running headless, you lose the visual comfort of a desktop network icon. Mapping a physical LED to a network validation script gives you instant visual feedback on the bench. Below is the interface and pin mapping used for this build.
| Interface / Pin | Physical Location | Function | Configuration Notes |
|---|---|---|---|
| end0 (Ethernet) | RJ45 Jack | Primary Wired Data | Auto-MDIX enabled; use Cat5e or better. |
| wlan0 (Wi-Fi) | Onboard PCB Antenna | Secondary Wireless | 2.4GHz & 5GHz; disable if not used to save thermal overhead. |
| GPIO 17 | Header Pin 11 | Diagnostic LED Anode | Requires 330Ω current-limiting resistor in series. |
| GND | Header Pin 9 | Diagnostic LED Cathode | Common ground reference for the LED circuit. |
Python Network Validation Script
This Python script targets the Pi 5's GPIO 17. It attempts a TCP socket connection to your router's DNS port (53) every 3 seconds. If the static IP is routing correctly, the LED stays solid green. If the network drops or the IP conflicts, the LED turns off, and the console logs the exact socket error.
import socket
import time
import sys
from gpiozero import LED
from gpiozero.exc import PinFactoryFallback, GPIOPinMissing
# Hardware Pin Definition (Mapped to physical Pin 11 on the 40-pin header)
DIAGNOSTIC_LED_PIN = 17
GATEWAY_IP = "192.168.1.1"
GATEWAY_PORT = 53 # DNS port, reliably open on almost all home routers
try:
net_led = LED(DIAGNOSTIC_LED_PIN)
except (PinFactoryFallback, GPIOPinMissing, Exception) as e:
print(f"[WARN] GPIO initialization failed: {e}. Defaulting to console-only mode.")
net_led = None
def is_gateway_reachable():
"""Attempts a non-blocking TCP connect to verify Layer 3 routing."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(2.0)
result = sock.connect_ex((GATEWAY_IP, GATEWAY_PORT))
return result == 0
except socket.error as e:
print(f"[ERR] Socket exception: {e}")
return False
def main():
print(f"Monitoring connectivity to {GATEWAY_IP} via static IP...")
while True:
if is_gateway_reachable():
if net_led: net_led.on()
print("[OK] Static IP routing verified.")
else:
if net_led: net_led.off()
print("[FAIL] Gateway unreachable. Check IP config or physical link.")
time.sleep(3)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nMonitor stopped by user.")
if net_led: net_led.off()
sys.exit(0)
Troubleshooting: Exact Errors & Ranked Causes
NetworkManager is notoriously strict about syntax and state. If your static IP assignment fails, these are the first three things to check:
- Profile Name Mismatch: You typed
eth0instead of the actual profile name (likeWired connection 1). - DHCP Pool Collision: Your chosen static IP (
192.168.1.50) is inside your router's active DHCP range, and the router just leased it to your phone. - Subnet Mask Error: You used
/32or omitted the CIDR notation entirely, isolating the Pi from the local gateway.
Below are the exact error strings NetworkManager throws, ranked by frequency, with their fixes.
1. Error: Connection 'Wired connection 1' not found.
Cause: The profile name you passed to nmcli connection modify does not exist in NetworkManager's database. This often happens if the Pi has only ever connected via Wi-Fi, or if the OS generated a default profile named preconfigured or eth0 during headless imaging.
Fix: Run nmcli connection show to list all profiles. Copy the exact string from the 'NAME' column, including spaces, and wrap it in quotes in your modify command.
2. Error: NetworkManager is not running.
Cause: The NetworkManager systemd service has crashed, is masked, or you are running a legacy Bullseye image where dhcpcd is still holding the network stack hostage.
Fix: Check service status with systemctl status NetworkManager. If it is dead, restart it via sudo systemctl restart NetworkManager. If you are on Bullseye, upgrade to Bookworm or revert to dhcpcd.conf editing.
3. RTNETLINK answers: File exists
Cause: You are trying to bring up a connection profile that is already active, or there is a lingering IP address bound to the interface from a previous manual ip addr add command that conflicts with NetworkManager's state.
Fix: Flush the interface and restart the service:
sudo ip addr flush dev end0
sudo nmcli connection down "Wired connection 1" && sudo nmcli connection up "Wired connection 1"
Extending or Simplifying the Build
To Simplify (Router-Side DHCP Reservation): If you don't want to manage IP assignments on the Pi itself, the easiest alternative is to leave the Pi set to ipv4.method auto (DHCP). Log into your router's admin panel, find the Pi's MAC address (via ip link show end0), and bind it to 192.168.1.50 in the router's DHCP reservation table. The Pi will always request an IP, and the router will always hand it the same static address. This prevents IP collisions and survives OS re-flashes.
To Extend (Interface Bonding): For critical headless deployments (like remote solar monitoring or 3D printer farms), you can extend this build by configuring NetworkManager to bond eth0 and wlan0 into a single logical interface. If the Ethernet cable is unplugged, the Pi seamlessly fails over to Wi-Fi without dropping the SSH session or changing the static IP address. Refer to the Debian NetworkManager Wiki for the specific nmcli connection add type bond syntax required to set up active-backup bonding.
Frequently Asked Questions
How to assign a static IP to Raspberry Pi headless (without a monitor)?
If you are imaging a Pi using the official Raspberry Pi Imager on your desktop, click the 'gear' icon (or press Ctrl+Shift+X) to open the Advanced Options. In the 'Network Settings' section, check 'Use custom network settings'. You can input your desired static IP, gateway, and DNS servers directly into the GUI. The Imager will write a NetworkManager configuration file to the boot partition, and the Pi will boot up with the static IP already applied, ready for SSH.
Why did my dhcpcd.conf static IP stop working in Raspberry Pi OS Bookworm?
Raspberry Pi OS Bookworm (Debian 12) completely removed the dhcpcd package in favor of NetworkManager. The dhcpcd.conf file is ignored by the system because the daemon that reads it no longer exists or is disabled by default. You must migrate your static IP configurations to nmcli or the nmtui text-based user interface to restore functionality.
How to assign a static IP to Raspberry Pi via router DHCP reservation instead?
Leave your Raspberry Pi configured for dynamic IP assignment (the default state). Find your Pi's MAC address by running cat /sys/class/net/end0/address in the terminal. Log into your router's web interface, navigate to the DHCP or LAN settings, and add a 'Static Lease' or 'Address Reservation'. Paste the Pi's MAC address and assign your desired IP (e.g., 192.168.1.50). Reboot the Pi, and it will automatically receive that exact IP from the router every time it connects.






