If you are still trying to edit /etc/dhcpcd.conf to set a static IP, stop immediately. That file is dead. Starting with Raspberry Pi OS Bookworm and continuing into Trixie, the Raspberry Pi Foundation completely replaced dhcpcd with NetworkManager. Attempting to use legacy methods will result in your Pi silently falling back to DHCP or dropping off the network entirely.

To change IP address Raspberry Pi on modern OS versions, you must use the nmcli (NetworkManager Command Line Interface) tool. Below, we will cover the exact CLI commands to set a static IP, and then build a hardware I2C OLED monitor that displays your Pi's active IP address on boot—complete with Python error handling for network interface delays.

Difficulty: Intermediate | Time: 45 Minutes | Board Target: Raspberry Pi 5 (8GB) running Pi OS Bookworm/Trixie (64-bit)

Project Spec Sheet & Parts List

This build pairs a network configuration workflow with a physical IP readout. This is especially useful for headless setups or robotics projects where the Pi moves between different VLANs or subnets.

Component Exact Variant / Model Notes & Pricing (Approx.)
Microcontroller Raspberry Pi 5 (8GB RAM) Requires active cooler; uses lgpio instead of legacy RPi.GPIO ($80)
Display Waveshare 1.3" OLED HAT (SH1106) 128x64 I2C interface, 3.3V logic ($14)
Power Supply Official Raspberry Pi 27W USB-C PD Required to prevent brownout warnings on Pi 5 ($12)
Storage 32GB Samsung EVO Plus microSD A1/A2 rated for OS boot reliability ($9)

Hardware Assembly & Pin Mapping

The Waveshare 1.3" OLED HAT plugs directly onto the first 26 pins of the Pi 5 GPIO header. While it physically covers more pins, it only actively uses the I2C bus and power rails. Ensure your Pi is powered off and the active cooler is seated before pressing the HAT onto the headers.

OLED HAT Pin Label Raspberry Pi 5 GPIO (BCM) Physical Pin Number Function
VCC 3V3 1 3.3V Power Rail
GND GND 6 Common Ground
SDA GPIO 2 3 I2C Data Line
SCL GPIO 3 5 I2C Clock Line
Bench Tip: Before attaching the OLED, enable the I2C interface by running sudo raspi-config → Interface Options → I2C → Enable. Verify it with ls /dev/i2c*—you should see /dev/i2c-1.

How to Change IP Address Raspberry Pi via NetworkManager

