Access-token authentication (TOKEN), custom tunnel DNS (DNS), and a machine-readable status file headline this release, alongside a large reliability pass: silently skipped NETWORK/FORWARD_FROM values with whitespace, a crash-loop on non-OpenVPN TECHNOLOGY values, racing reconnections, and GATEWAY_DNS=redirect never applying its DNAT rules are all fixed.
📥 Pull this release
docker pull azinchen/nordvpn:7.2.0
docker pull ghcr.io/azinchen/nordvpn:7.2.0⬆️ Upgrade notes
PUID/PGIDhave been removed. They never had an effect (OpenVPN runs as root; the container writes no host files), so nothing changes functionally — drop them from your configuration to silence the startup warning.TOKENis a new alternative toUSER/PASS: generate an access token in the Nord Account dashboard and the container fetches the service credentials itself, refreshing them on every reconnect.USER/PASStake priority when both are set. See https://github.com/azinchen/nordvpn#getting-service-credentialsDNSoverrides the server-pushed resolvers (resolution stays inside the tunnel);DNS=offleavesresolv.confuntouched. See https://github.com/azinchen/nordvpn/wiki/Custom-DNSGATEWAY_DNS=redirectusers: the redirect DNAT chain was never populated in previous releases; after upgrading, client DNS interception through the tunnel works as documented.- List values (
NETWORK,COUNTRY,CHECK_CONNECTION_URL, …) now accept;or,with whitespace around separators ignored.
✨ Features
- Fetch OpenVPN service credentials from the NordVPN API with a
TOKENaccess token, with automatic re-fetch on every service start so rotated credentials self-heal (#1238, #1250) DNSvariable for custom tunnel resolvers, includingDNS=offto leaveresolv.confunmanaged (#1258)- Publish the selected server (name, hostname, IP, load, location, timestamp) as world-readable
/run/xt/status.jsonfor monitoring (#1251) GATEWAY_DNS_SERVERaccepts a list of resolvers; each candidate is probed at startup and the first one answering is used (#1252)- NordVPN API access uses the bootstrap IPs exclusively (tried in order), removing the DNS-dependent attempt that always failed under the kill switch (#1240)
🐛 Bug Fixes
- Trim whitespace around
;/,-separated list values;NETWORK="10.10.0.0/16; 10.20.0.0/16"previously skipped every value after the first separator silently (#1235) GATEWAY_DNS=redirectnever populated its DNAT chain: OpenVPN scrubs the environment for its scripts, so the redirect updater always saw the feature disabled — container env is now re-imported from the s6 store (#1258)- Non-OpenVPN
TECHNOLOGYvalues (e.g.Wireguard, a typo) crash-looped the service with no log output instead of reporting a clear configuration error (#1241) - An unknown or retired specific-server hostname in
COUNTRY/CITYaborted the whole server selection mid-loop (#1242) - Concurrent reconnections (overlapping cron schedules, manual runs) raced on the shared config paths and interleaved service restarts; runs are now serialized with a lock (#1248)
- A hung OpenVPN shutdown could leave an orphaned process colliding with the next start on the management port and
tun0; the service now records the PID and escalates to SIGTERM/SIGKILL (#1249) - The startup log claimed "Added firewall pinhole" even when the iptables rule failed; failures are now reported with a warning (#1247)
TOKENwas visible in the process list during the credential fetch; it now travels in a 0600 curl config file (#1246)- Scheduled jobs could fire before the VPN service had started; crond now starts after it (#1245)
- Non-numeric
CHECK_CONNECTION_ATTEMPTS/CHECK_CONNECTION_ATTEMPT_INTERVALsilently bypassed validation and broke the health check at runtime (#1243) - Network diagnostics printed the literal string
AF_INETinstead of the peer address (#1244) mgmt_cmdfell back to the username instead of the password when the management password file was missing (#1259)
♻️ Refactoring
- Remove the no-op
PUID/PGIDvariables and theinit-adduserservice; a transitional startup warning tells existing configurations the variables can be dropped (#1239)
📖 Documentation
- Environment variables regrouped into purpose-based tables with wiki deep links; list-separator behavior documented once (#1236, #1237)
- Wiki: list separators and whitespace tolerance documented across pages, plus a troubleshooting entry for the partially-applied
NETWORKsymptom on older releases (#1237) - Wiki: all four specific-server hostname patterns documented (
us1,ca-us100,nl-onion6,socks-nl1) and the resolution mechanism corrected to API lookup (#1255) - Wiki: quoted log strings aligned with actual output on four pages (#1254); stale
--data-ciphersauto-injection claims removed (#1253) TOKENexamples added to the compose/run example pages; example port mappings fixed to reach the sample app (#1256)- Architecture page synced with current behavior (reconnect locking and staging, pidfile shutdown escalation, TOKEN refresh, state files) and the diagnostics guide sample replaced with real output (#1260)
- Reconnection/health-check tuning guidance (#1232)
🔧 CI/CD Updates
- Automated apk version bumps are verified against every published architecture before opening a PR, preventing multi-arch build breakage from x86_64-only availability (#1257)
- Dockerfile architecture mappings aligned with the full Alpine ∩ s6-overlay support matrix (
ppc64le/s390xbuildable locally; published set unchanged); published vs buildable platforms documented in the wiki (#1257) - Bump github/codeql-action from 4.37.4 to 4.37.6 (#1233)
🧩 Component versions
| Component | Version |
|---|---|
| OpenVPN | 2.7.6 |
| OpenVPN XOR patches | 2.7.4 |
| Alpine Linux | 3.24.1 |
| s6-overlay | 3.2.3.2 |
Contributors
@azinchen, @dependabot[bot]
Full Changelog: v7.1.0...v7.2.0