nord-vpn
Container
This is an OpenVPN client docker container that uses recommended NordVPN server. It makes routing containers traffic through OpenVPN easy.
Image details
Source details
Configuration
TypeContainerlinuxazinchen/nordvpn:latestYes8080:80/tcp[email protected]PASS=passwordCOUNTRY=Spain;Hong Kong;IE;131GROUP=Standard VPN serversRANDOM_TOP=10RECREATE_VPN_CRON=5 */3 * * *CHECK_CONNECTION_URL=https://www.google.comNETWORK=192.168.1.0/24;192.168.2.0/24OPENVPN_OPTS=--mute-replay-warningsunless-stoppedTemplate by novaspirit
Notes
Check our Github page: https://github.com/pi-hosted/pi-hosted
Official Webpage: https://github.com/azinchen/nordvpn
Official Docker Documentation: https://hub.docker.com/r/azinchen/nordvpn
Standalone Install
Select an install method, to see config/commands for deploying nord-vpn
Install on Portainer
Import all app templates into your Portainer instance, for easy 1-click deploys
- Ensure both Docker and Portainer are installed, and up-to-date
- Log into your Portainer web UI
- Under Settings → App Templates, paste the below URL
- Head to Home → App Templates, and the list of apps will show up
- Select nord-vpn, fill in any config options, and hit Deploy
Template Import URL
https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me
More install options in our documentation, or see azinchen/nordvpn for app-specific guidance.

