WireGuard — Xray-core
Xray-core has a userspace WireGuard implementation that runs as a protocol on both sides. On Linux, an optional kernel-TUN fast path is available — set noKernelTun: true to opt back into the userspace wireguard-go device.
Settings
settings for an outbound or inbound of "protocol": "wireguard":
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
noKernelTun | bool | false | true | false | Disable the kernel-TUN fast path (Linux only) and run the userspace wireguard-go device instead. Useful for unprivileged runs. |
secretKey | string | (required) | <key> | Local private key. Accepts hex (64 chars), base64 (padded or padless), or URL-safe base64 — see ParseWireGuardKey at infra/conf/wireguard.go:126. |
address | []string | [10.0.0.1, fd59:...:0001] | [<CIDR or IP>] | Local interface address(es). If omitted, two bogon IPs are used (one IPv4, one IPv6). |
peers | []*WireGuardPeerConfig | [] | [WireGuardPeerConfig] | Remote peers. For the standard single-peer setup this is a one-element list. |
mtu | int32 | 1420 | <bytes> | Tunnel MTU. 0 substitutes 1420 (the WireGuard wire-format default). |
workers | int32 | 0 (CPU-based) | <int> | Number of worker goroutines for the encryption pipeline. 0 uses runtime.NumCPU(). |
reserved | []byte | (empty) | <3 bytes> | Three-byte reserved-field override (used by some commercial WG implementations). Must be empty or exactly 3 bytes. |
domainStrategy | string | forceip | forceip | forceipv4 | forceipv6 | forceipv4v6 | forceipv6v4 | How peer endpoint hostnames are resolved. The four-letter forms control which family is tried first (v4v6 = IPv4 then IPv6 fallback). |
Source: infra/conf/wireguard.go:51-62 · pinned at v26.6.1 (94ffd50)
peers[]
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
publicKey | string | (required) | <key> | Peer public key. Accepts the same formats as `secretKey`. |
preSharedKey | string | (unset) | <key> | Optional PSK for additional handshake security. |
endpoint | string | (required for outbound) | <host:port> | Where to reach the peer. Hostname is allowed and re-resolved at handshake time. |
keepAlive | uint32 | 0 | <seconds> | WireGuard persistent-keepalive interval. 0 disables keepalives. |
allowedIPs | []string | ["0.0.0.0/0", "::0/0"] | [<CIDR>] | Source/destination CIDRs routed to this peer. Defaults to all-routes-on if omitted. |
Source: infra/conf/wireguard.go:13-19 · pinned at v26.6.1 (94ffd50)
Examples
Outbound to a single WireGuard peer:
json
{
"outbounds": [
{
"tag": "wg-out",
"protocol": "wireguard",
"settings": {
"secretKey": "<base64-private-key>",
"address": ["10.0.0.2/32", "fd00:dead:beef::2/128"],
"mtu": 1420,
"peers": [
{
"publicKey": "<base64-peer-public-key>",
"endpoint": "wg.example.com:51820",
"allowedIPs": ["0.0.0.0/0", "::/0"],
"keepAlive": 25
}
],
"domainStrategy": "forceipv4v6"
}
}
]
}Outbound with a 3-byte reserved field (some commercial providers):
json
{
"outbounds": [
{
"tag": "wg-cloak",
"protocol": "wireguard",
"settings": {
"secretKey": "<base64>",
"address": ["172.16.0.2/32"],
"reserved": [123, 45, 67],
"peers": [
{ "publicKey": "<base64>", "endpoint": "engage.cloudflareclient.com:2408" }
]
}
}
]
}Notes
- Keys are normalized to hex internally. Hex (64 hex chars), padded base64, and URL-safe base64 are all accepted on input (
infra/conf/wireguard.go:133-148). addressdefaults to bogon IPs if omitted, which works for outbound- only setups but breaks any traffic that uses the WireGuard interface for its source address. Set this explicitly for any non-trivial configuration.mtu: 0is silently rewritten to 1420 at build time (infra/conf/wireguard.go:91-95).reservedmust be empty or exactly 3 bytes. A length-2 or length-4 array fails to build (infra/conf/wireguard.go:100-102).- The
noKernelTunflag is the only way to opt out of the kernel-TUN fast path on Linux. Other platforms always use userspace wireguard-go.
Cross-core notes
- sing-box moved WireGuard to the endpoints model in 1.11+ — see WireGuard — sing-box for the new field shapes (
private_key,address,peers[],udp_timeout). - mihomo accepts a simplified single-peer form (top-level
server/port/public-key/allowed-ips) or a verbosepeers:list, and exposes anamnezia-wg-optionblock for AmneziaWG compatibility. See WireGuard — mihomo.
Source: infra/conf/wireguard.go:13-62 · v26.6.1 (94ffd50)
