Deep Dive 13 min read

V2Ray JSON Configuration Explained: What inbounds, outbounds, and routing do

Using a real-world configuration as an example, this guide breaks down the fields and relationships in the three main sections—inbounds, outbounds, and routing—so you can read and fine-tune your own configuration.

A V2Ray JSON configuration is not a script arranged in execution order; it is a declaration of the traffic path. Application traffic first enters an inbound. Routing rules inspect the domain, destination address, port, or inbound tag, then select an outbound for delivery. DNS, logging, and policy sections support the main path with name resolution, observability, and behavior controls. If you focus only on the server address, it is easy to miss the tag relationships that actually determine where traffic goes.

Quick overview

This guide is for readers who can import subscriptions but do not understand the generated configuration. It starts with the top-level structure, then reviews inbound listeners, proxy and direct outbounds, routing order, and DNS interaction, ending with a field checklist for configuration reviews.

Trace the full path first: JSON sections are connected

After reading the configuration, the V2Ray core creates listeners, outbound handlers, and a router. For a local browser, the browser sends requests to a local SOCKS or HTTP proxy port; the inbound accepts the connection and identifies its destination; routing evaluates rules from top to bottom; and the matching outboundTag points to a proxy, direct, or blocking outbound. If no rule matches, traffic usually falls back to the first outbound in the outbounds array, so array order matters too.

Application request Local inbound Destination identified Rule matching Outbound selected
10808
Example SOCKS port
10809
Example HTTP port
3
Primary outbound tag
Top to bottom
Routing rule order

The skeleton below omits server identity fields but keeps the connection points between sections. Pay close attention to the inbound tag, inboundTag and outboundTag in routing.rules, and the matching tags in outbounds. Tags are internal configuration names and can be customized; every reference must match exactly, including letter case.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks-in",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "udp": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls", "quic"]
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vmess",
      "settings": {}
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

Conclusion: trace tags before inspecting protocol details

When a rule exists but traffic is not split as expected, first verify that inboundTag, outboundTag, and the actual tags all match. Then check the domain and IP conditions. This is usually more effective than changing transport parameters first.

inbounds: What listens and what can enter the core

inbounds is an array of inbound entries, with each object representing a receiving endpoint. Desktop proxies most commonly use SOCKS, HTTP, or a local endpoint managed by the client. listen sets the listening address, port sets the port, protocol determines how the connection is interpreted, and settings stores protocol-specific options. With listen set to 127.0.0.1, only local programs can connect. Changing it to 0.0.0.0 exposes the listener on all network interfaces, so define the intended LAN access and review the system firewall rules first.

SOCKS inbound

tag
socks-in
listen
127.0.0.1
port
10808
protocol
socks
udp
true

Suitable for browsers, terminal tools, and applications that support explicit SOCKS5 connections.

HTTP inbound

tag
http-in
listen
127.0.0.1
port
10809
protocol
http
timeout
300

Suitable for desktop programs that read the system HTTP proxy settings.

tag does not change the protocol; it provides a stable reference for routing and logs. A configuration can define multiple inbounds and apply different policies to each. For example, socks-in can use the proxy by default while http-in is limited to internal networks. Routing rules can distinguish the two connection types through inboundTag instead of guessing from the source application.

sniffing recovers the target domain from data sent at the beginning of a connection. When a browser resolves a domain to an IP address before connecting, the router may see only the IP. Enabling sniffing and setting destOverride gives the core a chance to obtain the domain from an HTTP request or TLS handshake, allowing domain rules to participate in matching. It is not a DNS resolver and will not automatically fix every domain-routing issue. If an application uses an unrecognizable encapsulation, routing may still see only the address.

{
  "tag": "http-in",
  "listen": "127.0.0.1",
  "port": 10809,
  "protocol": "http",
  "settings": {
    "timeout": 300
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"]
  }
}
  • Port conflicts: When startup fails with a listening error, first check whether 10808 or 10809 is already occupied by another process.
  • Proxy type: The SOCKS5 or HTTP type entered in the application must match the corresponding inbound protocol; do not rely on the port number alone.
  • UDP toggle: If a SOCKS inbound must handle UDP, set settings.udp to true and confirm that the outbound and transport path support the target traffic.
  • LAN sharing: Do not change listen alone; also check the client's LAN access option, the system firewall, and the access-control scope.

outbounds: Defining proxy, direct, and blocking paths

