NerdNOS – API Endpoint Unreachable After Flash
Informational — Monitor and address as needed
Symptoms
- `curl http://<miner-ip>/api/system/info` returns `Connection refused`, `Connection timed out`, or `Empty reply from server`
- Browser dev-tools Network tab shows `(failed) net::ERR_CONNECTION_REFUSED` against the API origin
- Browser console shows `Access to fetch... has been blocked by CORS policy`
- Home Assistant / Grafana / Prometheus / Python scraper logs `requests.exceptions.ConnectionError` or `aiohttp.ClientConnectorError`
- `ping <miner-ip>` succeeds in <10 ms but `curl` to port 80 hangs or refuses
- `nslookup nerdnos.local` returns nothing on Windows but works on macOS / Linux
- OLED / TFT shows live hashrate, IP, and pool worker — the device itself is healthy
- API worked fine before a NerdNOS firmware bump; broke immediately after
- Browser-based dashboard at `http://<miner-ip>/` still loads but JSON endpoints return `404`
- Multiple monitoring clients hit the API simultaneously — first few succeed, later requests time out
- Scraper is on a different VLAN / subnet than the miner; pings might work but TCP/80 is blocked
- Endpoint paths from older AxeOS (`/system/info`, `/api/v1/...`) return inconsistent results across NerdNOS branches
Step-by-Step Fix
Replace every `<hostname>.local` reference with the raw IP. Pull the IP from the OLED, the router DHCP table, or `arp -a` on the scraper machine. Update Home Assistant configs, Grafana data sources, custom Python scrapers, and bookmarks. mDNS fails silently across DHCP renewals, router reboots, and Windows machines without Bonjour. Raw IPs survive every one of those failure modes and remove an entire category of 'API mysteriously broke' incidents from your future.
Run `curl -v http://<miner-ip>/api/system/info --max-time 10` from the failing client. Read the verbose output line by line. The first three lines reveal whether DNS resolves, whether TCP connects, and whether HTTP responds. That single command isolates the failure 80% of the time before you change anything else. Do not skip this for a guess-and-check approach — the answer is almost always in those verbose lines.
Confirm the miner is online and hashing before troubleshooting the API. OLED / TFT must show non-zero hashrate, the assigned IP, and your pool worker. Cross-check at the pool dashboard for a recent share within the last 10 minutes. If the device is not hashing the API failure is a downstream symptom — back out to the appropriate failure page (zero hashrate, brick after OTA, dashboard unreachable) instead of fighting the API directly.
Power-cycle the device for 30 full seconds at the barrel jack or XT30 — not a soft reset, not the web-UI reboot button. Wait the full 30 seconds for capacitors to drain and ESP32 state to clear. Plug power back in, wait for the OLED to display hashing, then re-test the API. This single step clears the documented `esp_http_server` socket-exhaustion pattern (issue #1279 on bitaxeorg/ESP-Miner, inherited by NerdNOS forks).
Reduce polling frequency across all monitoring clients. The ESP32-S3 HTTP server has a small connection pool — default around 7 concurrent sockets. Home Assistant + Grafana + a Python script + open browser tabs all polling `/api/system/info` every 5 seconds will exhaust the pool faster than it can reclaim sockets. Drop everyone to 30-60 second polls and consolidate to a single primary scraper that fans data out to your other tools.
Assign a DHCP reservation in your router for the NerdNOS device's MAC address, binding it to a fixed IP. Bookmark and hardcode that IP in every scraper config. This eliminates the entire 'API URL mysteriously broke last week' failure class — your hostname will never go stale because nothing depends on hostnames. Two minutes of router admin saves hours of future debugging.
Verify segment / VLAN / firewall path from the scraper machine: `ping <miner-ip>` must reply in under 10 ms, then `curl -v http://<miner-ip>/api/system/info --max-time 10`. Ping OK + curl hangs = HTTP server wedged, reboot. Ping fails = different subnet / VLAN / firewall block. Add an ALLOW rule from the scraper subnet to the miner subnet on TCP/80 in your router or firewall (UniFi Firewall, OPNsense Rules, Mikrotik `/ip firewall filter`).
Disable AP client isolation if the scraper and miner share an SSID. 'Wireless isolation' / 'AP isolation' is on by default on guest WiFi and many corporate hotspots — every device can reach the internet, no device can reach any other device. Toggle off in the router admin or move both devices to a non-isolated SSID. On hotel / corporate WiFi, hotspot off your phone instead — fastest workaround.
Sanity-check `curl` itself. Some Windows builds of `curl` have TLS-only defaults that misbehave against plain HTTP. Try `wget` or a Python one-liner: `python3 -c "import urllib.request; print(urllib.request.urlopen('http://<miner-ip>/api/system/info').read())"`. If Python returns JSON and `curl` doesn't, the issue is in `curl`'s config — proxy environment variables, `~/.curlrc`, cert bundle path. Adjust accordingly or switch tools.
Test the API from a SECOND device on the same LAN — phone, second laptop, tablet, anything. If every device fails, the miner is the problem. If only one device fails, the problem is local to that device: Windows Defender Firewall, macOS Little Snitch, corporate-managed laptop ACLs, browser extensions, hosts-file overrides, hardcoded proxies. Local firewalls routinely silently block 'unknown LAN destinations' on managed laptops without showing a notification.
CORS workaround for browser-based dashboards: run a tiny server-side proxy. Node example: `app.get('/api/proxy', (req, res) => fetch('http://<miner-ip>/api/system/info').then(r => r.json()).then(j => res.json(j)))`. Python: Flask + requests. Make the dashboard fetch `/api/proxy` (same origin) instead of the miner directly. Solves CORS forever, centralizes scraping, lets you cache responses for free. Never disable browser CORS in production — proxy server-side.
Hardcode the IP in `/etc/hosts` (Linux/macOS) or `C:WindowsSystem32driversetchosts` (Windows) on machines you control. Map `nerdnos.local` to the actual IP. This bypasses mDNS and DNS entirely — even when Bonjour, your router, and DNS server all break, the hostname still resolves on that machine. Bulletproof for a single home dev machine; not appropriate to deploy widely, but priceless as a debugging shortcut.
Capture a USB-C serial log. Plug in a USB-C *data* cable (charge-only cables won't enumerate the ESP32-S3). Open a serial terminal at 115200 baud — PuTTY on Windows, `screen /dev/ttyACM0 115200` on Unix, picocom, or Arduino IDE Serial Monitor. Reboot the device and save the first 60 seconds. Search for `httpd_start`, `ESP_ERR_NO_MEM`, `mdns_init`, `cJSON`, `Task watchdog got triggered`. The line `httpd_start: error starting server` is the smoking gun — the API task didn't come up.
Reflash NerdNOS via the official web-flasher in Chrome or Edge — Web Serial API is Chromium-only. Open the [shufps/nerdqaxe-web-flasher](https://github.com/shufps/nerdqaxe-web-flasher) GitHub Pages link from the repo README. Plug USB-C, select the correct branch for your hardware: `qaxe` for QAxe, `nerdqaxe` for NerdQAxe, `nerdqaxe-plus` for NerdQAxe+ / NerdQAxe++, dedicated branch for Hex variants. Erase Flash before reflashing if switching branches; old NVS partitions can corrupt new binaries' settings.
Verify the API schema against your scraper after reflashing. Run every endpoint your scraper depends on through `curl` and inspect the response: `/api/system/info`, `/api/system/asic`, `/api/swarm/info`. NerdNOS branches occasionally rename or relocate fields between minor versions (camelCase vs snake_case shifts, nested vs flat layouts). If your scraper expects `hashrate_5s` and the new branch returns `hashRate_5s`, nothing in HTTP is wrong but every parse fails. Diff your scraper against the actual response.
Roll back to a known-good NerdNOS commit if a recent flash broke your API. `github.com/shufps/qaxe` and the related forks tag releases — check the GitHub Releases page for the version preceding the one that broke things, flash that. Document the specific commit SHA or release tag that worked. The next time someone in your house auto-updates 'because there's a new version,' you have a one-step rollback path instead of a debugging session.
Disable swarm features if you're not using them. If you flashed a swarm-aware NerdNOS branch on a single standalone device, the `/api/swarm/info` endpoint may return malformed JSON or HTTP 5xx until you add a peer or disable swarm in Settings. Some custom dashboards hard-fail on the first 5xx and never retry. Disabling swarm if it's not your use case removes that whole class of false-positive 'API broken' reports.
Stop DIY and ship to D-Central if you have reflashed twice with the correct branch and the HTTP server still won't start, the ESP32-S3 won't enumerate over USB-C with BOOT held on any cable or laptop, the serial log shows repeated brownout / flash-read errors from a known-good PSU, or there is visible burn damage / lifted traces / burnt-component smell. At that point you have a dead ESP32-S3, damaged SPI flash, or a PCB-level fault — bench territory. D-Central does ESP32 reflash via JTAG / external UART, module replacement, and post-repair burn-in for the full QAxe / NerdQAxe / NerdNOS lineage.
When to Seek Professional Repair
If the steps above do not resolve the issue, or if you are not comfortable performing these repairs yourself, professional service is recommended. Attempting advanced repairs without proper equipment can cause further damage.
Related Error Codes
Still Having Issues?
Our team of Bitcoin Mining Hackers has been repairing ASIC miners since 2016. We have seen it all and fixed it all. Get a professional diagnosis.