NordVPN OpenVPN Docker Container
!GitHub releasegithub-releasegithub-releases !GitHub release dategithub-releasedategithub-releases !GitHub buildgithub-buildgithub-actions!GitHub starsgithub-starsgithub-link !GitHub forksgithub-forksgithub-link !Open issuesgithub-issuesgithub-issues-link !GitHub last commitgithub-lastcommitgithub-link
!Docker pullsdockerhub-pullsdockerhub-link !Docker starsdockerhub-starsdockerhub-link !Docker image sizedockerhub-sizedockerhub-link
!Multi-archmultiarch-badgewiki-platforms
OpenVPN client docker container that routes other containers' traffic through NordVPN servers automatically.
Prefer WireGuard? This has a sibling project, azinchen/nordvpn-wg — the same auto-routing NordVPN container over WireGuard (NordLynx). Both share the same configuration model and feature set.
✨ Key Features
- 🚀 Easy Setup — Route any container's traffic through VPN with
--net=container:vpn - 🌍 Smart Server Selection — Auto-select servers by country, city, group, or specific hostname (detailswiki-server)
- ⚖️ Load Balancing — Intelligent sorting by server load when multiple locations specified
- 🔄 Auto-Reconnection — Periodic server switching and health monitoring (detailswiki-reconnect)
- 🕵️ XOR Obfuscation — Built-in XOR patches disguise OpenVPN traffic to bypass DPI (detailswiki-xor)
- 🛡️ Kill Switch — Default-deny firewall blocks all traffic when VPN is down (detailswiki-security)
- 🏠 Local/LAN Access — Allow specific CIDRs with
NETWORK=...(detailswiki-network) - 🧭 Custom DNS — Resolve through the tunnel; override with
DNS=...(detailswiki-custom-dns) - 📵 IPv6 Firewall — Built-in chains default to DROP (detailswiki-ipv6)
- 🧱 iptables Compatibility — Auto-selects nft or legacy backend (detailswiki-firewall)
- 🚪 VPN Gateway Mode — Route downstream subnets through the tunnel with
FORWARD_FROM(detailswiki-gateway)
📖 Full documentation on the Wikiwiki-home — configuration guides, examples, troubleshooting, FAQ, and architecture.
Quick Start
docker run -d --cap-add=NET_ADMIN --device /dev/net/tun --name vpn \
-e USER=service_username -e PASS=service_password \
azinchen/nordvpnRoute other containers through VPN:
docker run --net=container:vpn -d your/applicationAlso available from GitHub Container Registry:
ghcr.io/azinchen/nordvpnRequirements
- Docker with
--cap-add=NET_ADMINand--device /dev/net/tun - NordVPN service credentials or an access token (not regular account credentials)
Getting Service Credentials
- Log into your Nord Account Dashboard
- Click on NordVPN → Advanced Settings → Set up NordVPN manually
- Go to the Service credentials tab
- Copy the Username and Password shown there
Note: These are different from your regular NordVPN login credentials.
Alternative — access token: instead of copying the service credentials, generate an access token (Nord Account Dashboard → NordVPN → Advanced Settings → Generate new token) and pass it as
TOKEN; the container then fetches the service credentials from the NordVPN API at startup. If USER/PASS are also set, they take priority over the token.Docker Compose Example
services:
vpn:
image: azinchen/nordvpn:latest
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
environment:
- USER=service_username
- PASS=service_password
# - TOKEN=access_token # alternative to USER/PASS
- COUNTRY=United States;CA
- RANDOM_TOP=10
- RECREATE_VPN_CRON=0 */6 * * *
- NETWORK=192.168.1.0/24
ports:
- "8080:80" # host:container — use your app's listening port
restart: unless-stopped
app:
image: nginx:alpine
network_mode: "service:vpn"
depends_on:
- vpn
restart: unless-stoppedMore examples: Docker Composewiki-compose · Docker Runwiki-run
Environment Variables
List values (countries, cities, CIDRs, URLs, IPs) accept;or,as separators; whitespace around separators is ignored.
Credentials
NordVPN service credentials, set directly or fetched automatically with an access token — see Getting Service Credentials above.| Variable | Details |
|---|---|
| USER | NordVPN service credentials username. Required unless TOKEN is set. |
| PASS | NordVPN service credentials password. Required unless TOKEN is set. |
| TOKEN | NordVPN access token; the service credentials are fetched from the NordVPN API at startup. Ignored when USER/PASS are set. |
Server Selection
Pick which servers to connect to; filters combine to narrow the pool. See Server Selectionwiki-server.| Variable | Details |
|---|---|
| COUNTRY | Filter by countries: names, codes, IDs, or server hostnames (listnordvpn-countries). |
| CITY | Filter by cities: names, IDs, or server hostnames (listnordvpn-cities). |
| GROUP | Filter by server group (listnordvpn-groups, detailswiki-groups). |
| RANDOMTOP | Randomize top N servers. Default: 0 |
OpenVPN Connection
Protocol, port, and traffic obfuscation. See Technologieswiki-tech.| Variable | Details |
|---|---|
| TECHNOLOGY | OpenVPN protocol: name, identifier, or ID (listnordvpn-technologies). Default: openvpnudp |
| PORT | Force a specific port for the VPN connection. Must be supported by the server. Default: auto |
| DNS | DNS servers written to resolv.conf; resolution goes through the tunnel (detailswiki-custom-dns). off leaves resolv.conf untouched. Default: server‑pushed resolvers |
| XOR | XOR scramble obfuscation key for openvpnxor technologies (detailswiki-xor-key). Default: NordVPN's built-in key |
| OPENVPN | Additional OpenVPN parameters (detailswiki-openvpn-opts). |
Reconnection & Health Monitoring
Rotate servers on a schedule and verify the tunnel actually works. See Automatic Reconnectionwiki-reconnect.| Variable | Details |
|---|---|
| RECREATE | Server switching schedule (cron). Default: disabled |
| CHECK | Health monitoring schedule (cron). Default: disabled |
| CHECK | URLs to test connectivity. Default: https://www.google.com |
| CHECK | Connection test retry count. Default: 5 |
| CHECK | Seconds between retries. Default: 10 |
| HEALTHCHECK | Enable the Docker HEALTHCHECK probe (checks tun0 + connectivity via CHECKCONNECTIONURL). When false, the container always reports healthy. Default: false |
Local Network & VPN Gateway
Open the kill‑switch firewall for LAN access and downstream routing. See Local Network Accesswiki-network and VPN Gateway Modewiki-gateway.| Variable | Details |
|---|---|
| NETWORK | LAN/inter‑container CIDRs to allow. Default: none |
| FORWARD | Downstream CIDRs allowed to route OUT through the tunnel (gateway mode). Traffic must arrive already SNATed into these nets. Default: none |
| GATEWAY | DNS interception for FORWARDFROM clients: redirect (DNAT port 53 to the tunnel resolvers — server‑pushed, or DNS when set), local (DNAT port 53 to this container, for a co‑located resolver such as AdGuard Home), forward (DNAT port 53 to GATEWAYDNSSERVER, reached directly over the uplink — not through the tunnel), off. Default: off |
| GATEWAY | External IPv4 resolver(s) for GATEWAYDNS=forward (e.g. an AdGuard Home on your LAN). With a list, the first resolver answering a DNS probe at startup is used. Default: none |
Advanced
Low‑level settings; the defaults work for most setups.| Variable | Details |
|---|---|
| NORDVPNAPI | IPs used for all NordVPN API access (no DNS involved). Default: 104.16.208.203;104.19.159.190 |
| NETWORK | Enable network diagnostics on connect (detailswiki-diagnostics). Default: false |
Issues
If you have any problems with or questions about this image, please contact me through a GitHub issuegithub-issues-link or emailemail-link.Check the Troubleshootingwiki-troubleshoot and FAQwiki-faq wiki pages first.
Serve nord-vpn on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.
Proxying nord-vpn.example.com to http://nord-vpn:80
Add this to your Caddyfile
nord-vpn.example.com {
reverse_proxy http://nord-vpn:80
}Check the logs first
Nine times out of ten the logs tell you exactly what went wrong.
- In Portainer, go to Containers, click the container, then Logs. Or run
docker logs nord-vpn - Exit codes help too:
137means killed, usually out of memory.126or127means the command inside the image is broken.
Port already in use
If deployment fails with "Bind for 0.0.0.0:8080 failed: port is already allocated", something else on your server is using that port.
- Find what's using it:
sudo ss -tlnp | grep :8080 - Stop the other service, or pick a different host port. In
8080:80only the left number is yours to change, the right one belongs to the app.
Running but the page won't load
The container is up but nothing appears in your browser.
- Use your server's real IP:
http://your-server-ip:8080. The 0.0.0.0 link Portainer shows isn't a real address. - Give it a minute after first deploy, nord-vpn can take a while to initialise.
- Make sure your firewall allows the port, e.g.
sudo ufw allow 8080
Image won't pull
Test the pull directly on the host: docker pull azinchen/nordvpn:latest
- "manifest unknown" means the tag no longer exists. This template uses
latest, so try pinning a specific version instead. - "toomanyrequests" is the Docker Hub rate limit. Log in with
docker loginto raise it. - "no space left on device" means a full disk. Reclaim space with
docker system prune
"exec format error"
This means the image was built for a different CPU architecture than your server.
- This image supports:
amd64, arm64, arm/v7, arm/v6, 386, ppc64le, s390x, riscv64 - Check yours with
uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.
Container keeps restarting
The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.
- Check the logs right after a restart, the last few lines before it died are the useful ones.
- Get the exit code with
docker inspect nord-vpn --format '{{.State.ExitCode}}' - Still stuck? Redeploy once with the restart policy set to
noso the failure stays visible.
Data disappears when the container is recreated
This template doesn't define any volumes, so everything nord-vpn saves lives inside the container and is lost on update or recreate.
- Add a volume mapping for nord-vpn's data folder before storing anything you care about.
Privileged mode
This template runs the container in privileged mode, giving it full access to your host.
- Only deploy it if you trust the app.
- If deployment is blocked, your Portainer security settings or hardened host may not allow privileged containers.
Raise an issue
Found something which isn't working as it should? Here's how to report it.
- Bug within the app: Open an issue on azinchen/nordvpn
- Template not working: Open an issue on novaspirit/pi-hosted
- This website not working: Open an issue on lissy93/portainer-templates
A single container
nord-vpn runs as one container, the simplest kind of app here. Just the one image to pull and nothing else wired up alongside it.
The app image
An image is the app packed up ready to go, everything nord-vpn needs bundled into one download. This template pulls azinchen/nordvpn:latest, which Docker fetches once (about 15 MB) and then starts your own copy from.
Where the image comes from
Docker pulls its images from registries, public libraries of ready-built apps. nord-vpn's comes from Docker Hub, published by azinchen.
Version tags
The bit after the colon in the image name is the version tag. Here it's latest, which always points at the newest build, so a redeploy can bump you to a newer release without you asking. Newest right now is 7.2.0. Pin a specific tag if you would rather stay on one version.
Which machines it runs on
Every image is built for particular CPU types. This one ships for amd64, arm64, arm/v7, arm/v6, 386, ppc64le, s390x, riscv64, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.
Ports
A port is the door the app answers on. A mapping like 8080:80 means it's reachable on port 8080 of your server, where the left number is yours to change and the right one belongs to the app. Once it's running, open http://your-server-ip:8080 in a browser. It opens:
8080:80, likely the web interface
No stored data
This template doesn't mount any storage, so whatever nord-vpn writes stays inside the container and is wiped if it's recreated or updated. That's fine for something stateless, but add a volume before trusting it with anything you want to keep.
Environment variables
Environment variables are the settings you hand over when you deploy, things like a password or a timezone. nord-vpn takes 9 of them, all with defaults you can leave alone or tweak:
USER, defaults to[email protected]. NordVPN userPASS, defaults topassword. NordVPN passwordCOUNTRY, defaults toSpain;Hong Kong;IE;131. Countries to connect to (see webpage readme)GROUP, defaults toStandard VPN servers. Group name filter to which to connect toRANDOM_TOP, defaults to10. Range of servers to pick from the topRECREATE_VPN_CRON, defaults to5 */3 * * *. Define when to look again for new defined serversCHECK_CONNECTION_URL, defaults tohttps://www.google.com. URL to check internet connection is working toNETWORK, defaults to192.168.1.0/24;192.168.2.0/24. Subnet to define network access (read Local Network access to services connecting to the internet through the VPN readme to get the right subnet!)OPENVPN_OPTS, defaults to--mute-replay-warnings. Used to pass extra parameters to openvpn
Restart policy
The restart policy here is unless-stopped, so Docker restarts nord-vpn after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).
Networking
Nothing custom is set, so nord-vpn sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.
Container name
Once it's deployed, Portainer names the container nord-vpn. That's what you'll spot in the containers list and use in commands like docker logs nord-vpn.
Privileged mode
This template runs nord-vpn in privileged mode, which gives it nearly as much access to your server as the system itself. Some apps genuinely need it to reach hardware or manage the host, so it's one to run only if you trust the source.
Platform
The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.
Open source license
nord-vpn is open source, released under the AGPL-3.0 license. In plain terms the code is out in the open, so you're free to run it and change it to fit what you need.
Portainer app templates
Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set nord-vpn up. Add the template list to Portainer once, then deploying nord-vpn is a click rather than a wall of config.