Skip to content

Configuration

Configuration can come from a JSON config file, CLI flags, or both. CLI flags take precedence over JSON file values. Defaults are applied for anything not supplied.

Both binaries accept -c, --config <PATH> to point at a JSON file.

Core fields

JSON fieldCLI flagMeaningRequiredDefault
server--listen / --serverserver: UDP bind address; client: remote host:portyes
password-k, --passwordpre-shared password; master key derived from ityes
cipher-m, --cipherAEAD cipher namenochacha20-poly1305
tun_name--tun-nameexplicit TUN interface name (e.g. utun7, tun0)noOS picks
tun_ip--tun-iplocal IPv4 address on the TUN interfaceyes
tun_netmask--tun-netmaskIPv4 netmask for the TUN interfaceno255.255.255.0
peer_ip--peer-ippoint-to-point peer IPv4 (server: client IP; client: server IP)yes
mtu--mtuTUN interface MTUno1400
obfs(config only)carrier obfuscation: none | quic | base64 (both ends must match)nonone

On the server the server field is the UDP bind/listen address; on the client it is the remote server address to connect to.

There are more client-only fields for policy routing — see Policy routing and the full configuration reference.

Example: server config

json
{
  "server": "0.0.0.0:8388",
  "password": "correct horse battery staple",
  "cipher": "chacha20-poly1305",
  "tun_name": "utun7",
  "tun_ip": "10.9.0.1",
  "tun_netmask": "255.255.255.0",
  "peer_ip": "10.9.0.2",
  "mtu": 1400
}

Example: client config

json
{
  "server": "vpn.example.com:8388",
  "password": "correct horse battery staple",
  "cipher": "chacha20-poly1305",
  "tun_name": "utun7",
  "tun_ip": "10.9.0.2",
  "tun_netmask": "255.255.255.0",
  "peer_ip": "10.9.0.1",
  "mtu": 1400
}

Mirror images

tun_ip and peer_ip swap between the two ends: the server's local tunnel IP is the client's peer, and vice versa.

Choosing a cipher

All ciphers are AEAD, from the RustCrypto project:

Cipher name (config)Key / salt length
aes-128-gcm16 bytes
aes-256-gcm32 bytes
chacha20-poly130532 bytes

The alias chacha20-ietf-poly1305 is accepted and treated as chacha20-poly1305. The default (when none is specified) is chacha20-poly1305 — a good choice everywhere, and the best choice on ARM hardware without a crypto-enabled build. See the ciphers reference.

Carrier obfuscation

The optional obfs field shapes the UDP payload so it doesn't read as an opaque random blob — quic wraps each datagram as a QUIC 1-RTT short-header packet, base64 encodes it as printable ASCII. Both ends must agree, and it has no CLI flag:

json
{
  "server": "vpn.example.com:8388",
  "password": "correct horse battery staple",
  "obfs": "quic"
}

This is cosmetic framing only — it adds no security. Details, trade-offs, and wire formats: carrier obfuscation.

Sharing configs between devices

  • Export/import a client config as a single shadowvpn:// URI or QR code — see Config URIs & QR codes.
  • Run many clients off one identical config with the server's NAT mode.

Released under the MIT License.