Skip to content

WireGuard — mihomo

mihomo's WireGuard outbound runs in userspace (gVisor-backed). The schema offers both a simplified single-peer shape (where peer fields sit at the proxy root) and a verbose multi-peer shape via peers:. The optional amnezia-wg-option block enables interoperation with AmneziaWG-flavored servers.

Outbound

Entry under proxies: with type: wireguard. Embeds BasicOption plus the simplified-peer fields from WireGuardPeerOption.

FieldTypeDefaultAllowed valuesDescription
namestring(required)<string>Unique proxy name.
ipstring(unset)<IPv4 CIDR>Local tunnel IPv4 address (e.g. `10.0.0.2/32`).
ipv6string(unset)<IPv6 CIDR>Local tunnel IPv6 address.
private-keystring(required)<base64 key>Local private key.
workersint(CPU-based)<int>Encryption-pipeline worker count.
mtuint1408<bytes>Tunnel MTU.
udpboolfalsetrue | falseEnable UDP relay.
persistent-keepaliveint0<seconds>Persistent-keepalive interval. 0 disables keepalives.
amnezia-wg-option*AmneziaWGOption(unset)AmneziaWGOptionAmneziaWG obfuscation parameters (junk packets, header masking).
peers[]WireGuardPeerOption(use simplified shape)[WireGuardPeerOption]Verbose multi-peer list. When set, the embedded simplified fields are ignored.
remote-dns-resolveboolfalsetrue | falseResolve DNS queries through the WireGuard tunnel using the peer's resolvers.
dns[]string[][<DNS server>]Resolvers to use inside the tunnel when `remote-dns-resolve` is true.
refresh-server-ip-intervalint0<seconds>Re-resolve peer hostnames every N seconds. 0 disables periodic re-resolution.

Source: adapter/outbound/wireguard.go:57-77 · pinned at v1.19.27 (5184081)

peers[] — multi-peer shape

FieldTypeDefaultAllowed valuesDescription
serverstring(required)<host>Peer hostname or IP.
portint(required)<port>Peer UDP port.
public-keystring(required)<base64 key>Peer public key.
pre-shared-keystring(unset)<base64 key>Optional PSK.
reserved[]uint8(empty)<3 bytes>3-byte reserved-field override.
allowed-ips[]string[][<CIDR>]Allowed-IPs for this peer.

Source: adapter/outbound/wireguard.go:79-86 · pinned at v1.19.27 (5184081)

amnezia-wg-option

FieldTypeDefaultAllowed valuesDescription
jcint0<int>Junk-packet count per handshake.
jminint0<bytes>Minimum junk-packet size.
jmaxint0<bytes>Maximum junk-packet size.
s1int0<bytes>Pre-init-packet padding length.
s2int0<bytes>Pre-response-packet padding length.
s3int0<bytes>AmneziaWG v1.5/v2 — pre-cookie-packet padding length.
s4int0<bytes>AmneziaWG v1.5/v2 — pre-data-packet padding length.
h1string(unset)<int or hex>Header magic for the init packet. Numeric or hex.
h2string(unset)<int or hex>Header magic for the response packet.
h3string(unset)<int or hex>Header magic for the cookie packet.
h4string(unset)<int or hex>Header magic for the data packet.
i1string(unset)<hex>AmneziaWG v1.5/v2 — special-packet 1 payload.
i2string(unset)<hex>Special-packet 2 payload.
i3string(unset)<hex>Special-packet 3 payload.
i4string(unset)<hex>Special-packet 4 payload.
i5string(unset)<hex>Special-packet 5 payload.
j1string(unset)<hex>AmneziaWG v1.5 only — junk packet 1 payload.
j2string(unset)<hex>AmneziaWG v1.5 only — junk packet 2 payload.
j3string(unset)<hex>AmneziaWG v1.5 only — junk packet 3 payload.
itimeint640<seconds>AmneziaWG v1.5 only — junk-packet emission cadence.

Source: adapter/outbound/wireguard.go:88-109 · pinned at v1.19.27 (5184081)

Examples

Simplified single-peer outbound:

yaml
proxies:
  - name: wg-simple
    type: wireguard
    server: wg.example.com
    port: 51820
    private-key: <base64>
    public-key: <base64 peer key>
    ip: 10.0.0.2/32
    ipv6: fd00::2/128
    allowed-ips: ['0.0.0.0/0', '::/0']
    udp: true
    persistent-keepalive: 25

Multi-peer outbound (e.g., a hub-and-spoke setup):

yaml
proxies:
  - name: wg-multi
    type: wireguard
    private-key: <base64>
    ip: 10.0.0.2/32
    udp: true
    peers:
      - server: spoke1.example.com
        port: 51820
        public-key: <base64-spoke1>
        allowed-ips: ['10.0.1.0/24']
      - server: spoke2.example.com
        port: 51820
        public-key: <base64-spoke2>
        allowed-ips: ['10.0.2.0/24']

AmneziaWG-compatible outbound:

yaml
proxies:
  - name: awg
    type: wireguard
    server: awg.example.com
    port: 12345
    private-key: <base64>
    public-key: <base64>
    ip: 10.13.13.2/32
    allowed-ips: ['0.0.0.0/0']
    udp: true
    amnezia-wg-option:
      jc: 4
      jmin: 40
      jmax: 80
      s1: 0
      s2: 0
      h1: '0x12345678'
      h2: '0x87654321'
      h3: '0xabcdef01'
      h4: '0x10fedcba'

Notes

  • mihomo accepts both the simplified shape (top-level server/port/ public-key/allowed-ips) and the verbose peers: list. When peers is set, the embedded simplified fields are ignored.
  • remote-dns-resolve: true makes WireGuard tunnel DNS queries to the resolvers listed in dns: (instead of using the local resolver). Useful when the local DNS cannot reach the destination.
  • refresh-server-ip-interval only matters when a peer's server is a hostname; the field re-resolves it on a fixed interval, useful for dynamic-DNS endpoints.
  • amnezia-wg-option fields are versioned: s3/s4/i1-i5 are AmneziaWG v1.5+; j1/j2/j3/itime are v1.5-only (removed in v2.0). See source comments at adapter/outbound/wireguard.go:94-108.

Cross-core notes

  • Xray-core uses peers: always (no simplified shape), exposes a noKernelTun flag for the Linux fast path, and uses field names like secretKey, address, publicKey (camelCase). See WireGuard — Xray-core.
  • sing-box moved WireGuard to the endpoints model — it is no longer under outbounds[]. Field names use snake_case (private_key, allowed_ips). See WireGuard — sing-box.

Source: adapter/outbound/wireguard.go:57-109 · v1.19.27 (5184081)

Core Tutorial by Argsment