Skip to content

Troubleshooting

Firewall stuck after a crash

If nym-vpnd crashes with the kill-switch on, its firewall policy remains loaded. WAN traffic outside the policy's exceptions stays blocked until the daemon recovers. DNS availability depends on the active policy and resolver configuration; it is not a reliable crash indicator.

To restart the daemon while keeping protection enabled:

/etc/init.d/nym-vpnd restart

To stop the VPN and restore ordinary WAN access:

/etc/init.d/nym-vpnd stop

Explicit Stop removes protection after the daemon exits; Restart preserves it. Procd also respawns crashed processes. If Stop cannot remove protection, check the runtime directory as described below. Deleting individual firewall chains is not a lasting recovery: a daemon policy update or firewall reload can restore them.

Upgrade interrupted half-way

If the package manager is killed during an upgrade (power loss, a closed SSH session without nohup, an OOM kill), the new files may already be on disk while the package database still records the old version, and opkg configure does nothing because no package is in the "unpacked" state. The upgrade hooks are designed to preserve the enabled kill-switch, but the daemon's state depends on where the interruption occurred. Recovery is to run the same install command again (opkg install <file>.ipk or apk add --allow-untrusted <file>.apk): it completes the transaction and restarts the daemon through the new init script. The packet-level test suite exercises interrupted upgrades in 08-interrupted-upgrade (opkg) and 18-upgrade-interrupted-apk (apk).

Kill-switch stays on after nym-vpnd stop

An explicit Stop normally removes protection and writes a stop marker in /var/run/nym-firewall/. The directory must be owned by root, mode 0700, and not a symlink. If it is untrusted, Stop cannot write the marker. On fw4, Stop can still remove the tables, but the next firewall reload installs a boot block. On fw3, teardown cannot safely take the state lock, so existing protection remains.

For an otherwise valid root-owned directory with the wrong mode, restore it with chmod 0700 /var/run/nym-firewall. Then run /etc/init.d/nym-vpnd stop again if you intend to open WAN access, or /etc/init.d/nym-vpnd start to restore VPN service. Reloading the firewall alone does not create the missing stop marker. If ownership or the path itself is wrong, repair that first rather than trusting files in the directory.

GL.iNet routers serve their admin panel through nginx on port 80, and nginx does not proxy /ubus — the endpoint LuCI uses for RPC. Open the app on port 80 and every RPC call gets an HTML redirect where JSON was expected. The daemon is fine; only the web transport is broken.

LuCI has its own uhttpd port (default 8080/8443) which serves /ubus correctly:

http://192.168.8.1:8080

If it is not listening there:

uci set uhttpd.main.listen_http='0.0.0.0:8080'
uci set uhttpd.main.listen_https='0.0.0.0:8443'
uci commit uhttpd
/etc/init.d/uhttpd restart

Upgrade from the LuCI Software page never finishes

Reported against packages up to 1.34.0: System → Software → Upgrade sits on "Executing package manager", and when the dialog finally goes away LuCI wants a new login — while opkg upgrade nym-vpn or apk upgrade nym-vpn from a shell works. Those packages restarted rpcd from their post-install step. LuCI runs opkg and apk through rpcd, so the restart cut off the reply the page was waiting for and dropped every login session. The upgrade itself normally went through; check with

opkg list-installed nym-vpn     # or: apk list -I nym-vpn

Newer packages refresh rpcd only after the package manager process has exited, so the page gets its result. Being asked to log in again after an upgrade that changed the web UI's permissions is expected.

Two limits apply to any large package installed from LuCI, not just this one: LuCI stops waiting for a package operation after 20 seconds and reports XHR request timed out, and rpcd kills the wrapper it launched after 30 seconds by default (rpcd.@rpcd[0].timeout). The package manager itself keeps running in both cases — nym-vpn is a 15–30 MB download written to flash, which can take longer than that on a slow router or link. Wait a minute, reload the page and check the installed version as above.

Not enough disk space

Installed size is roughly 18–36 MB — nym-vpnd is 16–33 MB depending on architecture, nym-vpnc another 2–3 MB. Devices with a small RAM-backed /tmp may not have room to stage the package.

df -h

Free some up:

rm -rf /tmp/opkg-lists          # re-fetch later with opkg update
rm -f /tmp/sf_log.txt /tmp/log/*

Don't stage the package on the overlay (/overlay/tmp or similar): that puts a third copy of the daemon on flash.

Upgrading with little flash

opkg stops the daemon once the package is downloaded, so an upgrade needs room for one copy of nym-vpnd. The kill-switch stays armed until the new daemon starts.

apk writes the new file before it removes the old one, so it needs room for both. If they don't fit, it fails with No space left on device and keeps the old version. Then upgrade by hand: download first, while the tunnel is up, and delete the old binary before installing. shutdown keeps the kill-switch armed, so the router stays blocked until the new daemon starts.

cd /tmp && apk update && apk fetch nym-vpn
/etc/init.d/nym-vpnd shutdown
rm /usr/sbin/nym-vpnd
apk add --allow-untrusted /tmp/nym-vpn-*.apk && rm -f /tmp/nym-vpn-*.apk
sed -i 's/^nym-vpn[<>=~].*$/nym-vpn/' /etc/apk/world

The last line matters: installing a local file pins the package to it, and apk upgrade would skip later releases.

If the install fails, /etc/init.d/nym-vpnd stop opens the router again.

Not enough RAM (OOM crash)

NymVPN needs roughly 80–100 MB. At 128 MB total you can hit OOM, most often at the moment both WireGuard tunnels start.

memory allocation of 26214400 bytes failed
Aborted

Fix it with zram — compressed swap in RAM, which roughly doubles what you can use:

opkg update
opkg install zram-swap
/etc/init.d/zram start
free -m                         # Swap total should be non-zero

zram-swap starts on boot from its own init script once installed:

/etc/init.d/zram enabled && echo "enabled" || echo "disabled"

File-based swap (swapon /path/to/swapfile) does not work on the UBIFS/JFFS2 filesystems OpenWrt normally uses. zram is the option.

Custom DNS setting has no effect

Custom DNS is enabled in the web UI (or nym-vpnc dns get says so), but lookups clearly go elsewhere — a leak test names your ISP's resolver, or one you never configured.

Check whether dnsmasq is ignoring its resolv file:

uci get dhcp.@dnsmasq[0].noresolv

If that prints 1, the daemon has deliberately stepped aside. The same setting in LuCI is Network → DHCP and DNS → Resolv & Hosts Files → "Ignore resolv file" (labelled just DNS on some versions).

The reason: the daemon applies VPN DNS servers by writing them into dnsmasq's resolv file, which is the only way to change upstreams without restarting dnsmasq — and a restart means several seconds with no DNS for the whole network on every connect. With "Ignore resolv file" ticked, dnsmasq never reads that file, so nothing written there can take effect. Your entries under Forwards are what resolve instead.

Usually that is correct and intended. AdGuard Home, https-dns-proxy and stubby all tick this box when installed, because you asked them to own DNS. Leave it alone — those queries still ride the tunnel while connected.

If you did not mean to, untick it:

uci delete dhcp.@dnsmasq[0].noresolv
uci commit dhcp
/etc/init.d/dnsmasq restart

VPN DNS servers apply from the next connect. Ad-blocking is unaffected either way — it works through separate dnsmasq directives, not upstream servers.

A resolver on your LAN (Pi-hole, AdGuard Home on another box)

A custom DNS server with a private address (10/8, 172.16/12, 192.168/16, link-local, IPv6 ULA) is not routed through the tunnel: it is handed to dnsmasq as an upstream and the kill-switch admits it on every interface except the WAN, whether the tunnel is up or down. So a Pi-hole on the LAN keeps answering the router and your clients while connected and while disconnected. While disconnected with the kill-switch on, dnsmasq's upstream list holds only the LAN resolver: the WAN-provided resolvers are rejected by the kill-switch anyway, and listing them would make dnsmasq burn its retries on them first. With the kill-switch off they follow the LAN resolver. The exclusion of the WAN is deliberate: behind another router the upstream gateway is also a private address, and a resolver there would carry every lookup out the ISP path — that one is rejected like any public resolver. The Pi-hole's own upstream queries are ordinary LAN-to-WAN forwards and follow the tunnel rules. If the daemon cannot determine the WAN interface it logs a warning and falls back to admitting the private resolver through the tunnel only.

No internet after clearing DNS forwards

You emptied Network → DHCP and DNS → Forwards, perhaps to force DNS through the VPN, and now nothing resolves.

This is the previous entry again: with "Ignore resolv file" ticked, the Forwards list is dnsmasq's only source of upstream servers, so emptying it leaves nowhere to forward to.

Put a forward back, or untick "Ignore resolv file" so dnsmasq falls back to the resolv file. Unticking is the better fix if VPN DNS was the goal, since it also lets the daemon supply the tunnel's servers.