Nov 2, 2026 · Configuration · About 12 min

V2Ray JSON Configuration Guide: inbounds, outbounds, and routing

Explore a complete V2Ray config, from log and inbounds to outbounds, routing, and DNS. Learn what each field does and how client-exported settings fit together.

At a glance

Start with the inbound, outbound, and the tags that link them, then look at routing and DNS. This guide walks through a traditional V2Ray JSON config with no remote server credentials, explaining its fields, rule order, and troubleshooting checks. It’s for users who have imported a subscription and want to understand files exported by v2rayN. The example covers local SOCKS access, direct connections, and blocking only; it does not provide remote proxy access.

1. Client settings vs. runtime config

v2rayN is a management interface; V2Ray and Xray are the cores that handle connections. A subscription stores server details or a config source, while the interface may also save groups, the current selection, and routing presets. The JSON read by the core at startup describes listening ports, outbound protocols, and routing rules. These files are not interchangeable just because they share a file extension.

This guide uses the traditional V2Ray JSON config format and does not cover every core’s proprietary fields. v2rayNG uses the Xray core, while v2flyNG uses the v2fly core. Seeing inbounds and outbounds in both does not mean every protocol option is compatible. For fields related to REALITY, flow control, and transports, check the documentation for the core actually in use.

App connection Inbound Routing match Outbound handling Destination

Follow this chain to trace references in the config. log helps you inspect what happens, while dns provides name resolution; neither is a network hop that must run in the order shown. The order of the five modules in the JSON object does not determine processing order, but the order of rules in the routing array can affect the result.

2. Full example: local SOCKS inbound and two outbounds

Here’s a complete config to illustrate the structure. It listens only on the local loopback address and accepts SOCKS requests. It blocks TCP connections to destination port 25; connections not matched by a rule go direct by default. It does not make apps use a proxy automatically or connect to a remote proxy server.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": false
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "inboundTag": ["local-socks"],
        "network": "tcp",
        "port": "25",
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": [
          "127.0.0.0/8",
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["full:localhost"],
        "outboundTag": "direct"
      }
    ]
  },
  "dns": {
    "servers": ["localhost"],
    "queryStrategy": "UseIP"
  }
}
10808
Example SOCKS listening port
2
Outbounds: direct and block
25
Example blocked TCP destination port

These are fixed values for this sample config, not defaults for every client version. If another program is using 10808, choose a free port and update the app’s SOCKS address to match. The private-network direct rule demonstrates the syntax; it is not a complete list of local network ranges.

3. log and inbounds: confirm requests reach the core

log.loglevel sets the amount of log detail. The example uses warning to focus on warnings and errors. For troubleshooting, you can temporarily switch to info, or use debug if needed. Leaving out access or error file paths does not mean logs disappear; the client that launched the core may still capture and display them.

inbounds is an array of entry points. This example accepts only SOCKS connections sent to 127.0.0.1:10808, so the app must be set to use that address. Pointing a browser’s HTTP proxy setting at a SOCKS-only port causes a protocol mismatch; simply starting the core does not change the system proxy settings.

What to check in the logs

loglevel
warning
Temporary troubleshooting
info or debug
Output location
Depends on the configured path and how the core was started

Once you’ve found the issue, lower the log level to avoid recording excessive connection details over time.

Local inbound

listen
127.0.0.1
port
10808
protocol
socks
tag
local-socks

tag is a routing reference, not a listening address or remote node name.

auth: "noauth" means this SOCKS inbound requires no authentication, so the example limits listening to the local machine. Don’t expose it on all interfaces just to let other devices connect without also setting up access controls. udp: false means SOCKS UDP forwarding is disabled on this inbound, so it can’t be used to verify whether UDP apps work.

A port conflict prevents the inbound from starting; a connection timeout occurs later in the chain. Check the logs for a listening error first, then investigate routing, DNS, or the remote server to avoid unnecessary changes.

4. outbounds: protocols connect; tags link to them

The outbounds array defines how connections leave the core. In this example, freedom connects directly to the destination, while blackhole drops matching traffic. direct and block are arbitrary tags. Renaming a tag does not change what the protocol does, but every rule that references it must be updated too.

FieldExample valueWhat to know
outbounds[0].tagdirectRouting reference; also the default outbound here
outbounds[0].protocolfreedomConnect directly to the destination, without a remote proxy
outbounds[1].protocolblackholeBlock connections selected by routing
settingsEmpty objectUses the default behavior in this example; proxy protocols usually need additional parameters

In this traditional config model, connections that match no rules go to the first outbound. Changing the array order can therefore change the default behavior. If block is first, requests that previously went direct may be blocked. Don’t treat the order as cosmetic.

A real VMess or VLESS outbound also includes the server address, port, authentication details, and possibly streamSettings. protocol specifies the proxy protocol; transport and security parameters go elsewhere. Simply changing freedom to vless will not establish a connection, and settings from different protocols cannot be swapped as-is.

To check the default behavior, inspect the first outbound and then its tag references

Before editing the config, note the tag of the first outbound and search for every outboundTag. Check both the default path and explicit routes to determine whether a connection will go direct, through a proxy, or be blocked.

