To change your Raspberry Pi IP address on modern OS versions (Bookworm and the upcoming Trixie), you must use nmcli to modify the NetworkManager connection profile. Editing /etc/dhcpcd.conf will fail because dhcpcd has been completely replaced. The direct command to set a static IP is: 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 ipv4.method manual, followed by sudo nmcli connection up "Wired connection 1".

If you are following a tutorial from 2022 or earlier, it is likely obsolete. The shift to Debian 12 (Bookworm) fundamentally changed how the Pi handles networking. This guide targets the Raspberry Pi 5 (8GB variant) running the 64-bit Bookworm OS, providing a reliable, code-driven approach to managing your IP with physical GPIO feedback.

Hardware Spec Sheet & Interface Mapping

While changing an IP is primarily a software task, headless embedded deployments require physical feedback mechanisms to confirm network state without a monitor. We are adding a status LED to the GPIO header to indicate when the IP configuration script is running, succeeding, or failing.

Parts List & Component Variants
Component Exact Variant / Specification Notes
Microcontroller Raspberry Pi 5 (8GB RAM) Targets Bookworm 64-bit OS
Power Supply Official 27W USB-C PD Power Supply Required for full peripheral power
Networking Cat6 Ethernet Cable (Shielded) Gigabit Ethernet interface (eth0)
Status Indicator 5mm Green LED + 330Ω Resistor Current limiting for 3.3V GPIO
Wiring 22 AWG Solid Core Jumper Wires For breadboard GPIO connections

GPIO Pin Mapping Table

The Python script below uses the gpiozero library, which defaults to Broadcom (BCM) pin numbering. Ensure your physical wiring matches the BCM definitions, not the physical board pin numbers.

Function BCM GPIO Pin Physical Board Pin Wiring Destination
Status LED Signal GPIO 17 Pin 11 330Ω Resistor → LED Anode (+)
Ground Reference GND Pin 9 LED Cathode (-)

Step-by-Step Terminal Configuration

Before automating this with Python, you need to understand the manual terminal sequence. NetworkManager organizes networking by connection profiles, not just hardware interfaces. By default, the Pi creates a profile named Wired connection 1 for the Ethernet port.

💡 Callout Tip: Never use the interface name (eth0) when modifying persistent settings in nmcli. Always use the connection profile name. Run nmcli connection show to verify your exact profile name.
  1. Identify the active connection:
    nmcli connection show --active
    Look for the name under the NAME column (usually Wired connection 1).
  2. Assign the static IP, Gateway, and DNS:
    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
    Note the CIDR notation (/24). NetworkManager requires this; it will reject standard subnet masks like 255.255.255.0.
  3. Apply the changes by restarting the connection:
    sudo nmcli connection up "Wired connection 1"
  4. Verify the new IP assignment:
    ip -4 addr show eth0

Python Automation: IP Manager with GPIO Feedback

In headless IoT deployments, you often need a script that provisions a static IP on first boot or via a trigger button. The following Python script uses subprocess to call nmcli safely, capturing standard error outputs if the command fails, while using the mapped GPIO 17 LED to provide visual state feedback.

Target Board: Raspberry Pi 5 (8GB) | OS: Bookworm 64-bit | Dependencies: sudo apt install python3-gpiozero

import subprocess
import sys
from gpiozero import LED
from time import sleep

# --- Pin Definitions (BCM Numbering) ---
STATUS_LED_PIN = 17
status_led = LED(STATUS_LED_PIN)

# --- Network Configuration Parameters ---
CONNECTION_NAME = "Wired connection 1"
TARGET_IP_CIDR = "192.168.1.50/24"
GATEWAY_IP = "192.168.1.1"
DNS_SERVERS = "1.1.1.1,8.8.8.8"

def set_static_ip():
    """Modifies NetworkManager profile and applies it, with GPIO feedback."""
    print(f"[INFO] Configuring {CONNECTION_NAME} to {TARGET_IP_CIDR}...")
    status_led.blink(on_time=0.5, off_time=0.5)  # Slow blink: Processing
    
    # Construct the nmcli command
    modify_cmd = [
        "nmcli", "connection", "modify", CONNECTION_NAME,
        "ipv4.addresses", TARGET_IP_CIDR,
        "ipv4.gateway", GATEWAY_IP,
        "ipv4.dns", DNS_SERVERS,
        "ipv4.method", "manual"
    ]
    
    try:
        # Execute modification with strict error checking
        subprocess.run(modify_cmd, check=True, capture_output=True, text=True)
        
        # Bring the connection up to apply changes immediately
        subprocess.run(
            ["nmcli", "connection", "up", CONNECTION_NAME], 
            check=True, capture_output=True, text=True
        )
        
        status_led.on()  # Solid ON: Success
        print("[SUCCESS] Static IP applied and connection restarted.")
        
    except subprocess.CalledProcessError as e:
        status_led.blink(on_time=0.1, off_time=0.1)  # Fast blink: Error
        print(f"[ERROR] nmcli failed with exit code {e.returncode}")
        print(f"[ERROR] stderr: {e.stderr.strip()}")
        sys.exit(1)
        
    except FileNotFoundError:
        status_led.off()
        print("[CRITICAL] 'nmcli' binary not found. Is NetworkManager installed?")
        sys.exit(2)

