Skip to content

Tunnel — mihomo

mihomo offers two equivalent ways to declare a static port-forwarder: the top-level tunnels: block (legacy, supports a compact string form) or an entry under listeners: with type: tunnel.

Listener form

Entry under listeners: with type: tunnel. Embeds BaseOption (listen, port).

FieldTypeDefaultAllowed valuesDescription
network[]string(required)tcp | udpTransports to accept. Pass both as `[tcp, udp]` to expose the same forwarder on both.
targetstring(required)<host:port>Destination of forwarded connections.

Source: listener/inbound/tunnel.go:13-17 · pinned at v1.19.27 (5184081)

Top-level tunnels: block

The top-level form is concise and pre-dates the unified listeners model. Each entry is either an object or a compact string:

FieldTypeDefaultAllowed valuesDescription
network[]string(required)tcp | udpTransports to accept. Object form takes a list.
addressstring(required)<host:port>Listening address.
targetstring(required)<host:port>Destination.
proxystring(routing default)<proxy or group name>Force this tunnel's traffic through a specific named proxy or group. Bypasses the rules list.

Source: listener/config/tunnel.go:11-16 · pinned at v1.19.27 (5184081)

The compact-string form is network,address,target[,proxy] — where network is tcp, udp, or tcp/udp:

yaml
tunnels:
  - tcp,127.0.0.1:5353,1.1.1.1:53        # TCP only
  - udp,127.0.0.1:5353,1.1.1.1:53        # UDP only
  - tcp/udp,127.0.0.1:5353,1.1.1.1:53    # both
  - tcp,0.0.0.0:8080,10.0.0.10:80,VPN    # forced through proxy "VPN"

The object form is the same thing with keys split out:

yaml
tunnels:
  - network: [tcp, udp]
    address: 127.0.0.1:5353
    target: 1.1.1.1:53
    proxy: VPN

Examples

Listener-form DNS forwarder:

yaml
listeners:
  - name: dns-fwd
    type: tunnel
    listen: 0.0.0.0
    port: 5353
    network: [tcp, udp]
    target: 1.1.1.1:53

Top-level form: forward HTTP to an internal host via a named proxy:

yaml
proxies:
  - name: VPN
    type: trojan
    server: vpn.example.com
    port: 443
    password: <password>

tunnels:
  - network: [tcp]
    address: 0.0.0.0:8080
    target: 10.0.0.10:80
    proxy: VPN

Notes

  • The compact-string form's address and target are both validated as host:port literals at parse time (listener/config/tunnel.go:53-58). A missing port produces a clear error.
  • Unknown values in network are silently skipped with a warning at startup (listener/inbound/tunnel.go:96-99). Only tcp and udp are accepted.
  • The proxy field bypasses the rules list — the tunnel's traffic goes through the named proxy regardless of mode or any matching rules: entries. Use this when you want a fixed route that is independent of normal routing.
  • The listener form and the tunnels: form can coexist in the same config — they are independent.

Cross-core notes

  • Xray-core uses the dokodemo-door protocol with a portMap field for multi-port forwarding in one inbound. See Dokodemo — Xray-core.
  • sing-box has no dedicated tunnel type — use the Direct inbound with override_address and override_port.

Source: listener/inbound/tunnel.go:13-17 · v1.19.27 (5184081)

Core Tutorial by Argsment