The core purpose of V2Ray and Xray configuration files is to turn “a local request from an application” into “a network request handled by a selected outbound.” A configuration is not just a collection of unrelated fields; it defines a data path with a clear order: the application first connects to an inbound listener, the core reads the destination and performs traffic sniffing, the routing module selects an outbound based on the domain, IP, port or inbound tag, DNS participates when resolution is needed, and the selected outbound establishes the connection. Once this path is clear, many seemingly random failures can be traced to a specific stage that did not receive the expected information.
v2rayN is a major desktop GUI client for Windows, macOS and Linux, while v2rayNG and v2flyNG target Android. GUI clients typically generate the runtime configuration from their interface settings, so direct edits to temporary files may be overwritten at the next launch or after switching servers. For persistent changes, use the routing, DNS, parameter or custom-configuration options provided by the client. Visit the download page when you need an installer; for a first connection, follow the quick-start path.
JSON Structure Overview and Configuration Load Order
How the Top-Level Objects Work Together
A complete configuration uses one JSON object as its root node. Common top-level fields include log, dns, inbounds, outbounds, routing, policy and stats. Inbounds and outbounds form the two ends of the connection path, while routing links them; DNS supplies results for domain matching and destination resolution; the policy module controls timeouts, statistics and behavior for different user levels; and the log field determines how much diagnostic information is retained at runtime. Optional modules may be omitted, but the configuration needs at least an inbound that can receive requests and an outbound that can handle them. Otherwise, even if the core starts, no complete data path can be formed.
Array order and tags both affect configuration behavior. Each inbound or outbound can receive a stable name through tag, which routing rules can reference with inboundTag or outboundTag. A tag is only an internal configuration identifier: it is not a server name and does not change the protocol itself. Use short, consistent English tags such as socks-in, proxy, direct and block. Do not reuse tags within the same scope; doing so makes it difficult to determine which object a rule targets, and some implementations may reject the configuration outright.
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": [
"1.1.1.1",
"localhost"
]
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
JSON Syntax and Data Types
JSON is stricter than many configuration languages. Objects use curly braces, arrays use square brackets, and keys and strings must be enclosed in straight double quotation marks. Boolean values must be lowercase true or false, and numbers must not be quoted. Do not leave a trailing comma after the last object field or array element. Standard JSON does not support comments, so do not put explanatory text in a configuration that will be loaded. Keep maintenance notes in external documentation or use a client-provided notes field. Adding fields the core does not recognize may cause them to be ignored or trigger an error in strict parsing mode.
Field types cannot be substituted merely because they look similar. A port is usually a number such as 10808, not the string "10808"; a domain list must be an array, even with one item, for example ["domain:example.com"]. By contrast, a rule’s port is commonly expressed as a string such as "53" or "80,443,1000-2000". These differences come from each field’s definition, not from a single universal syntax. Before editing, confirm whether a field expects a number, string, Boolean, object or array. This prevents configurations that parse as JSON but fail when the core reads them.
Configuration Loading and Connection Order
When loading a configuration, the core first parses the JSON and initializes its modules, then binds the inbound listener addresses. A port already used by another program, a listener address not assigned to the machine or an invalid field type can terminate startup early. Successful startup only means that the configuration structure and local resources are basically usable; it does not confirm that remote protocol parameters are correct. A remote connection is usually established only after an application request arrives, so “the core has started” and “the destination is reachable” are separate checks.
When a request arrives, the core determines the inbound tag, destination address, destination port and network type. With sniffing enabled, it may also identify a domain from an HTTP request or TLS handshake. Routing then checks rules from top to bottom; normally, the first complete match determines the outbound. If nothing matches, the default outbound is used, often based on the first outbound in the array or the client’s generation logic. Whether DNS participates depends on whether the destination needs resolution and on the domainStrategy setting. Finally, the outbound establishes a connection using the protocol, transport, security layer and server parameters. Troubleshooting in this same order is more effective than repeatedly switching options.
Keep configuration maintenance focused on one responsibility: inbounds describe local access, outbounds describe exit capabilities, routing expresses selection conditions, DNS handles resolution paths, and policy controls connection lifetimes and statistics. Combining several purposes in one rule may reduce line count temporarily, but makes rule precedence and regression testing harder over time. Change one module at a time, validate the JSON before saving, check warning or info logs after startup, then test explicit domain, IP and port scenarios individually.
inbounds: Listen Addresses, Ports and Traffic Sniffing
What Inbounds Receive
inbounds is an array of inbound objects, with each object describing one local access method. Desktop clients most commonly use SOCKS and HTTP inbounds: applications that support SOCKS connect to the SOCKS port, while HTTP-proxy-only applications connect to the HTTP port. In system proxy mode, the client usually points the operating system’s proxy setting to one of these local ports. Transparent interception, virtual network interfaces and redirect inbounds require additional platform permissions and network-stack configuration and should not be confused with ordinary local proxy ports.
listen determines the local address to bind. With 127.0.0.1, only local connections are accepted, which is common on personal desktops. With 0.0.0.0, the listener binds to all available IPv4 interfaces, so other devices on the LAN may reach the port. An option in v2rayN such as “Allow connections from the LAN” effectively changes the listening scope and related firewall requirements. Expand the scope only when other devices on the same LAN genuinely need access, and verify the operating-system firewall and network environment at the same time.
port must be a valid local port that is not already in use. 10808 is a common local SOCKS listener port, but it is not mandated by the protocol and can be changed for your environment. After changing it, update the browser, development tools, system proxy or other callers as well; changing only the core’s port makes applications appear unable to connect to the local proxy. Multiple inbounds cannot bind the same address and port, even when their protocols differ.
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"routeOnly": true
}
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
SOCKS, HTTP and UDP Behavior
For a SOCKS inbound, settings.udp controls whether UDP requests are accepted. Enabling it does not mean every application will automatically use UDP, nor does it guarantee that the outbound and remote protocol can handle every UDP scenario. The application must pass UDP requests to the SOCKS inbound in a compatible way, and routing must not send them to an unsuitable exit. When troubleshooting “web pages work but some real-time apps fail,” check the application’s access method, the inbound UDP switch, routing conditions and outbound protocol capabilities separately.
An HTTP inbound primarily accepts HTTP proxy requests and HTTPS tunnels established with the CONNECT method. It is not a regular web server and will not automatically handle arbitrary protocols sent to that port. Some applications read only the system HTTP proxy, some support a separate SOCKS setting, and others ignore the system proxy entirely. Confirm what access method the caller actually supports before adding more listener ports. The more ports you have, the harder it becomes to trace port conflicts, rule sources and firewall behavior.
When the listener is expanded to the LAN, consider authentication in the inbound settings, bearing in mind that available authentication features depend on the inbound protocol and how the client generates its configuration. A safer approach is to restrict the network boundary first, allow only controlled devices, and avoid exposing a local proxy port to an untrusted network. For local-only use, a loopback address is usually sufficient. Windows, macOS and Linux present firewall prompts and network permissions differently, but the principle is the same: verify that the core is listening on the intended address, then verify that the client device can reach that address and port.
How sniffing Helps Routing
Traffic sniffing with sniffing identifies destination domains from connection content. Common sources include the HTTP Host header, the TLS Server Name and visible destination information in QUIC. Its value is clear when an application resolves a domain to an IP first and then passes only the IP to the proxy: a routing rule that sees only an IP cannot match geosite or domain-suffix rules. With sniffing enabled, the core can use the detected domain for routing, giving domain rules more reliable input.
destOverride specifies which protocol fingerprints may override or supplement destination information. Common values are http, tls and quic. When routeOnly is true, the sniffed result is mainly used for routing and does not directly rewrite the final connection target, reducing possible side effects from destination replacement. Choose this setting based on the rule design: sniffing has limited benefit if you use only IP rules, but is generally more useful with geosite, full-domain and suffix rules.
Sniffing is not general-purpose decryption, and not every connection can be identified. Encrypted application protocols, non-standard handshakes, direct IP access and pre-established multiplexed connections may not expose a usable domain. Rules should not assume that every connection will produce a sniffed domain; retain sensible IP rules and a default outbound. If a domain rule matches only intermittently, temporarily raise the log level to info and compare the application’s original destination, the sniffed result and the final outbound tag. Restore warning afterward to avoid excessive long-term logging.
Multiple inbounds can be combined with routing through different tags. For example, point a browser to browser-in and development tools to dev-in, then use inboundTag to send the two request types through different outbounds. This is easier to reuse across platforms than process-name matching, provided each application can use a separate proxy port. With v2rayN’s system proxy mode, keeping the standard client-generated inbound is usually enough; add a custom entry only when there is a clear isolation requirement.
outbounds: Protocol Parameters, Tags and Transports
Outbound Arrays and the Default Exit
outbounds describes how connections are handled as they leave the core. Proxy-protocol outbounds connect to remote servers, freedom provides direct access to destinations, and blackhole terminates matching connections. In practice, it is useful to keep at least three clearly named tags—proxy, direct and block—so routing rules can express proxying, direct access and blocking separately. Tag names are up to you, but outboundTag references must match exactly, including capitalization.
Which outbound handles traffic that matches no routing rule depends on both core behavior and the client’s configuration-generation method. Many configurations put the primary proxy outbound first, making it the default path for unmatched traffic; other clients generate an explicit fallback rule. When reviewing the actual runtime configuration, do not look only at the server shown in the interface. Check the outbound order and whether a catch-all rule appears at the end of routing. If you want explicit behavior, add a broad fallback condition at the end, but avoid an overly broad rule that intercepts all traffic too early.
{
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "11111111-1111-4111-8111-111111111111",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "www.example.com",
"fingerprint": "chrome",
"publicKey": "dGVzdC1wdWJsaWMta2V5LWZvci1kb2N1bWVudA",
"shortId": "0123456789abcdef",
"spiderX": "/"
}
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "none"
}
}
}
]
}
The proxy parameters above use documentation example addresses and credentials solely to show field hierarchy; they cannot establish a real connection. In a real configuration, the address, port, user identifier, transport and security parameters must remain consistent as a set. Subscription imports usually generate these fields automatically. When editing by hand, do not replace a single value based only on the protocol name, because one protocol may be combined with TCP, WebSocket, gRPC, TLS, REALITY and different flow-control modes.
How protocol and streamSettings Work Together
protocol selects the application-layer proxy protocol, while settings stores the server and user information required by that protocol. streamSettings describes the underlying transport and security layer. For VLESS, for example, the server address, port and user identifier are under settings.vnext; TCP, WebSocket or gRPC is specified by streamSettings.network; and TLS or REALITY is specified by streamSettings.security. These levels are related, but they cannot be interchanged.
The user-field structure differs between VMess, VLESS, Trojan and Shadowsocks. VMess commonly includes an id and security settings; VLESS uses id and encryption and may also use flow; Trojan uses a password; and Shadowsocks uses an encryption method and password. Subscription imports reduce manual-entry errors, but after importing, verify that the protocol, transport type, TLS server name and port are all present. If a subscription update fails or a share link has an unexpected format, work through the subscription update failure checklist.
mux multiplexing lets multiple logical connections share fewer underlying connections. It is not a universal speed switch; its benefit depends on the protocol, transport, server configuration and workload. Some long-lived or timing-sensitive requests may not suit additional multiplexing. If connections establish successfully but ongoing transfers are unstable, test with Mux disabled as an isolated variable instead of changing the protocol, security layer and routing at the same time.
direct, block and Outbound Constraints
A freedom outbound connects directly to the destination and is commonly used for private addresses, local-region domains or requests that should clearly be handled by the local network. It is still subject to local DNS, network routing and firewall rules, so “direct” only means that no proxy protocol is used; it does not guarantee reachability. settings.domainStrategy controls how freedom handles resolution for domain destinations. Coordinate it with the top-level DNS and routing domain strategies to avoid inconsistent results for the same domain at different stages.
A blackhole outbound does not establish a normal connection to the destination and is suitable for blocking known unwanted domains, IPs or ports. Keep blocking rules specific and place them where they need to take priority. An overly broad condition can make updates, sign-ins or LAN services appear to time out. During debugging, temporarily change a suspicious rule’s outbound to direct or a separate tag to confirm whether the rule is responsible, then restore the intended behavior.
Some configurations use sendThrough to choose the local address for an outbound connection, or sockopt to adjust low-level socket options. These fields are intended for multi-interface systems, specific routing tables or advanced network environments and should not be the first response to an ordinary connection failure. If the address is not assigned to a local interface, the outbound fails immediately. On desktops, keep the client defaults unless you can clearly describe the required interface, address family and routing behavior.
| Outbound Role | Common protocol | Primary purpose | Common checks |
|---|---|---|---|
| Proxy exit | vless、vmess、trojan、shadowsocks | Establish a connection using the remote protocol | Are the address, port, user parameters, transport and security layer consistent? |
| Direct connection | freedom | Reach the destination through the local network | Local DNS, default route, firewall and address family |
| Blocked connection | blackhole | Terminate requests matched by a rule | Is the rule scope or order too broad? |
Keep outbound tags stable during maintenance. Routing rules reference tags rather than array positions, so stable tags decouple server-parameter updates from routing policy. After switching subscription nodes, a client may rebuild the proxy outbound, but the meaning of direct and block normally stays the same. If custom configuration references internal tags that the client may rename, check the generated result after every update to ensure rules do not point to a nonexistent outbound.
routing: Rule Order, Domain Matching and IP Split Routing
Rules Match in Order
routing.rules is an array of routing rules. The most common rule type is field, which can combine conditions for domains, IPs, ports, network types, inbound tags, protocols and users. Rules are checked from top to bottom: more specific, higher-priority conditions should come first, with broader rules later. Once a rule matches, the request normally does not continue searching for a later alternative, so order is itself part of the policy.
Different conditions within one field rule usually form an “all conditions must match” relationship. For example, specifying both domain and port means the outbound is selected only when both conditions are satisfied; multiple domain values in the same array usually mean “match any.” Packing unrelated conditions into one rule makes it easy to mistake them for independent checks. A clearer approach is to split rules by purpose and give each one a single, explainable reason for matching.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "hybrid",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"geosite:cn",
"domain:example.cn",
"full:service.example.cn"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:category-ads-all"],
"outboundTag": "block"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
This example handles private IPs first, then specified and regional domains, followed by regional IPs, selected categories to block, and finally sends the remaining TCP and UDP requests to proxy. In practice, whether blocking should come before regional direct access depends on the category data and target policy. If a domain belongs to two sets, the earlier rule takes control. Before changing rule order, list potentially overlapping sets instead of judging only by rule names.
domainStrategy Determines When to Resolve
domainStrategy controls whether the routing module resolves a domain to an IP and then tries IP rules when domain rules produce no direct result. AsIs generally uses the destination form already available, without proactively resolving a domain to match IP rules. IPIfNonMatch tries domain rules first, resolves the IP when nothing matches, then checks IP rules. IPOnDemand prepares IP results more proactively during routing. Exact behavior also depends on the core implementation and configuration combination, but the principle is straightforward: introduce resolution at the routing stage only when domain destinations need to participate in geoip or CIDR rules.
Resolution is not free. It adds DNS queries and can make routing results depend on the resolver, cache and address family. If all important destinations are covered by geosite, full-domain or suffix rules, AsIs is easier to understand. If domain destinations need to be split with geoip:cn or custom CIDR rules, IPIfNonMatch is often more suitable. Do not copy a template’s value blindly; choose based on whether the rules contain IP conditions and whether applications submit domains.
With sniffing enabled, routing may receive a domain identified from traffic. Without it, an application that submits an IP gives domain rules nothing to match. Conversely, when an application submits a domain, whether IP rules participate depends on domainStrategy. Inbounds sniffing, routing’s domain strategy and DNS are therefore not three isolated switches. When troubleshooting, record whether the original request used a domain or IP, whether sniffing found a domain, whether routing triggered resolution and which IPs were returned.
Domain, IP, Port and Tag Conditions
Common domain-rule forms include full:, domain:, regexp: and geosite:. full:example.com matches only the exact domain; domain:example.com also covers its subdomains. Regular expressions offer flexibility but reduce readability and make unintended matches more likely. geosite uses categorized datasets and suits broad rule sets. Prefer full or domain for a single, clearly defined site, and use category data when the set is large.
IP rules can use CIDR, such as 192.168.0.0/16, or reference geoip:private and other datasets. Private-address rules should usually appear early and route directly; otherwise LAN admin pages, file services and local development environments may be sent through the proxy. When a domain resolves to a private address, whether it matches still depends on whether routing performs resolution. Adding a private rule alone does not solve every LAN-domain issue.
port supports a single port, comma-separated lists and ranges. network commonly uses tcp, udp or both. A port describes the destination port, not the application category. Many services share 443, so the port alone cannot identify a domain; port 53 can also carry different forms of DNS traffic. Port rules are useful for clear network policies, not as a substitute for domain identification.
inboundTag can split traffic according to the inbound it came from, which is useful for isolating multiple local ports. The protocol condition depends on what the core identifies, such as a protocol detected through sniffing. When using advanced conditions, keep a fallback rule at the end so unidentified traffic still has an explicit outbound. After changing routing, test at least five scenarios—private IP, a clearly direct domain, a clearly proxied domain, an ordinary uncategorized domain and a UDP request—and inspect the actual outbound tag.
v2rayN’s routing interface usually combines presets, rule sets and the current proxy mode into the final configuration. Selecting “Global” or another mode may change the default outbound or add extra rules, so review manual snippets together with the client mode. For guidance on trade-offs between protocols and routing scenarios, continue with VMess, VLESS, Trojan and Shadowsocks protocol comparison.
DNS Configuration: Resolvers, Matching Domains and Address Families
The Boundary Between Built-In DNS and System DNS
The top-level dns module supplies resolvers, static mappings and query policies for domain resolution performed by the core. It does not automatically take over every DNS request on the device. Only queries entering the core’s processing path, requested by routing, or sent to the core by a dedicated DNS inbound use these settings. If an application connects to an external resolver itself, a browser uses independent secure DNS, or a request bypasses the proxy entirely, the top-level dns configuration may not participate.
This distinction is important when troubleshooting. If a domain resolves differently from what you expect, first determine who initiated the query: the operating-system resolver, the application’s own resolver or Xray’s built-in DNS. Do not change system DNS, browser settings, the core’s server list and routing rules all at once; even if the result changes, you will not know which layer took effect. First disable the application’s independent resolver for comparison, then use logs to see whether the core sent a query.
{
"dns": {
"hosts": {
"domain:internal.example": "192.168.10.20",
"full:router.example": "192.168.1.1"
},
"servers": [
{
"address": "1.1.1.1",
"port": 53,
"domains": [
"geosite:geolocation-!cn"
],
"skipFallback": true
},
{
"address": "223.5.5.5",
"port": 53,
"domains": [
"geosite:cn"
],
"expectIPs": [
"geoip:cn"
]
},
"localhost"
],
"queryStrategy": "UseIP",
"disableCache": false,
"disableFallback": false,
"disableFallbackIfMatch": true
}
}
The example uses domains to limit a resolver’s scope and keeps localhost as a later option. Whether you need a public resolver address, the system resolver or another transport depends on the network and destination environment. Server order, matching domains and the fallback switch jointly affect the final choice, so do not treat servers as a simple round-robin list.
servers, hosts and Query Strategies
servers can use either a string or an object. The object form can add a port, matching domains, expected IP ranges and fallback behavior. domains specifies which domains should preferentially be handled by that server, using syntax similar to routing domain rules. expectIPs checks whether returned addresses fall within an expected range; it filters results rather than forcibly mapping arbitrary addresses to a target region. With an unsuitable configuration, valid results may be rejected and a later query may be triggered.
hosts provides static domain mappings and is useful for fixed LAN services or explicit test targets. It is not a replacement for a large hosts file; too many entries increase maintenance costs. Use full: for an exact domain and domain: when subdomains should also match. After mapping a name to a private address, verify that the private rule in routing sends the connection through direct; correct resolution alone does not guarantee the expected outbound.
queryStrategy controls which address-family results are requested. Common strategies allow both IPv4 and IPv6, IPv4 only or IPv6 only; use the exact field names supported by the current core. Before selecting a single address family, confirm that the local network and remote service support that path. If IPv6 exists locally but the route is unstable, a domain may return IPv6 first and then fail to connect; forcing IPv4, on the other hand, excludes IPv6-only destinations. Test resolution results and actual routing separately rather than relying on the presence of an address on the device.
fallback, Caching and the Routing Loop
The fallback mechanism tries other servers when the preferred result does not meet the conditions or no result is available. skipFallback excludes a specific server from general fallback, disableFallback disables fallback globally, and disableFallbackIfMatch limits further fallback when a domain has matched a specific server. Combining these switches can make a configured server appear never to be called. Start with the smallest configuration: verify one server first, then add domain scopes and fallback restrictions.
Caching reduces repeated queries but can keep results from changing immediately after an edit. disableCache is useful for short-term diagnosis, but should not remain enabled because of one stale result. Also consider caches maintained by the operating system and applications; they are independent of the core cache. Restarting the core clears only the state it holds and may not clear the browser or system resolver cache.
DNS and routing form a loop: routing may issue a DNS query to match an IP rule, while the DNS server’s own connection must also pass through routing. If the resolver address is a domain, that domain must be resolved before the connection can be made, creating a long dependency chain or even a cycle. Prefer explicit addresses for basic resolvers, or ensure their domains resolve reliably through the system resolver. If DNS queries should use a specific outbound, define clear tags and rules and prevent the rule from triggering the same resolution path again.
A typical problem is that “the domain rule looks correct, but traffic uses the wrong outbound.” Check in this order: did the application submit a domain or an IP; did sniffing identify a domain; did a domain rule match first; did domainStrategy trigger resolution; which DNS server was used; did it return IPv4, IPv6 or both; did an IP rule cover the result; and what was the final outbound tag? Recording each link in this chain usually narrows the problem to one specific stage.
| Field | Purpose | Best suited for | Common misconception |
|---|---|---|---|
| hosts | Provide a static domain mapping | LAN services and fixed test targets | Assuming it rewrites system resolution for every application |
| domains | Limit a resolver’s matching domains | Choose a resolution path by domain set | Ignoring server order and fallback conditions |
| expectIPs | Filter resolution results within an expected range | When returned addresses need range validation | Treating it as a fixed-address mapping |
| queryStrategy | Control the address family used for queries | Handle differences between IPv4 and IPv6 paths | Forcing one address family without testing network support |
v2rayNG and v2flyNG run in the Android networking environment, where Private DNS, application-level resolution and the client core’s DNS may coexist. The method is the same as on desktop: map the request path first, then identify which layer performs resolution. Do not treat the DNS option in system settings and the top-level dns field in the configuration as the same switch.
policy: Connection Timeouts, User Levels and Statistics
How level Policies Map to Users
policy configures connection lifetimes, user levels and system statistics. It does not select servers or change the routing outbound. The levels object uses level numbers as keys; each level can define handshake timeout, idle timeout, how long to keep a connection when only one direction remains active, and user traffic-statistics switches. The level in a protocol user entry refers to one of these keys; when no level is explicitly specified, the default level is generally used.
A level is not a service-quality score and does not automatically provide bandwidth priority. It simply applies a set of policy parameters to the associated users. For a single-user desktop client, level 0 is often all that is needed. Multi-user server configurations may assign different levels, but this page focuses on client runtime configurations: do not add many levels for “performance optimization” until you have confirmed that actual user objects reference them.
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false,
"bufferSize": 4
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
},
"stats": {}
}
Units and permitted ranges depend on the field definitions supported by the current core. Examples show common structures and do not mean every network benefits from the same timeouts. A handshake timeout that is too short can terminate a high-latency connection before it completes; one that is too long keeps failed connections consuming resources. Idle timeouts should not be interpreted as “longer is always more stable,” because many inactive connections consume resources.
Handshake, Idle and Half-Closed Connection Timeouts
handshake sets how long the connection-establishment phase may wait. An unreachable remote host, slow DNS resolution or mismatched security parameters can consume this time. If logs repeatedly show handshake timeouts, check the address, port, transport and network reachability before increasing the value substantially. A modest increase is appropriate only after confirming that the path is slow and connections eventually succeed.
connIdle controls how long a connection may remain open without activity in either direction. Short web connections rarely need a long idle period, while message synchronization, remote terminals and long polling may have little visible traffic for a while. If an application always disconnects after a fixed idle period, compare its heartbeat interval with connIdle. Also check the remote server, transport layer and intermediate network devices, any of which may close the connection first.
uplinkOnly and downlinkOnly handle connections with activity remaining in only one direction. After one direction ends, the core waits for the other to finish instead of closing the entire connection immediately. Values that are too short may truncate data still being sent; values that are too long delay resource release. Ordinary clients should generally keep the sensible defaults from the core or client generator, adjusting them only when logs and captured connection states clearly indicate a half-close problem.
bufferSize controls connection buffering. A larger buffer does not automatically increase speed and also raises memory use per connection. Low-power devices, many concurrent connections and large transfers have different buffering needs. Change one value at a time and observe memory use, connection stability and actual throughput; do not copy parameters intended for high-concurrency servers into an ordinary desktop client.
Statistics Switches and Runtime Overhead
statsUserUplink and statsUserDownlink control per-user uplink and downlink statistics, while fields under system control inbound and outbound direction statistics. The top-level stats module enables statistics, but an empty object alone does not automatically produce data for every dimension; the corresponding policy switches and a statistics-reading interface are also required. Whether v2rayN displays particular statistics depends on how the client starts the core and reads its data.
If statistics are not needed, leaving the related switches off reduces unnecessary state maintenance. To determine whether a particular inbound or outbound is carrying traffic, enable the relevant dimension temporarily, but do not equate traffic changes with connection quality. Statistics only show that data passed through a direction; they do not prove that domain rules, DNS results or remote application responses are fully correct. Diagnosis still requires logs and explicit test requests.
A common policy mistake is to blame connection failures on timeout values. Incorrect protocol parameters, unreachable DNS results, a wrong routing outbound and an occupied port are more common. First verify configuration loading, inbound listening, rule matches and outbound handshakes; only then determine whether a lifecycle policy closed the connection early. policy becomes the main suspect only when logs show that the connection was established and then ended at a repeatable point in time.
Foreground and background network behavior differs across Windows, macOS, Android and Linux. Mobile devices in particular may restrict network activity when an application is in the background. This cannot be fixed simply by increasing connIdle. If v2rayNG or v2flyNG stops working in the background, first check the system’s network and battery restrictions for the app. If v2rayN exits immediately after launch on desktop, start with the port, configuration parsing and core startup logs.
policy is best used for fine-tuning after the configuration is stable, not as a required module for a first connection. A simple client configuration can rely entirely on default policies. Add an explicit policy only when you have a clear need involving long-lived connections, statistics or resource usage. Fewer fields also mean fewer compatibility points to maintain when upgrading the core or migrating clients.
Configuration Validation, Log Reading and Systematic Troubleshooting
Separate Parsing, Startup and Connection Stages First
Configuration failures should first be divided into three stages. Stage one is JSON parsing: mismatched brackets, missing quotation marks, trailing commas and invalid field types prevent the configuration from being read. Stage two is core startup: port conflicts, invalid listener addresses and unsupported module fields prevent the process from entering a normal running state. Stage three is request handling: server parameters, DNS, routing, security-layer and network-environment problems usually appear only after an application sends a request. Separating these stages prevents you from changing servers repeatedly for a JSON syntax error or checking local ports when the remote handshake is actually failing.
A GUI client showing a “Started” state only suggests that the process may be running. Continue by checking whether the local port is listening, whether the system proxy points to the correct port, whether the application is sending requests and which outbound handles them. In v2rayN, review the Core type, local SOCKS listener port, LAN connection switch, sniffing, Mux, log level and system proxy mode. In v2rayNG and v2flyNG, verify the active configuration, connection mode and system network permissions.
When editing JSON directly, you can use a local JSON parser to check syntax, but do not submit a complete configuration containing real server parameters to an uncontrolled online tool. A safer approach is a local editor with JSON support or asking the core to load the file in a test mode. Command-line options vary by core and installation method, so follow the client’s actual invocation rather than copying arguments from another program.
{
"log": {
"access": "",
"error": "",
"loglevel": "warning",
"dnsLog": false
}
}
warning is suitable for normal operation, retaining important anomalies without producing excessive output. Temporarily switch to info when tracing routing or DNS, then lower the output again after diagnosis. Logs may contain destination domains, addresses and connection times; remove unrelated sensitive information before sharing them. Do not provide only the final error line—the preceding resolution, rule and handshake messages often explain the cause.
Trace the Data Path Step by Step
First check the inbound. Confirm that the SOCKS or HTTP port shown by the client matches the application setting, that no other program is using it and that the listener address fits local-only or LAN use. Start with an application that clearly supports proxy settings to avoid mixing in system proxy behavior, browser-specific settings and applications that ignore proxies. If the application cannot connect to the local port, there is no need to inspect the remote protocol yet.
Second, check the routing input. Record whether the application submitted a domain or IP, whether sniffing is enabled and whether the destination protocol can be identified. Then inspect conditions from the first rule onward instead of checking only the rule you expected to match. Look especially for broad rules earlier in the list, such as rules covering every port, every network or a large domain set. Once a match is confirmed, verify that outboundTag exists and is spelled consistently.
Third, check DNS. If a domain rule matched directly, DNS may be used only when the outbound connects to the destination; if geoip rules are involved, routing may resolve the destination first. Check which resolver was used, what type of addresses it returned and whether fallback changed the result. Test a full domain and one resolved IP separately: if the domain fails but the IP works, focus on DNS and domain rules; if both fail, inspect the outbound and network reachability.
Fourth, check the outbound. The proxy protocol’s address, port, user parameters, transport, security layer and server name must be consistent as a set. If fields are missing after importing a subscription, update the subscription and confirm that the client supports the sharing format. REALITY and XTLS Vision combinations depend on the Xray core and matching remote parameters; see REALITY and XTLS Vision handshake and flow-control guide for the underlying principles. Do not replace flow, fingerprint or serverName one by one based on guesses; that makes the original issue impossible to reproduce.
Fifth, check the system path. The system proxy affects only applications that honor that setting; it does not mean every connection on the device passes through the local inbound. If one application works while another connects directly, first confirm whether the latter supports the system proxy. Proxy settings differ across Windows, macOS and Linux, while Android clients generally take over traffic through the system’s network connection facilities. See client comparison for platform installation and client selection.
| Symptom | Check first | Next step |
|---|---|---|
| Core will not start | JSON syntax, supported fields, port conflicts | Restore the minimal configuration, then add modules section by section |
| Application cannot connect to the local proxy | Listener address, port and application proxy type | Confirm that SOCKS and HTTP settings are not mixed up |
| Some domains use the wrong outbound | Rule order, sniffing and domainStrategy | Compare matches for domain and raw IP inputs |
| Domain fails but IP connects | DNS server, hosts and address family | Check fallback and the application’s independent resolver |
| Connection drops after a fixed period | connIdle, half-close timeouts and background restrictions | Compare the establishment and close times in the logs |
Minimal Configuration and Binary Restoration
When repeated edits make it impossible to identify the faulty section, the most effective approach is to restore a minimal configuration. Keep one local SOCKS inbound, one proxy outbound with complete known parameters, a direct outbound and a simple fallback rule; temporarily remove custom DNS, complex routing, statistics and policy. If the minimal configuration works, restore modules one at a time, adding one related group of fields per step and completing the same fixed tests.
When there are many rules, use a binary-search approach: disable half of the custom rules and test. If the issue disappears, it is in the disabled half; if it remains, it is in the retained half or another module. Continue narrowing the range, which is usually faster than moving rules randomly one by one. Apply the same method to DNS server lists and hosts mappings. Keep the server, application, destination domain and network environment unchanged in every round so results remain comparable.
Keep a structured record for stable configurations, including the client type, Core type, inbound port, outbound tags, the purpose of routing rules, the reason for DNS choices and any policy fields changed. Recording the reasoning matters more than saving the configuration alone, because datasets, network conditions and client-generation logic can change and make old rules unsuitable. Periodically remove exceptions whose purpose is no longer clear so the configuration does not become something you only dare to add to, never revise.
After a GUI client updates a subscription or switches cores, check again whether custom fields are still written to the final runtime configuration. v2rayN’s Avalonia desktop edition and Windows WPF edition differ in interface and platform support, but the core troubleshooting path is the same; see v2rayN desktop and WPF edition differences for the specific choices. For first-launch or network-permission issues on macOS, see macOS installation and network-permission steps.
If the error still cannot be classified, use the Troubleshooting section to continue by category: fundamentals, installation and configuration, usage tips or troubleshooting. When asking for help or documenting an issue, include reproducible steps, the client name, operating system, core type, relevant configuration snippets and redacted logs instead of simply saying “it does not work.” The closer the information is to the actual data path, the easier it is to determine whether the failure is in the inbound, routing, DNS, outbound or system network layer.
The goal of a configuration file is not to accumulate the most fields, but to make every connection’s handling predictable. Start with the client-generated default structure as a working baseline, then add routing, DNS and policy only for clear requirements; keep test scenarios and rollback paths for every change. This lets you use the GUI management features of v2rayN, v2rayNG or v2flyNG while still being able to read the runtime configuration and trace a problem to a specific module.