To set a static IP on Raspberry Pi OS Bookworm or newer, you must use NetworkManager via the nmcli command line tool, as the legacy dhcpcd daemon is deprecated. For a headless Pi 5 on a wired connection, the exact command to assign 192.168.1.50 is: 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 followed by nmcli con up "Wired connection 1". This guide covers the exact hardware integration, verification code, and debugging steps for a permanent embedded deployment.
The Decision Path: DHCP Reservation vs. True Static IP
Before locking in your network configuration, you must decide where the IP assignment lives. In embedded IoT deployments, making the wrong choice leads to IP conflicts or unreachable nodes after a power outage. Use this decision tree to terminate on the correct method for your build.
| Deployment Scenario | Network Environment | Recommended Method |
|---|---|---|
| Roaming test bench / Laptop | Multiple unknown routers | DHCP (Auto) |
| Home Lab / Media Server | Single router, full admin access | Router-side DHCP Reservation (MAC binding) |
| Industrial IoT / Headless Sensor Node | Managed switch, strict VLANs, no router access | Local Static IP via NetworkManager (Pick This) |
Default Recommendation: For any Raspberry Pi deployed inside an enclosure with a PoE HAT acting as a fixed sensor or MQTT gateway, configure a Local Static IP on the Pi itself. Do not rely on router reservations; if the router is replaced or reset, your headless Pi becomes unreachable without a monitor and keyboard.
Hardware Spec Sheet & Pin Mapping
This guide assumes a wired, industrial-style deployment. We are using Power over Ethernet (PoE) to eliminate the need for a separate 5V USB-C power supply, which is a common point of failure in field deployments due to connector vibration and voltage drop over long USB cables.
Parts List
- Board: Raspberry Pi 5 (8GB variant) - Target for all code and OS commands below.
- OS: Raspberry Pi OS Bookworm (64-bit, Lite)
- HAT: Official Raspberry Pi PoE+ HAT (Supports 802.3at, delivers up to 20W to the board)
- Networking: Cat6 solid-core Ethernet cable (run under 90 meters)
- Indicators: 2x 3mm LEDs with 330Ω current-limiting resistors for status feedback
Pin Mapping Table
The PoE+ HAT handles power delivery via the 40-pin header's 5V/GND rails, but it also requires I2C for thermal management. Furthermore, we are mapping two GPIO pins for physical network status LEDs, which our Python script will drive.
| Component | Pi 5 GPIO / Pin | Function | Notes |
|---|---|---|---|
| PoE+ HAT I2C SDA | GPIO 2 (Pin 3) | Fan controller comms | Do not use for other I2C sensors without a multiplexer |
| PoE+ HAT I2C SCL | GPIO 3 (Pin 5) | Fan controller comms | Pull-ups are provided on the HAT |
| PoE+ HAT 5V Power | Pins 2, 4 | Main 5V rail injection | Ensure HAT standoffs are tight to prevent arcing |
| Status LED 1 (Green) | GPIO 17 (Pin 11) | Static IP Verified | Connect anode to GPIO 17, cathode to GND via 330Ω |
| Status LED 2 (Red) | GPIO 27 (Pin 13) | Network / IP Error | Connect anode to GPIO 27, cathode to GND via 330Ω |
Step-by-Step: Configuring NetworkManager for a Static IP
With the hardware assembled and the Pi booted into Bookworm, SSH into the device (it will have a DHCP address initially). We will use nmcli (NetworkManager Command Line Interface) to overwrite the default wired profile. For deeper reading on NetworkManager's architecture, refer to the official NetworkManager nmcli documentation.
- Identify the active connection name:
nmcli con show --active
Look for the wired Ethernet connection. On a fresh Pi OS install, it is usually namedWired connection 1oreth0. We will useWired connection 1for this guide. - Assign the static IP and Subnet Mask:
nmcli con mod "Wired connection 1" ipv4.addresses 192.168.1.50/24
The/24is critical. It defines the subnet mask (255.255.255.0). Omitting it will cause routing failures. - Set the Default Gateway:
nmcli con mod "Wired connection 1" ipv4.gateway 192.168.1.1
This must match your router or Layer 3 switch's VLAN interface IP. - Define DNS Servers:
nmcli con mod "Wired connection 1" ipv4.dns "1.1.1.1 8.8.8.8"
Space-separated list. Required if your Pi needs to resolve external hostnames for MQTT brokers or NTP sync. - Switch the IPv4 method from auto to manual:
nmcli con mod "Wired connection 1" ipv4.method manual
This tells NetworkManager to stop sending DHCPDISCOVER broadcasts on boot. - Apply and restart the connection:
nmcli con up "Wired connection 1"
Warning: If you are connected via SSH over Ethernet, your session will drop immediately upon executing this command. Reconnect using the new static IP (192.168.1.50).
Verification Code: Python Network & PoE HAT Monitor
In headless embedded systems, you cannot rely on a monitor to verify network status. The following Python script targets the Raspberry Pi 5 (8GB). It verifies that the assigned IP matches our target static IP, checks the PoE HAT's I2C bus presence, and drives the physical GPIO LEDs mapped in our pin table.
Prerequisites: Install the required libraries via sudo apt install python3-gpiozero python3-smbus2.
import socket
import time
import sys
from gpiozero import LED
from smbus2 import SMBus
# --- Pin & Hardware Definitions ---
TARGET_STATIC_IP = '192.168.1.50'
I2C_BUS_ID = 1 # Pi 5 uses I2C bus 1 on GPIO 2/3
POE_HAT_I2C_ADDR = 0x45 # Default address for PoE+ HAT fan controller
# GPIO Pin Mapping for Status LEDs
led_ip_ok = LED(17) # Green LED on GPIO 17
led_ip_err = LED(27) # Red LED on GPIO 27
def get_local_ip():
"""Fetches the primary IPv4 address of the eth0 interface."""
try:
# Create a UDP socket to a public DNS to force OS to select the primary route
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 Error: {e}')
return None
def check_poe_hat_i2c():
"""Verifies the PoE HAT is seated and responding on the I2C bus."""
try:
with SMBus(I2C_BUS_ID) as bus:
# Attempt to read a single byte from the HAT controller
bus.read_byte(POE_HAT_I2C_ADDR)
return True
except OSError as e:
print(f'I2C Hardware Fault: PoE HAT not detected on bus {I2C_BUS_ID}. Error: {e}')
return False
def main():
print('Starting Embedded Network Monitor...')
# 1. Verify Hardware Seating
if not check_poe_hat_i2c():
print('WARNING: PoE HAT I2C communication failed. Check physical seating.')
else:
print('PoE HAT I2C handshake successful.')
# 2. Verify Network Configuration
current_ip = get_local_ip()
if current_ip is None:
print('CRITICAL: No network interface active.')
led_ip_err.blink(on_time=0.2, off_time=0.2) # Fast blink = No Link
sys.exit(1)
if current_ip == TARGET_STATIC_IP:
print(f'SUCCESS: Static IP verified ({current_ip}).')
led_ip_ok.on()
led_ip_err.off()
else:
print(f'ERROR: IP Mismatch. Expected {TARGET_STATIC_IP}, got {current_ip}.')
print('Fallback to DHCP detected. Check nmcli configuration.')
led_ip_err.on()
led_ip_ok.off()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\nMonitor stopped by user.')
led_ip_ok.off()
led_ip_err.off()
sys.exit(0)
Debugging: "Connection Failed" and IP Assignment Errors
When configuring static IPs via the command line, typos in subnet math or interface names will lock you out of the device. If you lose SSH access, plug in a HDMI monitor and USB keyboard to debug locally. For more on Raspberry Pi network troubleshooting, consult the Raspberry Pi OS Configuration Documentation.
Exact Error Strings and Ranked Causes
Error 1: Error: Connection activation failed: No suitable device found for this connection
- Cause A (Most Likely): The connection name is wrong. You typed
eth0but NetworkManager named itWired connection 1. Runnmcli con showto get the exact string. - Cause B: The Ethernet cable is unplugged or the PoE switch port is administratively down. NetworkManager will not activate a wired profile if the physical link layer (carrier) is down.
Error 2: RTNETLINK answers: File exists
- Cause A (Most Likely): You are trying to add a static IP route or address that is already assigned to another active interface (e.g., you left a static IP on
wlan0that overlaps with youreth0subnet). - Cause B: Duplicate IP conflict on the LAN. Another device is already holding 192.168.1.50, and the kernel is rejecting the route addition.
The First Three Things to Check When It Fails
- Check Interface State: Run
ip link show. Ensure your Ethernet interface (usuallyeth0orend0on Pi 5) showsstate UP. If it saysDOWN, you have a physical layer issue (cable, switch port, or PoE failure). - Check Subnet Math: Run
ip route. Ensure your gateway (e.g., 192.168.1.1) falls within the subnet range defined by your CIDR mask (e.g., /24). If your IP is 192.168.1.50/24, but your gateway is 192.168.2.1, routing will fail silently. - Check for Rogue DHCP: Run
cat /etc/resolv.conf. If you see a local router IP instead of the DNS servers you specified, a DHCP client daemon (like a lingeringdhcpcdservice) is overriding NetworkManager. Disable it viasudo systemctl disable dhcpcd.
Extending and Simplifying the Build
How to Extend: Add 802.1Q VLAN Tagging
In industrial environments, IoT sensors are rarely placed on the primary corporate LAN. You can extend this NetworkManager configuration to tag traffic for a specific VLAN (e.g., VLAN 40 for IoT devices) without touching the router. Run:
nmcli con add type vlan con-name 'IoT-VLAN40' dev eth0 id 40 ip4 10.0.40.50/24 gw4 10.0.40.1 ipv4.method manual
This creates a virtual interface (vlan40) that isolates your Pi's MQTT traffic from the main network, drastically improving security.
How to Simplify: The Emergency Fallback
If you misconfigure the static IP and lock yourself out of a headless Pi 5 mounted inside a sealed NEMA enclosure, you do not need to disassemble the hardware to fix it. Simplify your recovery by relying on Router-Side MAC Binding as a backup.
If the Pi fails to boot with its local static IP (e.g., due to a corrupted NetworkManager config file), it will eventually time out and fallback to a DHCP request if you configure the connection with ipv4.may-fail yes. Your router will then assign it a temporary IP based on its MAC address, allowing you to SSH back in and fix the nmcli syntax without opening the enclosure.






