Skip to content

Installation

One-line install (Linux & macOS)

The quickest way to install: a single curl | bash that detects your OS and CPU, downloads the matching binary from the latest release, installs it to /usr/local/bin, and drops an example config at /etc/shadowvpn/ (the client also gets the bundled gfwlist.txt for policy routing):

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

Server: one-line full setup

On a Linux server, add --setup to go from zero to a running service in one command. Beyond installing the binary, it:

  • writes /etc/shadowvpn/server.json with a random password and "nat": true (an existing config is never overwritten),
  • installs the systemd unit with the detected WAN interface and tunnel subnet, then enables + starts it,
  • opens the UDP port in ufw/firewalld (if active),
  • prints the matching client config, ready to paste on your devices.
sh
curl -fsSL https://raw.githubusercontent.com/madeye/shadowvpn/main/scripts/install.sh | sudo bash -s -- server --setup

Pick a port or an obfuscation mode (both ends must match; the printed client config includes them):

sh
curl -fsSL https://raw.githubusercontent.com/madeye/shadowvpn/main/scripts/install.sh | sudo bash -s -- server --setup --port 443 --obfs quic

Cloud firewalls

--setup opens the host firewall only. On DigitalOcean/AWS/GCP etc., also allow the UDP port in the cloud firewall / security group — a blocked port looks exactly like a wrong password from the client side.

Uninstall

Uninstall the same way (configs are kept unless you add --purge):

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

Options and overrides:

  • --service (after the role) also installs the systemd unit / launchd plist from the release package — installed but not enabled; see Running as a service.
  • server --setup (Linux) does the whole server setup in one shot — see above; --port N and --obfs none|quic|base64 tune the generated config.
  • uninstall all removes both binaries; --purge also removes /etc/shadowvpn configs.
  • SHADOWVPN_VERSION=v0.4.0 pins a release tag (default: latest); PREFIX=~/.local installs to a user-writable prefix without sudo (configs/services are then skipped).

Prefer to read before you pipe?

The script is scripts/install.sh — short, commented, and reviewable.

Windows has no curl | bash flow — use the self-contained release .zip (client + wintun.dll + policy data), described below.

Release packages

Tagged releases on the GitHub releases page ship prebuilt packages per target:

TargetPackageContents
x86_64-unknown-linux-gnu.tar.gzserver + client binaries, bundled gfwlist.txt
aarch64-unknown-linux-gnu.tar.gzserver + client binaries, bundled gfwlist.txt
x86_64-apple-darwin.tar.gzserver + client binaries, bundled gfwlist.txt
aarch64-apple-darwin.tar.gzserver + client binaries, bundled gfwlist.txt
x86_64-pc-windows-msvc.zipclient + matching-arch wintun.dll + policy data (self-contained)
aarch64-pc-windows-msvc.zipclient + matching-arch wintun.dll + policy data (self-contained)

The release also builds installable desktop app packages — a .dmg on macOS (arm64 + x86_64), .deb/.AppImage on Linux (x86_64), and an NSIS -setup.exe on Windows (x64 + ARM64) — see the desktop app guide.

Bundled policy data

The Unix tarballs and the Windows zip bundle gfwlist.txt next to the client (the Windows zip also downloads a GeoLite2-Country.mmdb at package time), so policy routing modes work out of the box — no separate downloads.

Building from source

Requires a recent stable Rust toolchain (edition 2021):

sh
cargo build --release

This produces two binaries:

  • target/release/shadowvpn-server
  • target/release/shadowvpn-client

Run the test suite (crypto + config unit tests):

sh
cargo test --lib

The shadowvpn-uri helper (optional)

Config export/import as shadowvpn:// URIs and QR codes lives in a separate binary behind the uri feature (off by default) so the server/client builds stay lean:

sh
cargo build --release --features uri --bin shadowvpn-uri

See Config URIs & QR codes.

Windows

ShadowVPN builds on Windows (x86_64-pc-windows-msvc / aarch64-pc-windows-msvc) with the MSVC toolchain; CI builds and tests the Windows target on every push.

The client's TUN layer uses Wintun, whose wintun.dll is loaded at runtime and must sit next to shadowvpn-client.exe — download the build matching the CPU architecture and drop it alongside the binary (the release zip already includes the right one).

A recommended folder layout, with the self-elevating launcher from scripts/:

shadowvpn\
  shadowvpn-client.exe
  wintun.dll              <- required, matching CPU arch
  client.json             <- your config
  shadowvpn-client.ps1    <- self-elevating launcher
  shadowvpn-client.cmd    <- wrapper that bypasses the execution policy

ARM64 needs the ARM64 DLL

An x86_64 wintun.dll next to an ARM64 shadowvpn-client.exe (or vice versa) fails to load — typically Windows error 193 (%1 is not a valid Win32 application). Match the DLL to the binary's architecture, not the OS marketing name.

Cipher performance on ARM

chacha20-poly1305 (the default) uses runtime SIMD feature detection and is fast out of the box on every target. AES-GCM uses AES-NI automatically on x86-64, but on aarch64 (Raspberry Pi, Apple Silicon, Windows-on-ARM) the ARMv8 AES backend is gated behind compile-time target features — a plain cargo build for ARM runs AES-GCM in slow constant-time software.

  • On ARM hardware that lacks (or isn't built for) AES acceleration, prefer chacha20-poly1305 — it is both faster and simpler there.

  • To use AES-GCM at full speed on ARM, build with the crypto features enabled:

    sh
    RUSTFLAGS="-C target-feature=+aes,+neon" cargo build --release
    # or, when building on the target device itself:
    RUSTFLAGS="-C target-cpu=native" cargo build --release

See the ciphers reference for details.

Next steps

Released under the MIT License.