A relay that clicks briefly when an ESP32 reboots is one of the most common—and most misunderstood—ESPHome hardware problems. The YAML may say the relay should start OFF, yet the relay still energises for a fraction of a second during reset, firmware upload or power-up.
The reason is simple: ESPHome cannot control a GPIO before the ESP32 itself has booted far enough to configure that pin. During the first milliseconds of reset, a GPIO may be high-impedance, weakly pulled up or down, used as a boot-strapping input, or momentarily driven by the ROM or peripheral hardware. A sensitive relay module can react before your switch component ever runs.
This guide explains how to identify an active-low relay, choose safer GPIOs, use restore_mode correctly, add hardware pull resistors, avoid strapping pins, prevent motor-direction relay conflicts and understand why an on_boot automation alone cannot fix every boot glitch.
The examples use ESPHome on a classic ESP32 DevKit, but the electrical principles apply to ESP32-S2, S3, C3, C6 and other variants. Their boot-strapping pins are not identical, so always check the documentation for the exact chip and board you are using.
What a Relay Boot Glitch Looks Like
Typical symptoms include:
- The relay clicks once when power is applied.
- The relay switches briefly when you press RESET.
- The load flashes on during an OTA reboot even though Home Assistant shows the switch as OFF afterwards.
- A relay board behaves correctly after boot but activates during firmware upload.
- One relay channel glitches while another channel on the same module does not.
- An ESP32 occasionally fails to boot when a relay module is attached to a particular GPIO.
These symptoms can have different causes. The important first step is to separate firmware state from electrical pin state during reset.
The Three Different States You Need to Think About
| Stage | Who controls the pin? | Can ESPHome restore_mode help? |
|---|---|---|
| Power-up / reset sampling | ESP32 hardware / ROM / pull resistors | No, not yet |
| ESPHome hardware setup | ESPHome configures GPIO and switch | Yes |
| Normal runtime | ESPHome / Home Assistant / automations | Yes |
A relay glitch that occurs before ESPHome hardware setup is an electrical design problem. Software can shorten the unwanted pulse, but only hardware can guarantee a defined input state before firmware takes control.
Active-High vs Active-Low Relay Modules
A relay board may energise when its input is HIGH or when its input is LOW.
| Relay type | GPIO state that energises relay | ESPHome pin setting |
|---|---|---|
| Active-high | HIGH | inverted: false |
| Active-low | LOW | inverted: true |
Many inexpensive relay modules are active-low because their input transistor or optocoupler is designed to sink current when the control signal goes low.
For an active-low module, this ESPHome configuration makes the logical Home Assistant switch behave normally:
switch:
- platform: gpio
id: relay_1
name: "Relay 1"
pin:
number: GPIO23
inverted: true
restore_mode: ALWAYS_OFF
With inverted: true, ESPHome understands that the physical GPIO must be HIGH for logical OFF and LOW for logical ON.
This fixes the runtime logic. It does not guarantee the physical pin is HIGH during the first moments of reset.
Why restore_mode Does Not Eliminate Every Glitch
Current ESPHome GPIO switches initialise at hardware setup priority and apply their initial state early in the boot sequence. The switch component supports restore_mode, including ALWAYS_OFF, ALWAYS_ON and modes that restore a previous state.
For a relay that should never automatically re-energise after a reboot, make the choice explicit:
switch:
- platform: gpio
id: boiler_relay
name: "Boiler Relay"
pin:
number: GPIO23
inverted: true
restore_mode: ALWAYS_OFF
ALWAYS_OFF means ESPHome will initialise the logical switch as OFF once the GPIO switch component is set up.
But ESPHome’s own GPIO-switch documentation warns that relay pins may have pull-ups active at reset before ESPHome code runs. That is why software interlocks and restore modes are not a substitute for hardware fail-safe design.
The Real Fix: Give the Relay Input a Defined Hardware State
If the relay must remain OFF from the instant power is applied, the relay-control input needs a resistor that biases it toward its safe state while the ESP32 GPIO is high-impedance.
Active-High Relay
For an active-high relay, OFF requires a LOW input. A pull-down resistor from the control input to ground keeps the relay inactive until the ESP32 deliberately drives the pin HIGH.
A value such as 10 kΩ is a common starting point for a transistor or logic input, but the correct value depends on the relay module’s actual input circuit.
Active-Low Relay
For an active-low relay, OFF requires a HIGH input. A pull-up resistor from the relay-control input to 3.3 V can keep it inactive while the ESP32 pin is floating.
Again, 10 kΩ is a common practical value for a logic-level input, but you must check that the pull-up voltage and current are compatible with the relay module.
Do not automatically add a pull-up to every active-low 5 V relay module. Some modules already contain pull-ups, transistor networks or optocoupler LEDs referenced to 5 V. Measure or inspect the schematic first so you do not accidentally expose an ESP32 GPIO to 5 V.
Why the Resistor Belongs at the Relay Driver Input
The goal is to control the state of the driver transistor or optocoupler input while the ESP32 pin is floating. The resistor should therefore establish the safe state at the node that actually decides whether the relay energises.
If you designed your own relay driver, the arrangement is straightforward. For example, an NPN transistor or N-channel MOSFET driving a relay coil can have a pull-down on its base/gate so that the transistor remains OFF until the ESP32 actively drives it.
With a ready-made relay module, the input circuit may already contain bias resistors. In that case, adding another resistor can change thresholds or increase GPIO current. Check the module before modifying it.
Boot-Strapping Pins Can Make the Problem Worse
Some ESP32 GPIOs are sampled during reset to choose boot configuration. On the original ESP32, Espressif identifies GPIO0, GPIO2, GPIO5, GPIO12 and GPIO15 as strapping pins. Their levels during reset can affect boot mode, flash voltage or other startup behaviour.
That creates two separate risks when a relay module is connected to one of these pins:
- The ESP32’s internal pull state or ROM behaviour may briefly energise the relay.
- The relay module’s own pull resistor may alter the strapping level and prevent the ESP32 from booting normally.
GPIO12 on the original ESP32 deserves particular care because its reset level can affect the flash supply configuration. Espressif documents that driving it HIGH during reset can select 1.8 V VDD_SDIO, which can prevent a board using 3.3 V flash from booting correctly.
GPIO0 is also the classic boot-mode pin. Holding it LOW during reset selects the serial download bootloader rather than normal SPI flash boot.
There Is No Universal ‘Safe GPIO’ List for Every ESP32
A common mistake is copying a GPIO recommendation written for an original ESP32 DevKit and applying it to an ESP32-S3, C3, C6 or another module. The strapping pins, flash pins and native USB functions differ between families.
For the classic ESP32 DevKit, GPIO16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32 and 33 are commonly convenient general-purpose outputs when they are not already used by your project.
That is not a universal rule. Check the exact board pinout and Espressif boot-configuration documentation before choosing the relay output.
A Practical Safe Relay Configuration
For a typical active-low relay module connected to GPIO23 on a classic ESP32, a conservative ESPHome configuration is:
switch:
- platform: gpio
id: relay_1
name: "Relay 1"
pin:
number: GPIO23
inverted: true
restore_mode: ALWAYS_OFF
Then make sure the hardware also keeps the relay input in its inactive HIGH state while GPIO23 is not yet configured.
If the module already provides a suitable pull-up, no additional resistor may be needed. If it leaves the input floating, add an appropriate external pull-up.
Can on_boot Fix It?
An on_boot automation can force a relay to a desired logical state early in the ESPHome startup process:
esphome:
name: relay-controller
on_boot:
- priority: 600
then:
- switch.turn_off: relay_1
This can be useful when other automations or restored states need to be overridden.
However, ESPHome documents hardware initialisation at a higher setup priority than ordinary boot automations. More importantly, on_boot still runs only after the processor has begun executing ESPHome firmware. It cannot control what the GPIO did during reset sampling or ROM startup.
Therefore on_boot is a policy layer, not a guaranteed cure for a sub-millisecond or millisecond hardware glitch.
Why OTA Updates Can Trigger the Relay
An OTA update ends with a reboot. During that reboot the GPIO passes through the same reset sequence as a manual RESET or power cycle.
If the relay clicks during every OTA update, that is strong evidence that the unwanted state occurs before normal ESPHome runtime control.
Fix the physical startup state rather than trying to detect OTA in Home Assistant. If OTA itself fails and the device ends in a boot loop, see our ESPHome OTA Update Failed: Safe Mode, Recovery and USB Reflashing guide.
Optocoupled Relay Boards Are Not Automatically Glitch-Free
An optocoupler electrically isolates—or partially isolates—the control path only if the board is wired to use that isolation correctly. It does not inherently define the input state during reset.
Many low-cost relay boards include an indicator LED, optocoupler LED and resistor network that makes the input active-low. If the ESP32 pin floats, the module may still see enough current to energise.
Some boards also use a VCC/JD-VCC jumper. The exact behaviour depends on the schematic, so do not assume the jumper creates full galvanic isolation or makes 5 V logic safe for an ESP32.
Relay Modules That Trigger at 3.3 V
Another source of erratic behaviour is a relay module whose input is nominally intended for 5 V logic but happens to trigger from a 3.3 V ESP32. It may work on one channel or one board and become marginal when supply voltage, temperature or component tolerance changes.
A relay input should have a clear logic margin. If the module needs a higher input voltage or current than the ESP32 can safely provide, use a proper transistor or logic-level interface rather than relying on a borderline direct connection.
Power Supply Brownouts Can Look Like a Relay Glitch
Sometimes the relay is not merely reacting to reset—the relay is causing the reset.
When a relay coil energises, its supply current rises. A weak USB supply, thin jumper wires or poor grounding can cause the ESP32 supply to dip, leading to a brownout and reboot. The reboot releases the GPIO, the relay drops out, the voltage recovers, and the sequence can repeat.
Typical symptoms include repeated clicking, Wi-Fi disconnects and serial logs showing resets or brownout messages.
- Use a power supply with adequate current margin.
- Do not route relay-coil current through weak breadboard power paths.
- Use proper decoupling close to the ESP32 and relay module.
- Ensure bare relay coils have flyback suppression.
- Separate noisy load current paths from logic-ground wiring where practical.
Mains Relay Safety
The ESP32 side may be only 3.3 V, but a relay can switch hazardous mains voltage. Use an appropriately rated relay, enclosure, creepage/clearance, fuse/protection scheme and wiring method for the load.
Do not test mains relay behaviour on an exposed breadboard. If you are not qualified to work on mains wiring, use an enclosed certified relay product or have the mains side installed by someone who is.
Two Relays for a Motor, Blind or Reversing Load
A roller shutter, blind motor or reversing contactor may use two relays: one for each direction. In that case a brief boot glitch can be more serious than a lamp flashing because both directions must never energise together.
ESPHome supports software interlocking:
switch:
- platform: gpio
id: relay_up
pin:
number: GPIO22
inverted: true
restore_mode: ALWAYS_OFF
interlock: [relay_down]
interlock_wait_time: 500ms
- platform: gpio
id: relay_down
pin:
number: GPIO23
inverted: true
restore_mode: ALWAYS_OFF
interlock: [relay_up]
interlock_wait_time: 500ms
This is useful during normal firmware operation, but ESPHome explicitly warns that software interlocking cannot protect the reset period before the firmware runs.
For a motor where simultaneous commands could cause damage, use a hardware interlock as well—for example relay contacts or properly designed drive circuitry that physically prevents both directions being powered at once.
Diagnosing Which Layer Is Causing the Click
A simple sequence can narrow the problem quickly.
Test with the Relay Input Disconnected
Power-cycle and reset the ESP32 with the relay input wire removed. If the relay no longer clicks, the control signal is responsible rather than relay-supply instability.
Measure the GPIO During Reset
A multimeter may miss a very short pulse, but it can still reveal whether the pin sits HIGH, LOW or floating during startup. An oscilloscope or logic analyser is much better for seeing short transitions.
Check Whether the Relay Is Active-Low
With the module safely powered, determine which input level activates the relay. Do not guess based on the label ‘LOW trigger’ in a marketplace listing; board revisions vary.
Try a Non-Strapping GPIO
Move the relay signal to a plain general-purpose output appropriate for your exact ESP32 variant. If the glitch disappears, the original pin’s startup behaviour was likely involved.
Add the Correct Hardware Bias
Add or verify a pull-up for an active-low input or a pull-down for an active-high input. Confirm that the chosen resistor does not conflict with the relay module’s own circuitry.
Common Mistakes
- Using inverted: true and assuming the hardware is now safe. Inversion changes ESPHome logic, not the pre-boot electrical state.
- Using restore_mode and expecting it to act before reset. It only applies once ESPHome configures the switch.
- Using on_boot as the only safety mechanism. It runs after reset has already happened.
- Putting the relay on GPIO0 or another strapping pin without checking boot behaviour.
- Adding a pull-up to 5 V on a signal connected directly to an ESP32 GPIO.
- Driving a relay coil directly from an ESP32 GPIO.
- Using software interlock as the sole protection for a reversing motor.
- Ignoring supply brownouts and assuming every click is a GPIO problem.
Active-Low Relay Example with a Local Button
Once the startup hardware is safe, normal control can remain entirely local in ESPHome while still appearing in Home Assistant.
binary_sensor:
- platform: gpio
id: wall_button
pin:
number: GPIO27
mode:
input: true
pullup: true
inverted: true
filters:
- delayed_on: 30ms
on_press:
- switch.toggle: relay_1
switch:
- platform: gpio
id: relay_1
name: "Room Relay"
pin:
number: GPIO23
inverted: true
restore_mode: ALWAYS_OFF
The local button can toggle the relay even if Home Assistant is temporarily unavailable. That runtime resilience is separate from boot-time safety; the relay input still needs the correct hardware bias.
Should the Relay Restore Its Previous State?
For lighting, restoring the previous state after a brief power failure may be convenient. For heaters, pumps, motors or other potentially hazardous equipment, automatically restoring ON can be undesirable.
ESPHome supports restore modes such as RESTORE_DEFAULT_OFF, but the right behaviour depends on the load. Decide intentionally what should happen after a reboot or power failure rather than simply copying a configuration.
For safety-sensitive loads, ALWAYS_OFF is usually the conservative starting point.
A Better Hardware Pattern for Custom Boards
If you are designing your own PCB, the cleanest relay output stage is one whose default electrical state is safe even when the microcontroller is absent.
- Use a transistor or MOSFET driver rather than placing coil current on the GPIO.
- Add a base/gate resistor as required by the driver topology.
- Add a pull resistor that keeps the driver OFF when the ESP32 pin is high-impedance.
- Use a flyback diode or other appropriate suppression for a DC relay coil.
- Choose a non-strapping GPIO where possible.
- Ensure the relay power rail cannot collapse the ESP32 supply.
- Add hardware interlocking where simultaneous relay activation would be dangerous.
With that design, ESPHome’s restore_mode becomes a policy choice rather than the only thing standing between reset and an unwanted output.
Practical Checklist
- Identify whether the relay input is active-high or active-low.
- Use
inverted: truefor an active-low GPIO switch. - Set
restore_mode: ALWAYS_OFFwhen the relay should default to OFF. - Choose a suitable non-strapping GPIO for the exact ESP32 variant.
- Bias the relay driver to its OFF state with hardware while the GPIO is floating.
- Verify the module does not pull an ESP32 input to 5 V.
- Check the power supply if relay activation causes ESP32 resets.
- Use hardware interlocks for reversing motors or mutually exclusive outputs.
- Test power-up, RESET, USB flashing and OTA reboot—not just normal Home Assistant switching.
Final Thoughts
A relay that clicks during ESP32 boot is not necessarily an ESPHome bug. In many cases ESPHome is doing exactly what it was configured to do—the relay reacts before ESPHome gets a chance to do it.
The robust solution combines both layers. Use sensible ESPHome logic with the correct inversion and restore mode, but design the relay input so its safe state exists electrically during reset, bootloader mode and firmware startup.
For current switch syntax and ESPHome’s own warning about reset-time GPIO states, see the ESPHome GPIO Switch documentation and Switch component documentation. For original ESP32 boot-strapping behaviour, see Espressif’s ESP32 Boot Mode Selection documentation.