if __name__ == "__main__":
    try:
        set_static_ip()
    except KeyboardInterrupt:
        print("\n[INFO] Script interrupted by user.")
        status_led.off()
        sys.exit(0)

Debugging: "Error: unknown connection 'eth0'"

When migrating from older Raspberry Pi OS versions or standard Debian server setups, the most frequent failure point is attempting to pass the hardware interface name to NetworkManager's modification command.

⚠️ Exact Error String:
Error: unknown connection 'eth0'.
Often accompanied by Python throwing: subprocess.CalledProcessError: Command '...' returned non-zero exit status 10.

Ranked Causes for this Failure

  1. Using Interface Name Instead of Profile Name (90% of cases): nmcli connection modify expects the logical profile name (e.g., Wired connection 1), not the kernel interface (eth0). Use nmcli device to see interfaces, and nmcli connection to see profiles.
  2. NetworkManager is Masked or Stopped (8% of cases): If you previously attempted to "fix" networking by reinstalling dhcpcd, you may have accidentally masked NetworkManager.
  3. Missing CIDR Subnet Mask (2% of cases): Passing 192.168.1.50 without the /24 suffix will cause nmcli to reject the address format, though this usually throws a "prefix missing" error rather than an "unknown connection" error.

The First Three Things to Check When It Fails

If your Python script throws an error or your terminal command fails, execute these three diagnostic commands in order:

  1. Verify the exact profile name:
    nmcli -t -f NAME,TYPE connection show
    This outputs a clean, script-readable list of all profiles. Look for the 802-3-ethernet type.
  2. Check NetworkManager service state:
    systemctl status NetworkManager
    Ensure it says active (running). If it is masked, run sudo systemctl unmask NetworkManager.
  3. Confirm no legacy DHCP conflicts:
    systemctl status dhcpcd
    On Bookworm, this should return "could not be found" or "inactive". If it is running, it will fight NetworkManager for control of eth0. Disable it with sudo systemctl disable --now dhcpcd.

Extending and Simplifying the Build

Depending on your deployment environment, you may want to scale this setup up for a fleet of devices or scale it down for quick bench testing.

How to Simplify (The Bench-Tester Route)

If you don't want to write Python scripts or memorize nmcli syntax, use the built-in Terminal UI. Simply type sudo nmtui in your SSH session. This opens a pseudo-graphical interface where you can arrow-key your way to "Edit a connection", type in your static IP, and tab over to "OK". It is the fastest way to configure a static IP on a headless Pi without relying on external tools. Furthermore, if you are flashing a brand new SD card, the Raspberry Pi Imager allows you to set a static IP in the "Advanced Options" (Ctrl+Shift+X) menu before you even write the OS to the card.

How to Extend (The Fleet IoT Route)

To extend this for a fleet of 50+ headless sensors, hardcoding IPs in a Python script is inefficient. Instead, extend the build by implementing a MAC-to-IP reservation on your router's DHCP server (e.g., pfSense or UniFi). This allows the Pi to remain on ipv4.method auto (DHCP) while guaranteeing it always receives the same IP based on its Ethernet MAC address. If you must manage it from the Pi side, extend the Python script to read the target IP from a local config.json file or fetch it via an MQTT payload on boot, allowing centralized fleet management without touching the SD cards.

Frequently Asked Questions

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

If you are completely headless and SSH is your only access, changing the IP will sever your current SSH session the moment you run nmcli connection up. To do this safely without getting locked out, use the at command to schedule a revert, or run the modification via a background script (like the Python example above) that includes a 60-second timeout to revert to DHCP if a specific "keep-alive" file isn't created. Alternatively, if you have physical access, plug in a USB-to-TTL serial console cable to the Pi's UART pins (GPIO 14/15) so you retain shell access even if the Ethernet IP changes or breaks.

Why did my static IP stop working after updating to Bookworm?

Prior to October 2023, Raspberry Pi OS used dhcpcd as its DHCP client and network configurator. Users would append static IP rules to the bottom of /etc/dhcpcd.conf. With the release of the Bookworm-based OS, Raspberry Pi Ltd adopted NetworkManager as the default networking stack to better handle complex WiFi roaming, enterprise WPA3, and VPN integrations. The OS completely ignores /etc/dhcpcd.conf now. You must migrate your static IP rules to nmcli connection profiles.

Can I set a static IP address using the Raspberry Pi Imager?

Yes, and this is the most reliable method for new deployments. When setting up your OS in the Raspberry Pi Imager application on your PC or Mac, click the gear icon (or press Ctrl+Shift+X / Cmd+Shift+X) to open the Advanced Options. Under "Network Settings", you can check "Set custom IP", input your desired address, router gateway, and DNS servers. The Imager writes a NetworkManager configuration file directly to the boot partition, which the Pi applies on its very first boot sequence.

Does changing the IP address affect my WiFi connection?

No, NetworkManager treats Ethernet (802-3-ethernet) and WiFi (802-11-wireless) as entirely separate connection profiles. Modifying "Wired connection 1" will not alter your WiFi profile (usually named after your SSID). If you want a static IP on WiFi, you must run the nmcli connection modify command against your specific SSID's profile name instead.