Bug: updater loop hangs on PUT /v1/updater/status and UPDATER_PERIOD stops after the first tick
Is this urgent?
No
Host OS
Ubuntu (VPS)
CPU arch
x86_64
VPN service provider
NordVPN
What are you using to run the container
docker-compose
What is the version of Gluetun
v3.40.0 (pinned by digest). The same loop code is in v3.41.3 and master (9408b39): git diff v3.40.0 v3.41.3 -- internal/updater/loop internal/server/updater.go is empty.
What's the problem
The server-data updater loop can hang for good, either through its own ticker or through the control server, and after that it never updates the servers data again until the container restarts.
We call PUT /v1/updater/status {"status":"running"} once when our app starts, and we set UPDATER_PERIOD=24h. Reading the code and running Go tests against the real loop package (fake Updater only, periods cut to milliseconds, test file attached), we reproduced three things.
1. The ticker wedges a loop that never crashed.
RunRestartTicker sends on l.start directly (internal/updater/loop/loop.go:169), bypassing SetStatus. Run then launches the update and, since crashed == false, blocks on l.running <- constants.Running (loop.go:112). Nobody reads it, because l.running is only read inside SetStatus (state.go:56).
Result: one tick-driven update, then Run is stuck on that send, and the next tick blocks the ticker goroutine on l.start <-. A later PUT running blocks forever on l.start <- (state.go:51), leaves the status at starting, and every following PUT returns already starting without doing anything.
Test output: with a 100ms period and no PUT, UpdateServers was called exactly 1 time in 1s (~10 ticks).
2. crashed is never reset, so a PUT after one failed update hangs.
crashed = false sits inside the if !crashed branch (loop.go:111-116), so once an update has failed (for example at boot, while the tunnel is still down: dial tcp: lookup ... write: operation not permitted, then retrying in 5s), Run never sends on l.running again.
A later PUT running does start an update, but SetStatus then waits on <-l.running forever while holding loopLock. The HTTP handler passes the server context, not the request context (internal/server/updater.go:78), so a client timeout never releases it.
A second PUT then blocks on loopLock while holding statusMu (state.go:37,47), so GET /v1/updater/status hangs too and no more updates run. The stuck goroutines survive shutdown.
3. The status can't tell these states apart.
GET /v1/updater/status returns completed for a healthy loop, for a loop that crashed once, and for a wedged one, so a client can't know whether a PUT is safe. The only safe case we found is stopped (a fresh process), and even that is not always safe: if the first tick-driven update fails, the status stays stopped but a PUT hangs.
Observed in production: after the container started, the updater failed every 5s until the tunnel came up, then succeeded. When our app restarted ~10 minutes later without recreating gluetun, its PUT /v1/updater/status timed out on the client side, yet gluetun logged [updater] starting and did update servers.json. That matches case 2.
Possible fixes (only a suggestion):
- reset
crashedcorrectly inRun; - make the ticker go through the same start path as
SetStatus, or don't block onl.runningwhen nobody is waiting (for example a non-blocking or buffered send); - use the request context in the updater handler, and
selecton the context when sending onl.start/l.stop.
Share your logs (at least 10 lines)
INFO [updater] updating Nordvpn servers...
ERROR [updater] getting servers: Get "https://api.nordvpn.com/v2/servers?limit=0": dial tcp: lookup api.nordvpn.com on 1.1.1.1:53: write udp ...->1.1.1.1:53: write: operation not permitted
INFO [updater] retrying in 5s
INFO [updater] updating Nordvpn servers...
ERROR [updater] getting servers: Get "https://api.nordvpn.com/v2/servers?limit=0": dial tcp: lookup api.nordvpn.com on 1.1.1.1:53: write udp ...->1.1.1.1:53: write: operation not permitted
INFO [updater] retrying in 5s
INFO [openvpn] Initialization Sequence Completed
INFO [healthcheck] healthy!
INFO [updater] updating Nordvpn servers...
WARN [updater] no region found for server United Kingdom #2318
(update succeeds, servers.json written)
... ~10 minutes later, our client sends PUT /v1/updater/status {"status":"running"} ...
INFO [updater] starting
INFO [updater] updating Nordvpn servers...
(client-side: request timed out after 10s; the server-side handler never returns)Share your configuration
image: qmcgaw/gluetun:v3.40.0
cap_add: [NET_ADMIN]
devices: [/dev/net/tun:/dev/net/tun]
environment:
- VPN_SERVICE_PROVIDER=nordvpn
- OPENVPN_USER=<redacted>
- OPENVPN_PASSWORD=<redacted>
- SERVER_COUNTRIES=Italy
- SERVER_CATEGORIES=Standard VPN servers
- UPDATER_PERIOD=24h
- HTTPPROXY=on
- HTTPPROXY_LISTEN=0.0.0.0:8888
restart: alwaysSource: passteque/gluetun