Run NymVPN in Docker and expose it as a local SOCKS5 proxy on
127.0.0.1:1090.
This gives applications an opt-in routing path: applications that support SOCKS5 can use NymVPN, while the rest of the host keeps using the normal network. It is not kernel-level split tunneling, but it is a practical split-tunneling-like workflow for proxy-aware applications.
This repository is an evolution of the earlier Dockerized NymVPN gist:
https://gist.github.com/cs224/1d01536c89f2fb0419674fc9152a977c
That gist was connected to the blog post:
https://weisser-zwerg.dev/posts/digital-civil-rights-networking-nym-node-operator-guide-2026/
The old gist followed an older NymVPN CLI/daemon behavior. This repository was updated and tested on 2026-04-23 with:
nym-vpnc 1.28.0
nym-vpnd Build Version: 1.28.0
nym-vpnd Commit SHA: 882cc06d9988459937d77fa16c63cd0749819ce1
The Compose stack has three services:
nymvpn1runsnym-vpnd, connects to NymVPN, and owns the network namespace.ssserver1runs a local-only Shadowsocks server inside thenymvpn1network namespace.sslocal1exposes a SOCKS5 listener, also inside thenymvpn1network namespace.
The host only sees:
127.0.0.1:1090 -> sslocal1 -> ssserver1 -> NymVPN tunnel
ssserver1 and sslocal1 use Shadowsocks method none intentionally because
that hop stays inside the local Docker network namespace. The host port is bound
to 127.0.0.1 only.
With NymVPN 1.28.0, Linux RPC access from nym-vpnc to nym-vpnd is
authenticated through the Polkit action:
com.nymvpn.vpnd.unix-access
Running nym-vpnc as root inside the container is not enough. The container must
also provide a system dbus-daemon, polkitd, and a rule allowing the intended
container user to access that daemon socket. This repo keeps those pieces inside
Docker instead of depending on host D-Bus/Polkit.
- Linux host with Docker and Docker Compose.
/dev/net/tunavailable on the host.- Docker permissions sufficient for
NET_ADMIN,/dev/net/tun, and the configuredsecurity_optentries. - A valid NymVPN account mnemonic.
Create the runtime directories if they do not exist:
mkdir -p config/nymvpn1/{etc,lib,logs}Create the local secret file from the example:
cp secrets.env.example secrets.envEdit secrets.env and put the real account mnemonic on one line:
MNEMONIC_SECRET=word1 word2 word3 ... word24Do not commit secrets.env. It is ignored by .gitignore.
The first successful run imports the mnemonic into persisted NymVPN account
state under config/nymvpn1/lib/. Later starts skip account import if persisted
account state already exists, but secrets.env should still exist because
Compose references it.
The Compose file exposes gateway selection in one-line presets. For entry and exit, set either an explicit gateway ID or a country:
environment: { ENTRY_GATEWAY_ID: "${ENTRY_GATEWAY_ID:-DBBCDYsgAAj7g4FLQkSxXZAcdG5m9Hx8vMreqRaX1Yqo}", ENTRY_GATEWAY_COUNTRY: "${ENTRY_GATEWAY_COUNTRY:-}", EXIT_GATEWAY_ID: "${EXIT_GATEWAY_ID:-7DfXy5Ucptc5AbMWL9gHzEJ7f688a5B2eRESw9EZcsP4}", EXIT_GATEWAY_COUNTRY: "${EXIT_GATEWAY_COUNTRY:-}" }
# environment: { ENTRY_GATEWAY_COUNTRY: "${ENTRY_GATEWAY_COUNTRY:-AT}", EXIT_GATEWAY_COUNTRY: "${EXIT_GATEWAY_COUNTRY:-CH}" }
# environment: { ENTRY_GATEWAY_ID: "${ENTRY_GATEWAY_ID:-entry-gateway-id-here}", EXIT_GATEWAY_COUNTRY: "${EXIT_GATEWAY_COUNTRY:-CH}" }Accepted variables:
ENTRY_GATEWAY_IDENTRY_GATEWAY_COUNTRYEXIT_GATEWAY_IDEXIT_GATEWAY_COUNTRY
Each side must have either an ID or a country. If both are set for the same side,
the country takes precedence. The script maps those variables to the NymVPN
1.28.0 flags --entry-id, --entry-country, --exit-id, and
--exit-country.
Keep exactly one preset uncommented. You can also override the same values from
the shell or a Compose .env file:
ENTRY_GATEWAY_COUNTRY=AT EXIT_GATEWAY_COUNTRY=CH docker compose upBuild and start:
docker compose build --pull
docker compose upOr run detached:
docker compose up -dCheck status:
docker compose exec -T nymvpn1 /usr/bin/nym-vpnc statusTest traffic through the local SOCKS5 endpoint:
curl -s --max-time 20 --socks5-hostname 127.0.0.1:1090 https://ifconfig.me/ipStop the stack:
docker compose downThis repo intentionally does not include real secrets or persisted runtime state.
Ignored local files include:
secrets.env- Compose
.env config/nymvpn1/etc/*config/nymvpn1/lib/*config/nymvpn1/logs/*- common key, database, and log file extensions
Treat config/nymvpn1/lib/ as sensitive. It can contain NymVPN account/device
state, keys, databases, and identifiers. Treat logs as potentially sensitive as
well because they can include gateway, device, or account-related metadata.
Avoid docker compose config when sharing output because it expands secrets.env
into the rendered Compose output.
If startup fails with:
Authentication is required to access the daemon
then nym-vpnc could not authenticate to nym-vpnd. Check the dbus,
polkitd, and vpnd logs under config/nymvpn1/logs/.
If NymVPN reports firewall/sysctl errors, verify that the nymvpn1 service still
has:
cap_add:
- NET_ADMIN
security_opt:
- apparmor=unconfined
- systempaths=unconfinedThose settings are narrower than full Docker privileged: true, but they are
needed by the current daemon's tunnel/firewall setup.