Skip to content

Quick start ​

Get a working tunnel between two hosts in a few minutes. You need a server host with a public (or otherwise reachable) UDP port, and a client host.

1. Install the binaries ​

The fastest path (Linux & macOS) — a one-liner per host that installs the latest release to /usr/local/bin and drops an example config at /etc/shadowvpn/:

sh
curl -fsSL https://raw.githubusercontent.com/madeye/shadowvpn/main/scripts/install.sh | sudo bash -s -- server
sh
curl -fsSL https://raw.githubusercontent.com/madeye/shadowvpn/main/scripts/install.sh | sudo bash -s -- client

One-shot server setup

On a Linux server, sudo bash -s -- server --setup does the whole server side in one command — config with a random password, systemd service enabled

Or build from source with a recent stable Rust toolchain (edition 2021):

sh
cargo build --release

This produces target/release/shadowvpn-server and target/release/shadowvpn-client. See Installation for the installer's options, release packages, Windows, and cross-compiling. The examples below use the source-build paths — with the one-line install, the binaries are just shadowvpn-server / shadowvpn-client on your PATH.

2. Start the server ​

Creating a TUN device requires elevated privileges (root on Linux, sudo on macOS, Administrator on Windows).

sh
sudo ./target/release/shadowvpn-server \
  --listen 0.0.0.0:8388 \
  --password "correct horse battery staple" \
  --cipher chacha20-poly1305 \
  --tun-ip 10.9.0.1 \
  --peer-ip 10.9.0.2

3. Start the client ​

sh
sudo ./target/release/shadowvpn-client \
  --server vpn.example.com:8388 \
  --password "correct horse battery staple" \
  --cipher chacha20-poly1305 \
  --tun-ip 10.9.0.2 \
  --peer-ip 10.9.0.1

Note how tun_ip and peer_ip are mirror images: the server's local tunnel IP is the client's peer, and vice versa.

4. Verify ​

From the client, ping the server's in-tunnel address:

sh
ping 10.9.0.1

A successful ping exercises the entire path: TUN → encrypt → UDP → server → decrypt → TUN, and the reply all the way back.

5. Route traffic through it ​

The tunnel itself is up, but nothing uses it until you route traffic into the TUN device. Pick one:

  • Full tunnel — route everything through the tunnel (and NAT it out on the server). See Routing & IP forwarding.

  • Split tunnel — let the client decide per destination with its built-in policy routing (gfwlist / chinadns modes), no external tooling:

    sh
    # tunnel only the domains in a gfwlist file
    sudo ./target/release/shadowvpn-client -c client.json \
      --mode gfwlist --gfwlist /etc/shadowvpn/gfwlist.txt
    
    # or: tunnel everything that isn't a China IP, from a GeoLite2 database
    sudo ./target/release/shadowvpn-client -c client.json \
      --mode chinadns --geoip /etc/shadowvpn/GeoLite2-Country.mmdb

    See Policy routing.

Either way, for tunneled traffic to reach the internet the server must enable IP forwarding and NAT — see Routing & IP forwarding.

Using config files instead ​

Both binaries accept -c, --config <PATH> pointing at a JSON file; CLI flags override JSON values. The same setup as above:

json
{
  "server": "0.0.0.0:8388",
  "password": "correct horse battery staple",
  "cipher": "chacha20-poly1305",
  "tun_ip": "10.9.0.1",
  "tun_netmask": "255.255.255.0",
  "peer_ip": "10.9.0.2",
  "mtu": 1400
}
json
{
  "server": "vpn.example.com:8388",
  "password": "correct horse battery staple",
  "cipher": "chacha20-poly1305",
  "tun_ip": "10.9.0.2",
  "tun_netmask": "255.255.255.0",
  "peer_ip": "10.9.0.1",
  "mtu": 1400
}
sh
sudo ./target/release/shadowvpn-server -c server.json
sudo ./target/release/shadowvpn-client -c client.json

See the configuration guide for every field.

Released under the MIT License.