5. routing: rules match in order, not by presumed specificity

routing.rules are checked from top to bottom, and the matching rule’s outbound is used. Longer domains and narrower IP ranges do not automatically get priority. Conditions within a rule generally all need to match; multiple values in a condition array usually match if any one of them does. Before configuring rules, distinguish between “all conditions” and “any condition.”

  1. Rule 1: The inbound tag must be local-socks, the network must be TCP, and the destination port must be 25. All three conditions must match for the connection to go to block.
  2. Rule 2: Send the connection to direct if the destination IP falls within any listed range. Even a private IP is blocked if it first matches the TCP port 25 rule above.
  3. Rule 3: full:localhost matches that exact domain, not every name containing the word localhost.
  4. No rule matches: Use the first outbound, which is direct in this example. Rules 2 and 3 produce the same result as the default here; they are included to demonstrate the syntax.

inboundTag must reference an inbound’s tag, and outboundTag must reference an outbound’s tag. Match spelling and capitalization exactly. These are not group names the core recognizes automatically, and they won’t follow changes to node display names in the client.

domainStrategy: "AsIs" means routing does not resolve domain names to IP addresses for IP-based rules. If the app passes a domain name, the second IP rule may not match; if it passes an IP address, the range can be checked directly. Switching to IPIfNonMatch attempts to resolve the domain after it fails to match at the domain stage. This does not mean all DNS requests are sent through a remote server.

6. dns: distinguish name resolution from traffic routing

The example’s dns.servers contains only "localhost", meaning it uses the local system’s name resolution. It does not listen on a local DNS port. queryStrategy: "UseIP" allows queries for both IPv4 and IPv6 addresses; the results still depend on DNS records, the resolver, and network conditions.

Including a dns module does not mean every app’s DNS requests pass through the core. An app may resolve a name itself and send the resulting IP over SOCKS, or pass the domain name to the core. In the first case, the domain information needed for domain-based routing may be lost; in the second, the destination domain is preserved. How the inbound receives requests and how the DNS module resolves names are separate concerns.

Config settingWhat it controlsDon’t confuse it with
dns.serversResolvers available to the built-in DNS moduleDNS settings for every app on the operating system
dns.queryStrategyAddress families to queryRouting rules that choose direct or proxied connections
routing.domainStrategyWhether routing uses resolved addresses when matchingForcing all DNS queries through the same outbound
Resolution behavior in an outboundHow that outbound handles destination domainsA universal field shared by every core

To choose a resolver based on the domain, you can explore fields such as domains in DNS server objects, but also check how query traffic reaches the resolver. Changing the DNS address alone does not prove that queries use the intended proxy route. When troubleshooting, check whether the request carries a domain or an IP address, and which outbound is ultimately selected.

7. Checking an exported config: from syntax to real connections

Make a copy of the config before editing rather than overwriting a runtime file generated by the client. v2rayN may regenerate the config when you switch nodes, update a subscription, or restart the core. Changes made only to a temporary runtime file may disappear on the next start. For lasting changes, use the client’s routing, DNS, or custom config settings; the exact location depends on the version.

  1. Check JSON syntax: Make sure quotation marks, brackets, braces, and commas are paired correctly. If Python is installed, use the command below to check basic syntax; passing this check does not mean the core supports every field.
  2. Check which core is running: Verify its name, version, and config file path in the client’s core selection and startup logs. Don’t rely on the client’s name alone.
  3. Check references: Search for each inboundTag and outboundTag to confirm its definition exists, and check the first outbound.
  4. Check the listener and app: Confirm 127.0.0.1:10808 starts successfully and that the app uses SOCKS on the same port.
  5. Test one change at a time: Change one module at a time. First test a basic TCP request, then test domain and blocking rules to avoid mixing variables.
python -m json.tool config.json

If the syntax is valid but the core won’t start, check the logs for the field path and error type. Unknown fields, incompatible protocol options, and port conflicts are different problems; changing DNS will not solve them all. Before sharing logs or a config, remove server credentials, subscription links, and any unnecessary personal file paths.

Why does the core still fail to start when the JSON check passes?

A syntax check only confirms that the file can be parsed. Open the client’s startup log, find the first error, and verify the protocol name, field types, and core in use. If the address is already in use, stop the program using port 10808 or change the listening port in both places.

Why did my original node stop working after I changed outboundTag?

First check that the new label exactly matches the tag of an outbound, then review the rule order. Don’t change only the routing reference and forget the outbound definition; also check whether an earlier rule is matching the request first.

Why doesn’t this copied config connect to a server in my subscription?

This example has only two outbounds: freedom and blackhole. To use a remote proxy, have the client generate the appropriate outbound from a valid subscription, then check how routing references it. Don’t guess at authentication parameters in this sample.

Why did my manual changes disappear after restarting v2rayN?

The client probably regenerated the runtime config. Keep copies from before and after your changes, save any routing or DNS changes you want to keep in the client’s persistent settings, then restart and check the newly generated file instead of repeatedly editing a temporary one.

Download V2Ray Clients