NetworkManager uses "connection profiles" rather than binding settings directly to hardware interface names (like eth0). This prevents configuration breakage if the kernel assigns a different predictable network interface name (like enp1s0f0).

  1. Identify your active connection profile:
    Run nmcli connection show. Look under the "NAME" column. For Ethernet, it is usually Wired connection 1. For WiFi, it will be your SSID name.
  2. Assign the static IP and subnet mask:
    sudo nmcli connection modify "Wired connection 1" ipv4.addresses 192.168.1.50/24
    (Replace 192.168.1.50 with your desired IP, and /24 with your subnet, typically /24 for home networks).
  3. Set the default gateway (your router's IP):
    sudo nmcli connection modify "Wired connection 1" ipv4.gateway 192.168.1.1
  4. Define DNS servers:
    sudo nmcli connection modify "Wired connection 1" ipv4.dns "8.8.8.8 1.1.1.1"
  5. Switch the IPv4 method from DHCP to Manual:
    sudo nmcli connection modify "Wired connection 1" ipv4.method manual
  6. Restart the connection to apply changes:
    sudo nmcli connection up "Wired connection 1"

Verify the change by typing ip -4 addr show. You should see your new static IP listed under the active Ethernet interface. For deeper architectural context, refer to the official Raspberry Pi NetworkManager documentation.

Python IP Monitor Script (With Error Handling)

Network interfaces on the Pi 5 can take 3 to 8 seconds to negotiate a link and pull an IP address on boot. If a Python script queries the IP immediately, it will crash. The script below targets the Pi 5, uses the lgpio backend required for Bookworm/Trixie, and implements a retry loop with explicit hardware pin definitions.

Prerequisites: sudo apt install python3-pip python3-venv libopenjp2-7 libtiff6 i2c-tools then pip install luma.oled lgpio


import time
import socket
import subprocess
import sys
from luma.core.interface.serial import i2c
from luma.oled.device import sh1106
from PIL import Image, ImageDraw, ImageFont

# --- HARDWARE PIN & BUS DEFINITIONS ---
I2C_PORT = 1        # Maps to Physical pins 3 (SDA) and 5 (SCL)
I2C_ADDRESS = 0x3C  # Waveshare 1.3" OLED default I2C address
OLED_WIDTH = 128
OLED_HEIGHT = 64
MAX_RETRIES = 10    # Wait up to 10 seconds for network link

# --- DISPLAY INITIALIZATION ---
try:
    serial = i2c(port=I2C_PORT, address=I2C_ADDRESS)
    device = sh1106(serial, width=OLED_WIDTH, height=OLED_HEIGHT)
except Exception as e:
    print(f"[FATAL] OLED Init Failed: {e}")
    print("Check I2C enablement and wiring.")
    sys.exit(1)

def get_ip_address(interface='eth0'):
    """Fetches IP using socket, falling back to nmcli if interface name is unpredictable."""
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        # Fallback to nmcli for modern NetworkManager environments
        try:
            cmd = "nmcli -g IP4.ADDRESS connection show --active | head -n 1"
            result = subprocess.check_output(cmd, shell=True).decode('utf-8').strip()
            return result.split('/')[0] if result else 'No IP'
        except subprocess.CalledProcessError:
            return 'Network Down'

def main():
    # Font setup (using default PIL font for broad compatibility)
    font_large = ImageFont.load_default()
    
    retries = 0
    ip_addr = 'Waiting for DHCP...'
    
    while retries < MAX_RETRIES:
        ip_addr = get_ip_address()
        if ip_addr != 'No IP' and ip_addr != 'Network Down' and ip_addr != 'Waiting for DHCP...':
            break
        time.sleep(1)
        retries += 1

    # Render to OLED
    image = Image.new('1', (OLED_WIDTH, OLED_HEIGHT))
    draw = ImageDraw.Draw(image)
    
    draw.text((0, 0), "Pi 5 Status:", font=font_large, fill=255)
    draw.text((0, 20), "IP Address:", font=font_large, fill=255)
    draw.text((0, 35), ip_addr, font=font_large, fill=255)
    
    device.display(image)
    print(f"[SUCCESS] Displaying IP: {ip_addr}")

if __name__ == "__main__":
    main()

For comprehensive API details on the display driver, consult the luma.oled ReadTheDocs documentation.

Debugging: Exact Error Strings & Ranked Causes

When configuring networks or driving I2C displays on the Pi 5, you will inevitably hit roadblocks. Here are the exact error strings you will see, ranked by frequency, and how to fix them.

1. "Error: Connection 'eth0' not found."

  • Cause: You tried to run nmcli connection modify eth0. NetworkManager uses profile names, not kernel interface names.
  • Fix: Run nmcli connection show and use the exact string from the NAME column (e.g., "Wired connection 1").

2. "RTNETLINK answers: File exists"

  • Cause: You are attempting to assign a static IP that is already actively bound to another interface (like a USB-to-Ethernet adapter or a VPN tunnel), or the IP is currently leased to another device on the LAN causing an ARP conflict.
  • Fix: Run ip addr flush dev eth0 to clear stale leases, then bring the connection up. Ensure your router's DHCP pool excludes your chosen static IP.

3. "ModuleNotFoundError: No module named 'lgpio'" or "RuntimeError: This module can only be run on a Raspberry Pi"

  • Cause: The legacy RPi.GPIO library is hardcoded for Pi 4 and older Broadcom chips. The Pi 5 uses the RP1 southbridge chip, which breaks legacy GPIO libraries.
  • Fix: Uninstall the old library (pip uninstall RPi.GPIO) and install the modern replacement: pip install lgpio. The luma.core library will automatically detect and use lgpio if it is present in the environment.

First Three Things to Check When It Fails:

  1. I2C Bus Presence: Run i2cdetect -y 1. If the grid is empty, your OLED HAT is not seated properly or I2C is disabled in raspi-config.
  2. Active Connection State: Run nmcli device status. If Ethernet shows "disconnected", your cable is bad or the switch port is dead.
  3. DHCP Pool Overlap: Log into your router. If your router hands out IPs from 192.168.1.2 to 192.168.1.254, setting a static IP in that range will eventually cause an IP collision.

Extending and Simplifying the Build

How to Simplify: If you don't need the physical OLED readout and just want a headless static IP setup for a server rack, strip away the Python script and hardware entirely. Simply execute the six nmcli commands listed above, and add a cronjob that emails you if the link drops using iptables logging.

How to Extend: Turn this into a network diagnostic tool. Wire a momentary pushbutton to GPIO 17 (Physical Pin 11) and a 10k pull-down resistor to GND. Modify the Python script to listen for a button press using lgpio. When pressed, have the script toggle the NetworkManager profile between ipv4.method manual (Static) and ipv4.method auto (DHCP). This allows you to physically switch your Pi between a lab bench subnet and a home network without needing a keyboard or SSH access.

Frequently Asked Questions

How do I change IP address Raspberry Pi without a monitor?

If your Pi is headless and you don't know its current IP, connect it directly to your PC via Ethernet. Set your PC's Ethernet adapter to a static IP in the 169.254.x.x (link-local) range. Alternatively, log into your router's admin panel and check the DHCP client lease table to find the Pi's hostname (usually raspberrypi) and its currently assigned IP, then SSH in to run the nmcli commands.

Why did my raspberry pi static IP change after reboot?

This happens almost exclusively because you edited the deprecated /etc/dhcpcd.conf file. In modern Pi OS (Bookworm and newer), dhcpcd is disabled by default. NetworkManager ignores that file entirely and requests a DHCP lease on boot. You must use nmcli connection modify to make the static IP persistent across reboots.

How to change IP address Raspberry Pi using nmcli for WiFi?

The process is identical to Ethernet, but the connection name will be your SSID. First, find the profile name with nmcli connection show. Then apply the manual IP settings:
sudo nmcli connection modify "MyWiFiSSID" ipv4.addresses 192.168.1.60/24 ipv4.gateway 192.168.1.1 ipv4.dns "8.8.8.8" ipv4.method manual
Finally, reconnect with sudo nmcli connection up "MyWiFiSSID".

Can I use the desktop GUI to set a static IP instead of the terminal?

Yes. If you are running the desktop environment of Pi OS, click the Network icon in the top right system tray, select "Advanced Options" or "Edit Connections", choose your active interface, and navigate to the IPv4 tab. Change the Method from "Automatic (DHCP)" to "Manual", input your Address, Netmask (usually 255.255.255.0), and Gateway, then save and reconnect.