esp32 beginner 20 min

ESP32: assign a static IP and reserve it in the router

Give an ESP32 a fixed IP with WiFi.config(), reserve it in your router with a DHCP reservation, and stop hunting the network for a device that moved.

Code available for: ESP32 ArduinoArduino C
Published Sep 22, 2026

Every ESP32 project I run for more than a week ends the same way: the web dashboard worked, then the router rebooted, then the dashboard was at a different address and nothing on my desk pointed at the right IP anymore. The fix is two layers, not one. Assign the IP you want inside the sketch with WiFi.config(), and make the router agree to never hand that address to anyone else (a DHCP reservation). This tutorial does both, in about 20 minutes.

The trap I hit: I set a static IP in code, felt clever, went to bed, and the next morning the device was unreachable. My router had handed my chosen address to my laptop while the ESP32 was rebooting, and two devices were now fighting over one IP. A sketch-side static IP with no router-side reservation is a conflict waiting to happen. Do both halves and this stops being a problem forever.

What you need

Needed

  • ESP32 dev board with Wi-Fi (e.g. an ESP32-WROOM-32 devkit): the device getting the fixed address
  • Your router’s admin password: the reservation half lives in the router’s web UI, not in code

Nice to have

  • A phone with a network scanner app (e.g. Fing, or nmap on a laptop): confirms which addresses are already taken before you pick
  • A label maker or masking tape: write the device name and IP on the board itself; future-you will not remember
  • Multimeter: only if the board also misbehaves electrically (random reboots can masquerade as network problems)

Wiring

None. This one is a software tutorial; the only “hardware” step is powering the board over USB. (If your project already has sensors wired up, nothing about those connections changes.)

Install

Nothing to install. WiFi.h ships with the ESP32 Arduino core. If you have not installed the core yet, work through the toolchain tutorial first (Arduino IDE >> File >> Preferences >> Additional Board Manager URLs, then Boards Manager >> install esp32).

The code

The mental model: WiFi.config() must be called after WiFi.begin() starts the driver but the addresses only stick if the router also reserves the address (covered right after the code). Here is a complete sketch that connects with a fixed IP and tells you where it landed.

#include <WiFi.h>

const char* ssid     = "your-wifi";
const char* password = "your-password";

// Pick an address INSIDE your router's DHCP range but OUTSIDE the pool
// it assigns from (see the router section below). Match the subnet your
// router actually uses; 192.168.1.x is common but not universal.
IPAddress local_IP(192, 168, 1, 50);
IPAddress gateway(192, 168, 1, 1);      // your router's address
IPAddress subnet(255, 255, 255, 0);     // almost always this
// Optional; usually you can leave DNS to the router:
IPAddress primaryDNS(192, 168, 1, 1);
IPAddress secondaryDNS(1, 1, 1, 1);     // Cloudflare, as a fallback

void setup() {
  Serial.begin(115200);
  delay(1000);

  WiFi.mode(WIFI_STA);
  WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
  WiFi.begin(ssid, password);

  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("Connected. IP: ");   Serial.println(WiFi.localIP());
  Serial.print("Gateway: ");         Serial.println(WiFi.gatewayIP());
  Serial.print("Subnet: ");          Serial.println(WiFi.subnetMask());
  Serial.print("DNS: ");             Serial.println(WiFi.dnsIP());
}

void loop() {
  // Reconnect logic belongs here in a real device (see the Wi-Fi
  // tutorial for a battle-tested version).
}

One line does the work: WiFi.config(...) before WiFi.begin(). The order matters. Calling config() after the connection is up can leave you with the old DHCP address until the next reconnect, which looks like “the code did nothing.”

The router half (the part that makes it stick)

The sketch pins the address, but the router does not know that. If the ESP32 reboots and another device grabs 192.168.1.50 first, you get an IP conflict. The fix is a DHCP reservation: tell the router “this MAC address always gets this IP.”

Router UIs differ, but the path is usually one of these:

  • TP-Link: Advanced >> Network >> DHCP Server >> Address Reservation
  • Asus: Advanced Settings >> LAN >> DHCP Server >> Manually Assigned IP
  • Netgear: Advanced >> Setup >> LAN Setup >> Address Reservation
  • OpenWrt: Network >> Interfaces >> LAN >> DHCP Server >> Static Leases

You need two values from the ESP32: its MAC address (printed by this sketch if you add Serial.println(WiFi.macAddress()); in setup) and the IP you chose. Enter the MAC, enter the IP, save, reboot the ESP32.

Now the two halves agree. The ESP32 asks for 192.168.1.50 every time, and no other device is ever offered it, because the router itself is holding the reservation.

Static IP vs DHCP reservation vs mDNS

ApproachSet whereSurvives router swapGood for
WiFi.config() static IPsketchno (new subnet breaks it)devices you never move
DHCP reservationrouteryes (IP re-pins via MAC)everything, honestly
mDNS namesketchyes, but needs .local supporthuman-friendly access

If I am being honest: the reservation alone is usually enough, because DHCP hands out the same address to the same MAC every time once a lease exists. The WiFi.config() call is belt-and-suspenders for the case where the router reboots faster than the ESP32 (e.g. a power blink) and the lease table resets. Doing both costs one line and one router form.

What you learned

  • WiFi.config(ip, gateway, subnet, dns) before WiFi.begin() pins the device’s address; the call order is the whole trick.
  • A sketch-side static IP without a router-side reservation eventually causes an IP conflict with whatever grabs the address first.
  • A DHCP reservation in the router (keyed to the MAC address) makes the IP predictable for every device on your network, not just this one.
  • mDNS (device.local) is the friendlier overlay on top of a stable IP; they solve different halves of the same problem.

When something breaks

  • The ESP32 never connects after adding WiFi.config(). One of the four addresses does not match your network. The subnet mask and gateway are the usual offenders (e.g. some ISPs hand out 192.168.0.x, not 192.168.1.x). Check the gateway and mask on a working computer (Windows: ipconfig; macOS/Linux: ip route) and copy what you see.
  • Connected at the old IP, not the new one. WiFi.config() was called after WiFi.begin() took effect, or the sketch uploaded without saving. Re-flash, and power-cycle the board (not just a soft reset) so the driver starts clean.
  • “Connected” but no internet. DNS. If you set primaryDNS to the router, make sure the router actually forwards DNS; if you set it to a public resolver, make sure that resolver is reachable. Setting both DNS entries to the router’s address is the safe default for a home LAN.
  • Everything works, then stops after a router reboot. Your reservation is missing or keyed to the wrong MAC (boards with two MAC addresses, one for 2.4GHz and one for Ethernet-style interfaces, can surprise you; copy the MAC the sketch prints while connected to your actual network).
  • Two devices fight over one IP. You skipped the reservation. Add it, or move your static IP outside the router’s DHCP pool entirely (e.g. pool 100-199, statics at 10-49), which is the cleanest long-term layout.

What to build next

  • The mDNS tutorial adds device.local names on top of your now stable address, so you can stop typing IPs entirely.
  • The sensor dashboard web server tutorial is the project this unlocks: a dashboard at a fixed, known address that other tools can scrape forever.
  • The MQTT publish/subscribe tutorial prefers stable addressing too; brokers and clients re-find each other much more reliably when the addresses do not wander.
  • The NTP time sync tutorial assumes the device can always reach the gateway and DNS you just configured, which is exactly what a pinned address plus a reservation guarantees.