To change a Raspberry Pi IP address dynamically on modern Raspberry Pi OS (Bookworm and newer), you must use NetworkManager's command-line tool (nmcli) via a Python subprocess call. The legacy dhcpcd.conf method is deprecated and will fail on current images. By wrapping nmcli in a Python script tied to physical GPIO buttons, you can build a headless network configurator that toggles between DHCP and a static IP without needing a monitor or SSH access.

Project Spec Sheet & Hardware Requirements

Difficulty Rating: Intermediate (Requires basic Linux CLI and Python knowledge)
Target Board Variant: Raspberry Pi 5 (8GB) running Raspberry Pi OS Bookworm (64-bit)
Estimated Build Time: 45 minutes
Primary Libraries: gpiozero, subprocess (Standard Library)

This build creates a physical toggle switch for your Pi's network interface. When the button is pressed, the script updates the NetworkManager profile and restarts the interface. Two LEDs provide instant visual feedback on the current IP assignment mode.

Parts List

  • Microcontroller: Raspberry Pi 5 (4GB or 8GB variant) with active cooler
  • Pushbutton: 6x6mm tactile switch (normally open)
  • LEDs: 1x 5mm Green LED (DHCP indicator), 1x 5mm Red LED (Static IP indicator)
  • Resistors: 2x 330Ω through-hole resistors (for LED current limiting)
  • Wiring: 4x Female-to-Male jumper wires, half-size breadboard

GPIO Pin Mapping for Network Status Indicator

We use the gpiozero library to handle hardware debouncing and pin states. The button is configured with an internal pull-up resistor, meaning it reads True (High) when unpressed and False (Low) when pressed.

Component BCM GPIO Pin Physical Pin Function / Notes
Tactile Button GPIO 17 11 Toggle trigger (connect other leg to GND)
Green LED (Anode) GPIO 27 13 DHCP Mode Indicator (via 330Ω resistor)
Red LED (Anode) GPIO 22 15 Static IP Mode Indicator (via 330Ω resistor)
Common Ground GND 9, 14, etc. Shared ground for button and LED cathodes
⚠️ Hardware Warning: Never connect 5V directly to any Raspberry Pi 5 GPIO pin. The Pi 5 GPIO bank operates strictly at 3.3V. Supplying 5V to GPIO 17, 27, or 22 will instantly destroy the SoC's GPIO controller.

Python Script to Change Raspberry Pi IP Address

The following script targets the Raspberry Pi 5 running Bookworm. It relies on NetworkManager, which replaced dhcpcd as the default network stack. Ensure you have gpiozero installed (sudo apt install python3-gpiozero).

Save this as ip_toggle.py and run it with sudo python3 ip_toggle.py. NetworkManager modifications require root privileges unless your user is explicitly added to the netdev group with polkit rules configured.

import subprocess
import time
from gpiozero import Button, LED
from signal import pause

# --- Pin Definitions ---
PIN_BTN_TOGGLE = 17
PIN_LED_DHCP = 27
PIN_LED_STATIC = 22

# --- Hardware Setup ---
# pull_up=True means pin is HIGH by default, goes LOW when pressed to GND
btn_toggle = Button(PIN_BTN_TOGGLE, pull_up=True, bounce_time=0.2)
led_dhcp = LED(PIN_LED_DHCP)
led_static = LED(PIN_LED_STATIC)

# --- Network Configuration Variables ---
# Use 'nmcli con show' in terminal to verify your exact connection name
CON_NAME = "Wired connection 1" 
STATIC_IP_CIDR = "192.168.1.50/24"
GATEWAY_IP = "192.168.1.1"
DNS_SERVERS = "1.1.1.1,8.8.8.8"

def run_nmcli_command(cmd_list):
    """Executes nmcli command and handles specific subprocess errors."""
    try:
        subprocess.run(cmd_list, check=True, capture_output=True, text=True)
        return True
    except subprocess.CalledProcessError as e:
        print(f"[ERROR] nmcli failed with exit code {e.returncode}.")
        print(f"[ERROR] Output: {e.stderr.strip()}")
        return False

def set_static_ip():
    print("Applying Static IP configuration...")
    cmds = [
        ["nmcli", "con", "mod", CON_NAME, "ipv4.addresses", STATIC_IP_CIDR],
        ["nmcli", "con", "mod", CON_NAME, "ipv4.gateway", GATEWAY_IP],
        ["nmcli", "con", "mod", CON_NAME, "ipv4.dns", DNS_SERVERS],
        ["nmcli", "con", "mod", CON_NAME, "ipv4.method", "manual"],
        ["nmcli", "con", "up", CON_NAME]
    ]
    
    for cmd in cmds:
        if not run_nmcli_command(cmd):
            led_static.blink(0.2, 0.2) # Fast blink indicates failure
            return
            
    led_dhcp.off()
    led_static.on()
    print(f"Success: IP changed to {STATIC_IP_CIDR}")

