Skip to content

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":

FieldTypeDefaultAllowed valuesDescription
noKernelTunboolfalsetrue | falseDisable the kernel-TUN fast path (Linux only) and run the userspace wireguard-go device instead. Useful for unprivileged runs.
secretKeystring(required)<key>Local private key. Accepts hex (64 chars), base64 (padded or padless), or URL-safe base64 — see ParseWireGuardKey at infra/conf/wireguard.go:148.
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.
mtuint321420<bytes>Tunnel MTU. 0 substitutes 1420 (the WireGuard wire-format default).
reserved[]byte(empty)<3 bytes>Three-byte reserved-field override (used by some commercial WG implementations). Must be empty or exactly 3 bytes.
domainStrategystringforceipforceip | forceipv4 | forceipv6 | forceipv4v6 | forceipv6v4How 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:59-69 · pinned at v26.7.28 (5ca6f4b)

peers[]

FieldTypeDefaultAllowed valuesDescription
publicKeystring(required)<key>Peer public key. Accepts the same formats as `secretKey`.
preSharedKeystring(unset)<key>Optional PSK for additional handshake security.
endpointstring(required for outbound)<host:port>Where to reach the peer. Hostname is allowed and re-resolved at handshake time.
keepAliveuint320<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.
leveluint320<uint32>Policy level for this peer. Inbound (server side) only — each peer is registered as a user.
emailstring(unset)<string>Peer identifier shown in stats/logs. Inbound (server side) only.

Source: infra/conf/wireguard.go:17-26 · pinned at v26.7.28 (5ca6f4b)

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:155-170).
  • address defaults 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: 0 is silently rewritten to 1420 at build time (infra/conf/wireguard.go:116-120).
  • reserved must be empty or exactly 3 bytes. A length-2 or length-4 array fails to build (infra/conf/wireguard.go:122-124).
  • The noKernelTun flag is the only way to opt out of the kernel-TUN fast path on Linux. Other platforms always use userspace wireguard-go.
  • On an inbound (server-side) block, each peers[] entry is registered as a user: its email and level feed stats/logs and policy (infra/conf/wireguard.go:97-113). On an outbound both fields are ignored.

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 verbose peers: list, and exposes an amnezia-wg-option block for AmneziaWG compatibility. See WireGuard — mihomo.

Source: infra/conf/wireguard.go:17-69 · v26.7.28 (5ca6f4b)

Core Tutorial by Argsment