outbounds is an array of exits. A proxy outbound encapsulates traffic for a remote path, a freedom outbound connects directly to the destination, and a blackhole outbound terminates matching connections. routing does not store server connection parameters; it returns an outboundTag. The actual address, port, user identity, transport, and security layer live in the referenced outbound.

VMess proxy outbound

tag
proxy
protocol
vmess
address
Node domain
port
443
network
ws
security
tls

User parameters are in settings, while transport and security parameters are in streamSettings.

Local control outbound

direct
freedom
block
blackhole
Reference method
outboundTag
Default outbound
First array entry

Private addresses usually go direct; destinations that must be explicitly terminated can be sent to block.

Using VMess as an example, settings.vnext contains the server list, users stores user parameters such as id, alterId, and security, and streamSettings describes transports such as TCP and WebSocket along with TLS settings. The transport, path, host name, and port required by the server must match as a complete set. Changing network from tcp to ws without also synchronizing path and the server-side endpoint will not produce a compatible connection.

{
  "tag": "proxy",
  "protocol": "vmess",
  "settings": {
    "vnext": [
      {
        "address": "server.example",
        "port": 443,
        "users": [
          {
            "id": "User ID provided by the subscription",
            "alterId": 0,
            "security": "auto"
          }
        ]
      }
    ]
  },
  "streamSettings": {
    "network": "ws",
    "security": "tls",
    "wsSettings": {
      "path": "/gateway"
    },
    "tlsSettings": {
      "serverName": "server.example"
    }
  }
}

VLESS is commonly used in Xray core configurations. The outer structure still contains tag, protocol, settings, and streamSettings, but fields such as flow and Reality depend on the capabilities of the relevant core and transport and cannot be copied mechanically into every V2Ray configuration. The core used by v2rayN depends on the client's settings; v2rayNG uses Xray core, while v2flyNG uses v2fly core. When reviewing generated subscription content, identify the active core first, then check the fields it supports.

  1. First confirm that the proxy entry's tag in outbounds matches the name actually referenced by routing.
  2. Then verify address, port, and user identity fields, taking care not to confuse a local listening port with the remote port.
  3. Next, check that transport-layer fields such as network, security, path, and serverName correspond as a complete set.
  4. Finally, review handshake, timeout, and connection-refused messages in the logs instead of changing parameters at random.

Conclusion: troubleshoot connectivity layer by layer

If the local port cannot connect, check the inbound first. For remote timeouts, check the outbound address and network. If only certain domains use the wrong exit, inspect routing. Layered troubleshooting avoids changing several variables at once.

routing: Match conditions, rule order, and the default outbound

