This guide is for users who can import subscriptions but still find generated configurations difficult to read. It follows a request through inbounds, routing, and outbounds, provides a local direct-connection example the core can run, and explains where VLESS and VMess parameters belong. It also shows how to diagnose JSON syntax, port conflicts, and rule-order problems after making changes.
Understand the outer configuration structure first
V2Ray and Xray typically use JSON for runtime configuration. The most common top-level objects are log, inbounds, outbounds, routing, and dns. The three sections that determine how requests enter, which path they take, and where they leave are inbounds, routing, and outbounds.
Think of a connection as a fixed data chain: a browser or another app first connects to a local listening port, the core identifies the destination, and routing rules select an outbound. Subscription nodes mainly populate outbounds, the client's routing mode generally generates routing, and system proxy settings tell apps to send traffic to inbounds.
The example below contains no remote node; it sends SOCKS requests directly from the local machine. Use it to verify JSON structure, port listening, and routing initialization. The core can load and run this configuration, but it does not provide remote proxy access.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "blocked",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
inbounds: where local apps enter the core
inbounds is an array, so one configuration can listen on multiple entry points at once. For example, SOCKS can listen on 10808 and HTTP on 10809. System proxy settings usually point to the HTTP entry, while apps that support SOCKS can connect directly to 127.0.0.1:10808. The actual ports come from the client-generated configuration; do not assume every installation uses the same values.
listen controls the listening scope. With 127.0.0.1, only programs on the same machine can connect. Using a LAN address or listening on all interfaces changes who can access the port. For typical desktop use, keep it bound to localhost. If you see “connection refused,” first confirm that the core is running, then verify that the app's port matches the inbound port.
- tag: The inbound's internal name, referenced by
inboundTagin routing. You may choose the name, but every reference must match it exactly. - protocol: Specifies the entry protocol. Desktop clients commonly generate SOCKS or HTTP inbounds; TUN mode creates the corresponding virtual network entry through the client.
- settings: Stores parameters for that inbound protocol. In the SOCKS example,
udp: trueallows UDP requests to be handled. - sniffing: Recovers the destination domain from connection content so domain rules can participate in matching. It is not a speed-test switch and does not directly change the remote node protocol.
Local SOCKS entry
- Listening address
- 127.0.0.1
- Listening port
- 10808
- Protocol
- socks
- UDP
- true
Suitable for browsers, download tools, and command-line programs that support SOCKS5 settings.
Local HTTP entry
- Listening address
- 127.0.0.1
- Listening port
- 10809
- Protocol
- http
- Use
- System proxy
Use the port shown in the client's runtime logs and generated configuration.
To verify the SOCKS entry, first check whether the core log contains a line such as “listening on 127.0.0.1:10808,” then connect to that address with a SOCKS5-capable tool. When using domain rules, have the tool resolve domains through the proxy instead of resolving them locally first, or the domain-matching condition may be lost.
outbounds: remote protocol settings and exit tags
outbounds is also an array. The first outbound usually serves as the default exit; later entries can provide direct connections, blocking, or other nodes. Each outbound consists of tag, protocol, settings, and optional streamSettings. routing does not contain server addresses directly; it selects one of these outbounds through outboundTag.
VLESS and VMess account details usually go under settings.vnext, including the server address, port, and user ID. Transport and security parameters such as TCP, WebSocket, and TLS belong in streamSettings. Do not mix the two layers: even with valid remote credentials, an incorrect transport path will still fail during the handshake.
VLESS + TCP + TLS
- Outbound protocol
- vless
- Transport
- tcp
- Security layer
- tls
- User encryption
- none
- Common ports
- 443
The address, user ID, server name, and security parameters must match the server configuration.
VMess + WebSocket + TLS
- Outbound protocol
- vmess
- Transport
- ws
- Path
- /ws
- Security layer
- tls
- Common ports
- 443
Verify the WebSocket path, Host, and TLS server name one by one.
{
"tag": "proxy-main",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "edge.example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000001",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "edge.example.com"
}
}
}
The address and user ID above only demonstrate where fields belong; they cannot be used to connect to a real node. Real configurations should come from a subscription or server-side parameters. When checking manually, start with protocol, then review address and port, verify the user ID, and finally check network, the TLS server name, and the WebSocket path.
freedom connects directly to the destination; direct is a commonly used tag. blackhole terminates matched connections; blocked is a common tag. Neither requires remote server parameters. Keeping proxy, direct, and blocked outbounds together lets routing handle proxying, direct connections, and blocking separately.
Bottom line: check the protocol layer, then the transport layer
Check settings when logs report authentication or user ID errors. Check streamSettings for TLS, WebSocket path, or connection-closed errors. Layer-by-layer diagnosis is more effective than repeatedly changing ports.
routing: rules match in order, not by name
routing.rules is an ordered array. The core checks rules from top to bottom and, after a match, uses that rule's outboundTag; later rules normally do not override the result. Put specific rules first and broad fallback rules last.
type: "field" matches requests by field conditions. Common conditions include domain, ip, port, network, inboundTag, and protocol. Multiple condition types in one rule generally must all match; multiple values within the same field match any one of those values.
| Rule position | Match conditions | Outbound tag | Purpose |
|---|---|---|---|
| Rule 1 | domain: domain:example.net | blocked | Block specified domains |
| Rule 2 | ip: geoip:private | direct | Connect directly to LAN and private addresses |
| Rule 3 | domain: geosite:cn | direct | Match the corresponding domain set |
| No match | No explicit conditions | Default outbound | Usually the first outbound |
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": ["domain:example.net"],
"outboundTag": "blocked"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy-main"
}
]
}
}
domainStrategy: "AsIs" mainly uses the domain or IP already present in the request; IPIfNonMatch tries to resolve the domain to an IP when no domain rule matches, then lets IP rules participate. Choose a strategy based on your DNS settings and sniffing results, rather than assuming the option that performs more lookups is faster.
The final network: "tcp,udp" rule is a broad fallback. If moved to the top, most TCP and UDP requests will match proxy-main immediately, making the later private-address direct rule ineffective. This is a common reason for traffic splitting to fail even though all the rules appear to be present.
Bottom line: check the first matching rule when routing behaves unexpectedly
Check the destination domain, resolution result, and rule order together. Confirming that a rule exists is not enough: it must appear before broader rules and reference an outbound tag that actually exists.
Where to edit client-generated configurations
v2rayN 7.x generates its runtime configuration from the selected node, routing settings, and core options. Direct edits to a runtime file may last only for the current process; switching nodes, updating a subscription, or restarting the core can regenerate it. For changes that should persist, make them through the corresponding client settings whenever possible.
To view basic options, go to “Settings” → “Parameter Settings.” To adjust node parameters, select a target in the “Servers” list and open its editor. Maintain routing-related settings under “Settings” → “Routing Settings.” Menu names may vary slightly between minor releases, but the targets remain the same three layers: inbounds, node outbounds, and routing rules.
- Back up the currently working configuration or export the client settings to keep a rollback baseline.
- Change one field at a time—for example, adjust only the listening port—then restart the core immediately after saving.
- First confirm that the JSON loads, then test the local port, and finally test the routing result for a specific domain.
- When changing routing, record the destination domain, the matching rule, and the final outboundTag.
- After a subscription update, review your custom settings again to confirm the client has not overwritten local changes with subscription fields.
When v2rayNG uses the Xray core, it also generates a corresponding runtime configuration, with a structure similar to the desktop client. When v2flyNG uses the v2fly core, follow the protocols and fields actually supported by that core. Some Xray extension parameters cannot be copied directly into a v2fly core configuration; if you see an unknown-field error, first confirm which core family is running.
Troubleshooting order for load failures and routing issues
Configuration problems fall into three groups: invalid JSON, a core that starts but has an unusable inbound, and a core with working inbounds whose routing results are unexpected. Checking them in this order helps prevent syntax errors from being mistaken for node failures.
The core exits immediately after saving—what should you check first?
Start with the first error line in the core log. If it reports invalid character, unexpected token, or a missing delimiter, check double quotes, commas, and square brackets. JSON cannot have a trailing comma after the final item.
What if port 10808 cannot be reached?
Confirm that listen is 127.0.0.1 and port is 10808 in inbounds, then check the logs for address already in use. If the port is occupied, switch to 10810 and update the app’s SOCKS address too.
The node works, but a specified domain does not connect directly—why?
Check whether sniffing identified the domain, then see whether a broad proxy rule appears before the direct rule. Move the specific domain rule above the network fallback rule, restart the core, and establish a new connection.
The log says the outboundTag cannot be found—what now?
Compare routing's outboundTag character by character with the tag in outbounds. proxy-main, proxy_main, and Proxy-Main are different names, and removing an outbound also requires removing rules that reference it.
The domain rule exists, but the IP rule is not being evaluated—why?
Check domainStrategy. If IP resolution should continue after no domain rule matches, use IPIfNonMatch and confirm that DNS returns a result. After editing, disconnect existing connections and test again so an old session is not reused.
After the port test passes, inspect the remote handshake logs. The VLESS or VMess address, port, user ID, transport, and security layer must form a complete combination. Changing only one field may allow the TCP connection to open before it fails during TLS, WebSocket, or protocol authentication.
When verifying routing, do not rely only on whether a webpage opens. A more reliable approach is to enable the core access log and confirm the outboundTag selected for the destination domain or IP. After changing rules, close existing connections and make a new request, because an established long-lived connection will not automatically switch to a new outbound.
- Step 1: Confirm that the JSON parsed successfully and the core did not exit during startup.
- Step 2: Confirm that 127.0.0.1 and the inbound port are listening, and that the app's proxy address matches exactly.
- Step 3: Confirm that the remote outbound protocol, port, user parameters, and transport parameters match.
- Step 4: Confirm that the request matched the intended rule and selected an outboundTag that exists.
- Step 5: Establish a new connection to eliminate interference from old sessions and cached results.
The key to understanding a configuration is not memorizing every field, but keeping the layers clear: inbounds determine “how traffic enters,” outbounds determine “where traffic leaves,” and routing determines “which exit this request uses.” When errors occur, follow the actual request path layer by layer; the issue usually narrows down to one port, one rule, or one set of protocol parameters.