def set_dhcp():
    print("Reverting to DHCP configuration...")
    cmds = [
        ["nmcli", "con", "mod", CON_NAME, "ipv4.method", "auto"],
        ["nmcli", "con", "mod", CON_NAME, "ipv4.addresses", ""],
        ["nmcli", "con", "mod", CON_NAME, "ipv4.gateway", ""],
        ["nmcli", "con", "up", CON_NAME]
    ]
    
    for cmd in cmds:
        if not run_nmcli_command(cmd):
            led_dhcp.blink(0.2, 0.2)
            return
            
    led_static.off()
    led_dhcp.on()
    print("Success: Reverted to DHCP")

def toggle_network_mode():
    if led_static.is_lit:
        set_dhcp()
    else:
        set_static_ip()

# --- Initial State Check ---
# Check current NetworkManager state on boot
try:
    result = subprocess.run(
        ["nmcli", "-g", "ipv4.method", "con", "show", CON_NAME],
        capture_output=True, text=True, check=True
    )
    if "manual" in result.stdout:
        led_static.on()
    else:
        led_dhcp.on()
except subprocess.CalledProcessError:
    # Default to DHCP LED if connection name is wrong
    led_dhcp.on()

# --- Event Binding ---
btn_toggle.when_pressed = toggle_network_mode

print("Network Toggle Script Active. Press Ctrl+C to exit.")
pause()

Debugging: Ranked Causes for Network Failures

When automating network changes on embedded Linux, things will break. If your script fails to change the Raspberry Pi IP address, here are the exact error strings you will encounter and the ranked causes to check.

The First Three Things to Check

  1. Verify the Connection Name: Run nmcli con show in the terminal. If your Ethernet cable was plugged in after boot, the connection might be named eth0 or enp1s0f0 instead of Wired connection 1. Update the CON_NAME variable in the script to match exactly.
  2. Check Privilege Escalation: Ensure you are running the script with sudo. NetworkManager restricts profile modifications to root or authorized users via Polkit.
  3. Verify Physical Link State: NetworkManager will often refuse to bring up a connection (nmcli con up) if the physical carrier is down. Ensure the Ethernet cable is plugged in and the switch port LED is lit before triggering the button.

Exact Error Strings & Fixes

Error String: subprocess.CalledProcessError: Command '['nmcli', 'con', 'mod', 'Wired connection 1'...]' returned non-zero exit status 10.
Cause: Exit status 10 in nmcli usually means "Connection not found" or "Permission denied".
Fix: Verify the connection string spelling (case-sensitive) and ensure the script is executed with sudo.
Error String: Error: unknown connection 'Wired connection 1'.
Cause: The Pi has not generated a default wired profile, common on headless first boots where no cable was present.
Fix: Manually create the profile first: sudo nmcli con add type ethernet con-name "Wired connection 1" ifname eth0.
Error String: socket.error: [Errno 101] Network is unreachable (Seen if you add a network ping test to the script).
Cause: The static IP you assigned is on a different subnet than your physical router, or the gateway IP is incorrect.
Fix: Verify your router's subnet. If your router is 10.0.0.1, setting the Pi to 192.168.1.50 will result in an unreachable network.

Extending and Simplifying the Build

Depending on your deployment environment, you may want to scale this project up for a fleet of devices or strip it down for a single kiosk.

  • How to Extend (Fleet Management): Add an I2C OLED display (SSD1306) to the Pi's GPIO 2 (SDA) and GPIO 3 (SCL). Modify the Python script to query ip -4 addr show eth0 and print the actual assigned IP address to the screen. This turns the Pi into a self-reporting kiosk that displays its network identity on boot, saving you from hunting through router DHCP tables.
  • How to Simplify (Headless Kiosk): If you don't need dynamic toggling and just want a permanent static IP without writing Python, drop the hardware entirely. Simply run a one-line terminal command: sudo nmcli con mod "Wired connection 1" ipv4.addresses 192.168.1.50/24 ipv4.gateway 192.168.1.1 ipv4.method manual followed by sudo nmcli con up "Wired connection 1". This persists across reboots natively.

Frequently Asked Questions

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

If you cannot attach a monitor or use SSH because the Pi is on the wrong subnet, connect a USB-to-TTL serial console cable to the Pi's UART pins (GPIO 14 TXD and GPIO 15 RXD). Use a terminal program like PuTTY or screen at 115200 baud to access the command line directly, bypassing the network stack entirely. From there, you can execute the nmcli commands to correct the IP address.

Why does my static IP change back to DHCP on reboot?

This happens if you are using the ip addr add command instead of NetworkManager. The ip command only modifies the kernel's live network state in RAM; it does not write to disk. To make the change persistent across reboots on Raspberry Pi OS Bookworm or newer, you must modify the NetworkManager profile using nmcli con mod, which writes the configuration to /etc/NetworkManager/system-connections/.

Can I change Raspberry Pi IP address using the older dhcpcd.conf method?

No. As of Raspberry Pi OS Bookworm (released late 2023) and continuing into current 2026 releases, the dhcpcd daemon has been entirely removed and replaced by NetworkManager. If you attempt to edit /etc/dhcpcd.conf, the file will either not exist or be completely ignored by the OS. You must use nmcli or the nmtui (Network Manager Text User Interface) tool to configure static IPs on modern Pi images. For deeper insights into Pi networking transitions, refer to the gpiozero and Pi OS documentation.