Skip to content

cs224/docker-nymvpn-socks5

Repository files navigation

docker-nymvpn-socks5

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.

Lineage

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

How It Works

The Compose stack has three services:

  • nymvpn1 runs nym-vpnd, connects to NymVPN, and owns the network namespace.
  • ssserver1 runs a local-only Shadowsocks server inside the nymvpn1 network namespace.
  • sslocal1 exposes a SOCKS5 listener, also inside the nymvpn1 network 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.

Why D-Bus And Polkit Are Included

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.

Prerequisites

  • Linux host with Docker and Docker Compose.
  • /dev/net/tun available on the host.
  • Docker permissions sufficient for NET_ADMIN, /dev/net/tun, and the configured security_opt entries.
  • A valid NymVPN account mnemonic.

Setup

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.env

Edit secrets.env and put the real account mnemonic on one line:

MNEMONIC_SECRET=word1 word2 word3 ... word24

Do 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.

Gateway Selection

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_ID
  • ENTRY_GATEWAY_COUNTRY
  • EXIT_GATEWAY_ID
  • EXIT_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 up

Run

Build and start:

docker compose build --pull
docker compose up

Or run detached:

docker compose up -d

Check status:

docker compose exec -T nymvpn1 /usr/bin/nym-vpnc status

Test traffic through the local SOCKS5 endpoint:

curl -s --max-time 20 --socks5-hostname 127.0.0.1:1090 https://ifconfig.me/ip

Stop the stack:

docker compose down

Secret And State Hygiene

This 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.

Troubleshooting

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=unconfined

Those settings are narrower than full Docker privileged: true, but they are needed by the current daemon's tunnel/firewall setup.

About

Run NymVPN in Docker and expose it as a local SOCKS5 proxy for app-selective routing.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors