Skip to content

WireGuard VPN

The settings page speaks plain HTTP, and its password is optional and off by default. Either way it is fine on your own network and a bad idea on the open internet, so do not port-forward it. The WireGuard client is the alternative: the device joins your VPN itself, and you reach it on its tunnel address from anywhere the VPN reaches.

The ESP32-C2 and the SmallTV Pro. What decides this is how much space each board’s firmware image has left, not what the chip could do.

BoardWireGuardWhy
SmallTV (ESP8266) and SmallTV-ultranoThe chip has ~80 KB of heap in total and the ticker’s TLS already runs close to it. WireGuard on this platform also pulls in a second crypto stack alongside the BearSSL already in the image.
SmallTV (ESP32-C2)yesWith the client in it the image is 1,469,520 bytes, 93% of its 1,572,864-byte update slot.
NM-TV-154 (classic ESP32)noThe classic ESP32’s own framework libraries cost about 100 KB more than the C2’s, so this image is already 1,501,344 bytes, 95% of the same slot. Built with WireGuard the linker reported 1,571,195 bytes: it fits with about 1.6 KB to spare, which is not something to ship.
SmallTV Pro (classic ESP32, 8 MB)yesSame chip family as the NM-TV-154 and the same image size, but its stock partition layout gives each OTA slot 2,228,224 bytes instead of 1,572,864, so the client fits with hundreds of kilobytes left over.

The NM-TV-154 is the only board held back by the layout rather than the silicon. Giving it more room means a different partition table, and a device already in the field cannot install one over the air; it would need a USB reflash. If yours is on the bench and you are happy to reflash it that way, Building from source has the flags.

A device without the client still shows the section as absent rather than broken: the settings page simply has no WireGuard card.

A WireGuard peer you can add this device to: a router that speaks WireGuard, a VPN server you run, or a commercial provider that gives you a full config. You need five things from it.

  • Your own key pair. Generate it on your computer with wg genkey | tee device.key | wg pubkey > device.pub. The private half goes into the device, the public half goes to the server as this device’s peer. Many router and provider interfaces generate the pair for you and hand you both halves, which works just as well.
  • The server’s public key.
  • The server’s hostname or IP, and its UDP port (51820 unless told otherwise).
  • The address the device should have inside the tunnel, as a CIDR, for example 10.6.0.12/32.
  • Which addresses the device should route through the tunnel, for example 10.6.0.0/24.

There is one more field, Keepalive, which you do not need anything from the server for. It is how often the device sends an empty authenticated packet to hold a NAT or firewall mapping open, in seconds. The default of 25 is the usual value and works behind almost every home router; 0 turns it off, which is only worth doing when the device sits on a public address with nothing stateful in between.

The device does not generate keys and cannot show you its own public key, so keep the public half from the step above; it is what the server needs.

  1. Add the device as a peer on your WireGuard server, with the public key you generated and the tunnel address you picked for it.
  2. Open the settings page, go to the WiFi tab, and find the WireGuard VPN card.
  3. Paste the private key, the server’s public key, the endpoint host and port, the tunnel address, and the allowed IPs. Leave Keepalive at 25.
  4. Tick Enable the tunnel and press Save settings.
  5. Watch the line at the top of the card. Within a few seconds it should read “Tunnel up”.

Once it is up, the settings page answers on the tunnel address as well as on the LAN one: http://10.6.0.12 in the example above.

The private key is stored on the device and included in a settings export, so treat an exported config file like a password. The settings page never reads the key back; the field shows “(unchanged)” and leaving it blank keeps the stored one, so you can edit the rest of the form without handling the secret again.

Allowed IPs, and what the device can actually reach

Section titled “Allowed IPs, and what the device can actually reach”

This is the setting people get wrong, and the symptom is a tunnel that handshakes but cannot be pinged.

The device’s network stack has no routing table. What decides which addresses are reachable through the tunnel is the interface’s netmask, and a /32 address matches nothing but itself. So the firmware widens it for you: the tunnel interface takes the widest allowed-IPs range that contains its own address. A device at 10.6.0.12/32 with allowed IPs 10.6.0.0/24 comes up as 10.6.0.12/24 and can talk to everything on 10.6.0.0/24.

So list the subnet the other end is on, and not only the device’s own address. If you set allowed IPs to 0.0.0.0/0, the tunnel becomes the device’s default route and everything, including the ticker’s price fetches, goes through the VPN.

The card’s top line says what the tunnel is doing, and names the likely cause when it is not working.

LineWhat it means
Tunnel offThe tunnel is not enabled.
Tunnel suspended after repeated rebootsThe crash guard below has held it down.
Tunnel not started: a field is empty or malformedSomething in the form did not parse. The usual cause is a tunnel address with no prefix, or a typo in one.
Waiting for the endpoint name to resolveDNS has not answered yet. Check the hostname, and that the device has working DNS.
Handshakes sent to <ip> with no replyThe device found the server and is talking to it, and the server is not answering. Check that the peer on the server carries this device’s public key, and that UDP to that port reaches it.
Tunnel up, last handshake N agoWorking.

The Status tab also carries a one-word VPN line, so you can see the tunnel state without opening the WiFi tab.

A tunnel bug must never lock you out of the device’s own settings page. The firmware counts consecutive crash reboots that happen while the tunnel is enabled, and after three in a row it holds the tunnel down at the next boot and boots normally without it.

Three things clear the count: any clean boot, the first completed handshake, and saving the WireGuard settings again. That last one is deliberate; re-saving is you saying the configuration is fixed and the device should try again.

  • It waits for the clock before the first handshake, up to 90 seconds. WireGuard stamps handshakes with the wall clock and a peer rejects one stamped wrong, so enabling the tunnel also starts NTP whether or not night mode is on.
  • A failed bring-up backs off from 5 seconds to 60 seconds and keeps trying.
  • Three minutes without a handshake tears the tunnel down and rebuilds it, which resolves the endpoint hostname again. That is what makes a peer on dynamic DNS come back after its address changes.
  • Bring-up runs from the main loop in small steps, so the settings page stays responsive while the tunnel is connecting.
  • One tunnel and one peer.
  • IPv4 allowed IPs only.
  • No preshared key.
  • The device cannot show its own public key; keep the one you generated.
  • Up to four allowed-IPs entries, plus the device’s own address. That is a compile-time limit.
  • The endpoint hostname is resolved when the tunnel is built, not per packet.

The tunnel itself is droscy/esp_wireguard 0.4.5, pinned exactly, the same component ESPHome ships. The firmware adds the configuration, the state machine, the crash guard, and the web UI around it.