The Short Answer: Static IP on Raspberry Pi 5 (Bookworm) vs Pi 4 (Bullseye)
If you are setting a static IP on a Raspberry Pi running Raspberry Pi OS Bookworm (the default for Pi 5 and current Pi 4 deployments), you must use NetworkManager via the nmcli command-line tool. The legacy dhcpcd.conf method used in Bullseye and Buster is completely deprecated and will fail on modern images.
For a quick assignment on Bookworm, the command is:
sudo nmcli con mod "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
The transition from dhcpcd to NetworkManager is the number one reason older tutorials fail today. Below is a data-dense breakdown of the network stack differences across recent Pi OS generations so you know exactly which toolchain to use.
| OS Release (Codename) | Default Network Manager | Configuration Method | Primary CLI Tool | Config File Location |
|---|---|---|---|---|
| Bookworm (2024-2026) | NetworkManager | Connection Profiles | nmcli / nmtui |
/etc/NetworkManager/system-connections/ |
| Bullseye (2021-2023) | dhcpcd | Daemon Config | systemctl / ifconfig |
/etc/dhcpcd.conf |
| Buster (2019-2021) | dhcpcd | Daemon Config | systemctl / ifconfig |
/etc/dhcpcd.conf |
| Ubuntu Server (Pi) | systemd-networkd / Netplan | YAML Netplan | netplan apply |
/etc/netplan/50-cloud-init.yaml |
Source: Raspberry Pi Official Documentation
Parts List and Interface Mapping
While networking is primarily a software configuration, robust embedded deployments require hardware feedback. If your Pi is headless (no monitor), a physical GPIO indicator saves you from plugging in an HDMI cable just to see if the static IP bound correctly.
Hardware BOM
- Board: Raspberry Pi 5 (8GB variant) or Raspberry Pi 4 Model B (4GB+)
- Storage: 64GB NVMe SSD via PCIe HAT (Pi 5) or High-Endurance microSD (Pi 4)
- Network: Cat6 Ethernet Cable (hardwired static IPs are vastly more reliable than WiFi for servers)
- Indicators: 2x 3mm LEDs (Green for Net OK, Red for Fault), 2x 330Ω resistors
- Wiring: Female-to-male jumper wires
GPIO Pin Mapping for Network Status
We map the network status to physical pins so our Python verification script can trigger hardware alerts. This satisfies the need for physical embedded feedback in headless racks.
| Function | GPIO Pin (BCM) | Physical Pin | Component | Wiring Note |
|---|---|---|---|---|
| Network OK | GPIO 17 | Pin 11 | Green LED + 330Ω Resistor | Anode to GPIO 17, Cathode to GND |
| Network Fault | GPIO 27 | Pin 13 | Red LED + 330Ω Resistor | Anode to GPIO 27, Cathode to GND |
| Common Ground | GND | Pin 9 / 14 | Shared LED Cathodes | Tie to Pi GND rail |
Step-by-Step: Setting a Static IP via NetworkManager (Pi 5 / Bookworm)
This is the modern, supported method for Raspberry Pi OS Bookworm. NetworkManager uses "connection profiles" rather than interface names, which prevents configuration breakage if your USB-Ethernet adapter changes from eth0 to eth1.
- Identify your active connection name:
Runnmcli con show. Look under the NAME column. For the onboard Ethernet, it is usuallyWired connection 1. For WiFi, it will be your SSID name. - Assign the Static IP, Gateway, and DNS:
Assuming your target IP is192.168.1.50on a standard /24 subnet:
sudo nmcli con mod "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 Autoconnect on conflicting profiles (Optional but recommended):
If you have multiple wired profiles, ensure only the correct one boots:
sudo nmcli con mod "Wired connection 1" connection.autoconnect yes - Apply the changes immediately without rebooting:
sudo nmcli con up "Wired connection 1" - Verify the assignment:
Runip -4 addr show eth0(orenp1s0f0depending on your Pi 5 kernel naming). You should seeinet 192.168.1.50/24.
sudo nmtui in the terminal. This opens a text-based UI that walks you through the exact same NetworkManager profile edits using arrow keys.
Step-by-Step: Setting a Static IP via dhcpcd (Pi 4 / Bullseye & Legacy)
If you are maintaining an older Pi 4 fleet running Bullseye, or using a legacy industrial image, dhcpcd is still your daemon. Do not use this method on Bookworm; the dhcpcd service is masked and will silently fail.
- Open the dhcpcd configuration file:
sudo nano /etc/dhcpcd.conf - Scroll to the bottom and append your static profile:
interface eth0 static ip_address=192.168.1.50/24 static routers=192.168.1.1 static domain_name_servers=1.1.1.1 8.8.8.8 - Save and exit: Press
Ctrl+O,Enter, thenCtrl+X. - Restart the daemon:
sudo systemctl restart dhcpcd
Verification Code: Python Network Fallback Script
In embedded deployments, you need to know if the static IP actually bound to the interface, or if a cable fault caused a fallback to an APIPA address (169.254.x.x). The following Python 3 script targets the Raspberry Pi 5 (Bookworm). It checks the IP, verifies subnet validity, and triggers the GPIO LEDs mapped in our pin table.
Dependencies: Uses gpiozero (pre-installed on Pi OS) and standard library modules. No pip installs required.
#!/usr/bin/env python3
"""
Network Status Verification Script
Target: Raspberry Pi 5 (Bookworm) / Python 3.11+
Purpose: Verify static IP assignment and trigger GPIO hardware alerts.
"""
import socket
import subprocess
import time
import sys
from gpiozero import LED
# --- PIN DEFINITIONS ---
# Mapped to physical hardware indicators on the workbench
PIN_NET_OK = 17 # Green LED
PIN_NET_FAIL = 27 # Red LED
# --- NETWORK TARGETS ---
EXPECTED_SUBNET = "192.168.1."
TARGET_INTERFACE = "eth0" # Change to 'wlan0' or 'enp1s0f0' if applicable
def get_interface_ip(interface):
"""Fetches the IPv4 address of a specific interface using system tools."""
try:
# Using 'ip' command is more reliable than socket.gethostbyname() on headless Pi
cmd = f"ip -4 addr show {interface} | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){{3}}'"
result = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL).decode('utf-8').strip()
return result if result else None
except subprocess.CalledProcessError:
return None
def main():
# Initialize GPIO LEDs (Active High wiring assumed)
led_ok = LED(PIN_NET_OK)
led_fail = LED(PIN_NET_FAIL)
print(f"[*] Monitoring interface {TARGET_INTERFACE} for subnet {EXPECTED_SUBNET}...")
try:
while True:
ip_addr = get_interface_ip(TARGET_INTERFACE)
if ip_addr and ip_addr.startswith(EXPECTED_SUBNET):
led_ok.on()
led_fail.off()
print(f"[+] Network OK: Bound to {ip_addr}")
else:
led_ok.off()
led_fail.blink(on_time=0.5, off_time=0.5)
print(f"[-] Network FAULT: Current IP is {ip_addr or 'None (Link Down)'}")
time.sleep(10) # Poll every 10 seconds to avoid CPU spam
except KeyboardInterrupt:
print("\n[!] Script terminated by user. Cleaning up GPIO.")
led_ok.off()
led_fail.off()
sys.exit(0)
if __name__ == "__main__":
main()
Troubleshooting: "Connection Not Found" and DHCP Clashes
When configuring static IPs on embedded Linux, things go wrong. Here are the exact error strings you will encounter, ranked by frequency, and how to fix them.
1. Error: Error: unknown connection 'eth0'
- Cause: You are using Bookworm/NetworkManager and trying to pass the interface name (
eth0) instead of the connection profile name. NetworkManager separates the physical device from the logical connection. - Fix: Run
nmcli con showto find the exact profile name (usuallyWired connection 1) and use that in yournmcli con modcommand.
2. Error: Failed to start dhcpcd.service: Unit dhcpcd.service not found.
- Cause: You are following an outdated 2022 tutorial on a modern Bookworm installation. The
dhcpcdpackage has been removed from the default image. - Fix: Abandon the
dhcpcd.confmethod entirely. Switch to thenmclisteps outlined in the first half of this guide.
3. Error: RTNETLINK answers: File exists (when bringing up interface)
- Cause: IP address collision. Another device on your LAN already holds
192.168.1.50, or you have a duplicate static entry in your router's DHCP reservation table. - Fix: Ping the target IP from another machine. If it replies, change your Pi's static IP or clear the router's DHCP lease for that MAC address.
- Subnet Mask Math: Did you type
192.168.1.50/24or just192.168.1.50? NetworkManager requires the CIDR prefix length, or it will assume a /32 (host-only) route and drop your gateway. - Gateway Reachability: Can the Pi ping its own gateway (
ping 192.168.1.1)? If not, your static IP is on the wrong VLAN or subnet. - DNS Resolution vs Routing: If you can ping
8.8.8.8but cannot load a webpage, your IP and Gateway are fine, but youripv4.dnsparameter innmcliis missing or typo'd.
Extending and Simplifying Your Network Build
Once your static IP is locked in, you can optimize the deployment for headless embedded environments.
Simplify: Use mDNS (Avahi) for Local Discovery
If you only need a static IP so you don't have to scan the network to find your Pi, you might not need a static IP at all. Raspberry Pi OS includes avahi-daemon by default. You can simply SSH into raspberrypi.local (or myhostname.local) regardless of what IP the DHCP server hands out. This simplifies fleet deployment immensely.
Extend: Headless First-Boot Configuration
If you are building 20 Pis for a sensor network, typing nmcli on each one is a waste of time. Instead, use the Raspberry Pi Imager's "OS Customisation" menu (the gear icon) before flashing the SD card. You can inject the WiFi SSID, static IP, and SSH keys directly into the firstrun.sh script that executes on the very first boot. For enterprise Linux deployments, look into NetworkManager's keyfile drop-in directories to copy a pre-configured .nmconnection file directly into the /boot/firmware/ partition.
Setting a static IP on a Raspberry Pi is no longer the simple text-file edit it was five years ago, but the shift to NetworkManager provides vastly superior reliability for embedded systems that swap network interfaces or boot from different USB topologies. Stick to nmcli on Bookworm, verify with physical GPIO feedback, and your headless deployments will stay online.