routing.rules is an ordered array of rules. The common type is field, with conditions such as domain, ip, port, network, inboundTag, and protocol. When a rule contains different types of conditions, they generally all must be satisfied; multiple values within one field form a candidate set. Rules are evaluated in declaration order. The first match determines outboundTag, and later rules do not override it.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["domain:intranet.example"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

This rule set first allows private addresses, then specified internal domains, then terminates matching destinations, and finally sends tcp,udp traffic through the proxy as a fallback. If the fallback proxy comes first, it captures most connections and leaves the later direct and blocking rules no opportunity to run. When reviewing split-routing settings, ask not only “Is this rule present?” but also “Is there a broader condition before it?”

Field Match target Typical syntax What to check
domain Target domain domain:example.com Can the domain be obtained, or was it intercepted by an earlier rule?
ip Target address geoip:private Does domainStrategy trigger resolution?
port Target port 53 or 80–443 This is not the local inbound port
network Transport type tcp,udp Place broad conditions later as fallbacks
inboundTag Traffic entry point socks-in Must match the tag in inbounds

domainStrategy determines how routing handles domain and IP conditions. AsIs tries to match the original destination and does not actively resolve domains for IP rules. IPIfNonMatch tries domain rules first, then resolves the address and continues with IP rules if nothing matches. IPOnDemand resolves more proactively when an IP match may be needed. This is not a speed setting; it selects rule semantics. When a configuration includes IP rules such as geoip:private and the inbound commonly receives domains, IPIfNonMatch is an easy starting point to understand.

How DNS and routing work together: a resolved address does not determine the final outbound

The dns section defines the resolvers, static hosts, and query strategies available to the core, while routing decides which outbound handles the connection. They are related but separate steps. If an application sends its own DNS query, that query first enters the core as ordinary network traffic. If the application already supplies a destination IP, whether domain rules can participate depends on sniffing and whether recognizable domain information exists in the connection.

{
  "dns": {
    "hosts": {
      "domain:internal.example": "192.168.10.20"
    },
    "servers": [
      {
        "address": "223.5.5.5",
        "port": 53,
        "domains": ["geosite:cn"]
      },
      "1.1.1.1"
    ],
    "queryStrategy": "UseIP"
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "port": 53,
        "network": "udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

The example sends UDP traffic on port 53 to proxy, but this does not cover every resolution method: an application may use TCP 53 or an encrypted HTTPS connection for DNS. A more reliable troubleshooting approach is to determine whether the request comes from the system resolver, the application itself, or the core DNS, then decide whether routing should match a port, domain, or specific inbound. Adding a DNS address alone cannot guarantee that every request uses the same outbound.

  • When domain rules never match, check whether sniffing is enabled on the inbound and whether the logs show the destination as a domain or an IP.
  • If a private domain should go direct but is being proxied, place explicit internal-domain and geoip:private rules before the broad proxy rule.
  • If changing DNS produces no visible difference, restart the relevant application and clear the system DNS cache before running a single-target test.
  • If resolution succeeds but the connection times out, DNS has done its job; next inspect the routing decision and the outbound path.
53
Traditional DNS destination port
443
Common encrypted DNS port
3 layers
Application, core, system resolver

A pre-edit checklist: avoid having changes overwritten

Clients usually combine subscription nodes, global parameters, and routing settings into a runtime configuration. In v2rayN 7.13.x, general parameters are available under “Settings” → “Parameter Settings”; first record the local ports, core type, system proxy mode, and DNS options. Make routing changes in the routing settings provided by the client. In v2rayNG 1.10.x, check the local proxy, DNS, and split-routing options under “Settings.” The wording may change between versions, but the review order remains the same: identify the configuration source first, then verify the generated result.

  1. Keep a recoverable copy: Export the current client configuration or copy the standalone JSON you plan to modify, and record its original mode and scope.
  2. Validate JSON syntax: Check commas, quotation marks, square brackets, and braces. JSON does not allow end-of-line comments or an extra comma after the last array item.
  3. Check tag consistency: List every inbound tag, outbound tag, and rule reference, and confirm that none contain spelling differences.
  4. Change one layer at a time: Change only the inbound for port issues, only the outbound for node issues, and only rule order or conditions for routing issues.
  5. Build a minimal test set: Test one destination that should go direct, one that should use the proxy, and one that should be explicitly blocked, recording all three results.
  6. Return changes to persistent client settings: Once a temporary change works, write the equivalent setting back to the official configuration entry point in v2rayN, v2rayNG, or v2flyNG.

The JSON starts successfully, but every website uses the proxy. What is the most likely mistake?

First check whether the first entry in routing.rules captures every connection with network: tcp,udp or an overly broad domain condition. Move private addresses, internal domains, and explicit direct rules above the fallback proxy rule, then restart the core and test again.

Why did the server address revert after switching nodes?

The current file was likely generated dynamically from a subscription node. Edit the node profile saved by the client, or update the corresponding server field in the node list. Do not treat a temporary JSON in the runtime directory as a long-term configuration source.

The rule specifies a domain, but the logs show only an IP. What should I do?

Check that sniffing.enabled is true for the relevant inbound, and confirm that destOverride includes the actual traffic type. If only an IP still appears, check whether the application resolved the domain in advance and the connection contains no recoverable domain information.

10808 connects, but 10809 fails. Is the node broken?

First verify that inbounds declares both ports and that the protocol for 10809 is http. A failure on one local port is usually a listening or port-conflict issue, not evidence that the remote node is broken.

The configuration has proxy, direct, and block. How can I tell which one is actually being used?

Temporarily set the log level to info, visit preset proxy, direct, and blocking destinations separately, and compare the destination addresses with the errors. Restore warning afterward and keep the three test destinations for future regression checks.

The key to understanding a V2Ray configuration is not memorizing every field, but reconstructing each connection: which inbound received it, what destination it carried, which rule matched, and which outbound handled it. inbounds solve reception, outbounds solve delivery, and routing solves selection; DNS and logs add resolution and observability. Following this path makes it possible to quickly locate port conflicts, broken tag references, shadowed rules, and mismatched transport parameters—even when the configuration is generated automatically by a subscription and client.

Download v2rayN