01 / CONFIG ROOT
JSON structure overview and data flow
A configuration is more than a list of nodes
The root of a V2Ray configuration file is a JSON object. Common top-level fields include log, dns, inbounds, outbounds, routing, policy and stats. Inbounds accept connections from local applications, outbounds determine where connections leave, routing maps each connection to an outbound, DNS supplies results for domain matching and target resolution, and policy and statistics control connection-level behavior. Understanding this flow matters more than memorizing fields: an application connects to an inbound port, the core identifies the target domain or IP, routing checks rules from top to bottom, the matching rule selects an outbound tag, DNS may resolve the target when needed, and the selected outbound establishes the connection.
A client-generated configuration may be more complex than a hand-written example because GUI clients can add local management interfaces, statistics endpoints, multiple DNS servers or compatibility fields. During troubleshooting, do not delete an unfamiliar section immediately. First identify its top-level module, then check whether other modules reference it by tag. For example, a routing rule's outboundTag must match an outbound tag; level numbers in policy must correspond to the levels used by users or inbounds; and a DNS tag may also be handled by routing rules as a separate traffic source.
JSON syntax and type requirements
JSON is strict about punctuation and data types. Objects use braces, arrays use brackets, and strings must be enclosed in double quotes; object members need commas, but the final member cannot have a trailing comma. Boolean values are written as true or false, not strings. Ports are normally numbers: "10808" may pass a JSON syntax check but still fail core field validation. Comments are not part of standard JSON, so copying an example containing // or block comments often produces an “invalid character” parsing error.
Field names are case-sensitive. outboundTag and outboundtag are different fields, and domainStrategy cannot be rewritten arbitrarily. Another common problem is incorrect nesting: protocol-specific parameters usually belong inside that object's settings, transport parameters belong inside streamSettings, and the server address cannot be placed directly at the outbound root. When you see “unknown field,” check the field's nesting level before suspecting the network.
| Top-level field | Primary responsibility | Common references |
|---|---|---|
inbounds |
Listen on local ports and accept SOCKS, HTTP and other connections | Identified by routing rules through the inbound tag |
outbounds |
Define proxy, direct and blocked exits | Selected by outboundTag |
routing |
Route traffic by domain, IP, port, protocol and inbound tag | Read inbound tags and point to outbound tags |
dns |
Provide domain resolution and DNS server selection | Affected by routing policy and domain-matching behavior |
policy |
Set timeouts, statistics switches and system-level policies | Can be used with user levels and stats |
Build outward from a minimal configuration
When maintaining a configuration by hand, start with the smallest set that can launch: one local inbound, one working proxy outbound, one direct outbound and a few routing rules. Once the core can read the file, add DNS, complex rules and policy settings. This separates syntax errors, protocol parameter errors and routing logic errors. Pasting hundreds of lines at once may seem convenient, but a misplaced brace can make the problem much harder to locate.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
}
]
}
A GUI client may regenerate the configuration after saving settings, so manual edits to a temporary file may not persist. Rules intended for long-term use should go through the client's custom configuration, routing-rule or DNS settings. To analyze a startup failure, export or copy the current configuration elsewhere, then reduce it section by section instead of letting the next subscription update overwrite the troubleshooting state.
02 / INBOUNDS
inbounds: listening, authentication and traffic identification
Inbounds define how local applications connect
inbounds is an array, so one configuration can provide multiple local entry points. Desktop clients commonly use SOCKS and HTTP inbounds: browsers and applications that support proxy settings can connect to an HTTP entry point, while SOCKS5-capable programs can use a SOCKS entry point. Transparent proxying, tunnel interfaces and LAN sharing are more advanced access methods and should wait until the basic local proxy works. Give each inbound a clear, unique tag, such as socks-in or http-in, so routing rules can distinguish entry points.
listen sets the listening address. With 127.0.0.1, normally only the current device can connect, which suits a personal desktop. Changing it to a LAN-reachable address expands access and requires a review of the system firewall, inbound authentication and network boundaries. Do not expose a wider listening address just because an application cannot reach the local port. First check that the client is running, the port is available and the application's proxy type matches the inbound protocol.
port is the local listening port and must match the system proxy or application proxy settings. The number itself is not fixed, but two programs cannot occupy the same port on the same address. If the log reports address already in use, close the conflicting process, change the listening port or check for a duplicate client instance. After changing the port, update browser settings, terminal environment variables and other applications as well; otherwise the core may be running normally while application traffic still never enters it.
SOCKS and HTTP inbound settings
A SOCKS inbound's settings commonly includes auth and udp. Personal use on a loopback address often uses noauth; if the listening scope is expanded, configure access control according to the client's capabilities. udp determines whether the inbound accepts UDP requests, but it does not guarantee that the remote protocol, outbound path and target service support UDP. An HTTP inbound mainly handles HTTP proxy requests and CONNECT tunnels, so the application must explicitly support HTTP proxy mode. Entering a SOCKS port in an HTTP proxy field often results in an immediate disconnect or a handshake-format error.
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
What sniffing does—and does not do
sniffing identifies the target domain from early connection data, allowing traffic that initially exposes only an IP address to be routed by domain rules. Common destOverride values include http and tls, corresponding to recognizable HTTP host information and the server name in a TLS handshake. It does not decrypt web content, and it cannot recover a domain from every connection. Changes to encrypted client hellos, non-standard protocols and direct IP connections may leave routing with only the IP address.
After enabling traffic identification, if an application's destination is rewritten or its connections behave unexpectedly, keep only the necessary override types first and compare the logs. Routing should not depend entirely on identification results: important local networks, reserved addresses and known IP ranges should still have explicit IP rules. Conversely, disabling identification may prevent many domain rules from matching, especially for applications that resolve domains at the system level and submit only an IP target. Enable it based on the inbound method and application behavior, not by applying one setting everywhere.
Dividing responsibilities across multiple inbounds
Multiple inbounds are useful not only for different proxy types but also for different routing policies. For example, one SOCKS inbound can use normal split routing while another always uses a designated outbound. Match the entry point with inboundTag in a routing rule, then set the destination outboundTag. Inbound-specific rules should come before general domain rules, or a broader rule may capture the connection first.
Troubleshoot an inbound in three steps: check whether the core log reports a successful listener, verify that the corresponding local port is actually listening, and confirm that the application request enters it. If the core receives no connection, the issue is usually the application's proxy settings, system proxy state or a port conflict. If a connection arrives but cannot reach its destination, move on to routing, outbounds and DNS. Do not repeatedly change server protocol parameters before the first step is confirmed.
03 / OUTBOUNDS
outbounds: protocols, servers and transport layers
Outbound arrays and tag design
outbounds describes how connections leave the core. A typical configuration includes at least one proxy outbound, one freedom direct outbound and one blackhole blocking outbound. A proxy outbound connects to a remote server; a direct outbound reaches the target through the local network; a blocking outbound explicitly rejects selected traffic. Routing chooses exits only by tag, so tags should be stable, short and purpose-driven, such as proxy, direct and block. When changing nodes, you can update the server parameters inside proxy without rewriting every routing rule.
Array order may affect the default exit when no routing rule produces an explicit result. To avoid relying on implicit behavior, direct important traffic to a tag with explicit rules and keep the primary proxy outbound in an easy-to-identify position. A GUI client may dynamically place the current node first. When merging configurations by hand, do not infer an outbound's purpose from position alone; also inspect its tag, protocol and protocol settings.
Separating protocol and transport parameters
A proxy outbound usually has two layers: settings stores the server, port and user information required by the protocol itself; streamSettings stores the underlying network, TLS, REALITY or WebSocket transport settings. Both must match the server. If the protocol is correct but the transport layer is not, TCP may connect before the handshake fails; if the transport is correct but the user identity is wrong, the remote server may reject the connection immediately.
The example below shows where the fields of a VLESS outbound belong. The domain, identifier and public key are illustrative values and must be replaced with a real configuration. security inside the user object describes VLESS user-layer settings; security also appears in streamSettings, where it identifies the transport security method. The same field name appears at different nesting levels and cannot be substituted between them.
{
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000000",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "www.example.com",
"fingerprint": "chrome",
"publicKey": "replace-with-server-public-key",
"shortId": "0123456789abcdef"
}
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "none"
}
}
}
]
}
address, serverName and the target domain are different things
address is the server address the client actually connects to, either a domain or an IP. The TLS- or REALITY-related serverName is the server name used in the handshake; it may match the address or be determined separately by the server configuration. The target domain in routing is the site the application originally wants to reach. Keep all three separate when troubleshooting: failure to resolve the server address is an entry-point problem, a mismatched server name usually causes a handshake failure, and an application target sent through the wrong exit is a routing problem.
If the server address is a domain, the core must resolve it before opening the proxy connection. Overly aggressive DNS rules may send that lookup through a proxy path that has not been established yet, creating a circular dependency. A safer approach is to provide a working initial resolution path for the server domain or use the client's supported server-address handling. If every node fails after a DNS change, check this layer first instead of re-entering each node.
Multiple proxy outbounds and selection
A configuration can contain multiple proxy outbounds, such as proxy-main and proxy-alt. However, multiple outbounds in the V2Ray core configuration do not automatically provide the latency-based selection or failover behavior of a GUI client. Selection is determined by routing rules, load-balancing policy or client-generated logic. Do not append several node objects and expect automatic switching. The node-selection UI in v2rayN, v2rayNG and v2flyNG generates the corresponding configuration; with a hand-written configuration, make clear which component references each tag.
Troubleshoot an outbound from the outside in: confirm that the server address resolves, the destination port is reachable, the protocol user fields are correct, and the transport security and network type match. A timeout usually points to an unreachable address, port or path; handshake failed is more likely a transport-layer mismatch; invalid user and other authentication messages point back to the user identity. For detailed TLS error categories, see the TLS certificate error troubleshooting checklist.
04 / ROUTING
routing rules: match order and split-routing syntax
Rules match from top to bottom
routing.rules is an ordered array. The core checks connection attributes one rule at a time and normally selects the corresponding outbound when an applicable rule matches, so order directly determines the result. Put narrower, higher-priority rules first and fallback rules later. For example, block unwanted local protocols, handle private addresses, send selected domains direct, then define proxy or default exits. If a broad domain rule comes first, later precise rules may never take effect even when their syntax is valid.
Multiple match dimensions in one rule usually add constraints to the same connection. For example, a rule containing both inboundTag and domain requires the connection to come from the specified inbound and satisfy the domain condition. Multiple entries within one dimension generally mean that any one of them can match. For complex rules, first state the goal plainly—“connections from the socks-special inbound whose target belongs to example.com use proxy-alt”—then map it to fields, so “and” and “or” are not reversed.
domainStrategy and domain matching
domainStrategy controls when routing resolves a domain for IP-based rules. Common approaches are to prefer domain matching and resolve to an IP only when necessary, or to try IP rules after domain rules fail. Supported values and details depend on the current core, but the principle is the same: resolution introduces a DNS dependency, so a more aggressive strategy requires a more reliable DNS path.
Common domain entries include exact matches, subdomain scopes, keywords and rule datasets. full:example.com matches only the complete domain; domain:example.com covers that domain and its subdomains; keyword:example is broader and more prone to false matches; geosite: references a domain category available to the core. Rule data must match the resources shipped with the current core. If a category is missing or a resource failed to load, do not treat it as a network failure.
IP, port and protocol rules
IP rules can contain individual addresses, CIDR ranges or geoip: data categories. Private networks should normally be sent direct explicitly, including loopback, private-network and link-local ranges. Ports can be specified individually or as ranges to constrain particular services, but a port is not a reliable application identifier; different services can use the same port. Protocol matching depends on connection features the core can identify and is best reserved for clear targets, not used as a replacement for domain and IP rules.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"full:intranet.example.com",
"domain:local.example"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "block"
},
{
"type": "field",
"inboundTag": [
"socks-special"
],
"outboundTag": "proxy"
}
]
}
}
Default exits and the final rule
Not every configuration needs a final rule covering every possible target. The outbound used when no rule matches depends on core defaults and outbound ordering. For easier auditing, keep the primary exit in a stable position and list exceptions explicitly. When rules are shared across a team or multiple devices, document the default path instead of relying on memory. After importing a new node, also check whether the outbound order changed.
When split routing fails, first check whether the target appears as a domain or an IP during routing. If the rule uses a domain but the log shows only an IP, inspect inbound identification and DNS handling. If the target domain is visible but does not match, check prefixes and rule order. If the log shows the correct tag but the connection still fails, move to that outbound. Distinguishing “no match” from “outbound failure after a match” prevents endless duplicate rules.
Where client custom rules take effect
Routing settings in v2rayN and split-routing settings in v2rayNG and v2flyNG are ultimately converted into rules the core can read. Clients may differ in how they merge rule sets, presets and custom entries. Before editing, export the current configuration or inspect the runtime configuration to see whether custom rules come before or after presets. If a client offers modes such as “bypass LAN,” “global” or “rules,” those modes control generation logic, not just a visual toggle.
Common patterns such as direct access for mainland China and proxying for destinations outside mainland China depend jointly on domain categories, IP data and rule order; copying one line is not enough. For the complete syntax and troubleshooting workflow, see V2Ray split-routing rules in practice. When migrating rules, also confirm that the target client’s core and resource files support the same category names.
05 / DNS
DNS configuration: server selection, domain rules and exit paths
What V2Ray DNS handles
The DNS module supplies results for domains the core needs to resolve and can choose different servers by domain category. It does not completely replace system DNS: an application may resolve a name at the system level and pass only an IP to the proxy, or pass the domain unchanged to a SOCKS or HTTP inbound. The server address itself may also require an initial lookup by the system or core. Before diagnosing DNS, determine who initiated the query, where the target domain is resolved and whether the result participates in routing.
A simple servers array can contain localhost or DNS server addresses. A more detailed object can specify the server address, applicable domains and expected query behavior. Server order should not be read simply as “always use the second one if the first fails”; actual behavior also depends on domain matching, query type and core implementation. When configuring multiple servers, assign clear domain responsibilities instead of stacking addresses and expecting an automatically better result.
Selecting DNS servers by domain
When a server object has domains, matching domains can prefer that server. Domain expressions resemble routing rules and can use exact domains, domain scopes and supported data categories. These rules answer “which server should be queried,” while routing rules answer “which exit carries the query traffic or final connection.” They are different layers. Adding a DNS domain condition without handling the corresponding exit does not guarantee that the query follows the intended path.
{
"dns": {
"hosts": {
"router.example": "192.168.1.1"
},
"servers": [
{
"address": "localhost",
"domains": [
"full:router.example",
"domain:local.example"
]
},
{
"address": "1.1.1.1",
"domains": [
"geosite:geolocation-!cn"
],
"skipFallback": true
},
"localhost"
],
"queryStrategy": "UseIP"
}
}
hosts provides static mappings for specific names and suits fixed local service names or test environments. It should not maintain a large, frequently changing address table. Static mappings bypass normal queries, and an outdated address can continue causing connection failures. When a domain resolves differently from the system result, remember to inspect hosts, including custom host records generated by the client.
Query strategy and address families
queryStrategy controls which address families queries prefer. Available values and details depend on the core, but they generally cover using IPv4 and IPv6 together, using only one, or choosing based on the environment. Do not permanently remove IPv6 capability because one IPv6 path failed. First confirm that the local network, remote outbound and destination form a complete path. If the system has an address but no usable route, the lookup can succeed while the connection times out; that is a network-path problem, not a missing DNS result.
When domain rules use IP categories, the core may resolve the target first and compare the result with IP rules. Query strategy can therefore indirectly change routing: the same domain may return different address families and match different network rules. Avoid making routing conclusions depend too heavily on one incidental lookup result. For private names that must remain direct, pairing clear domain rules with private-network ranges is easier to maintain.
How DNS queries choose an outbound
A DNS server address is itself a network target and can be controlled through a dedicated tag or routing rule. With a plain IP address, match the DNS target by IP and port. An encrypted DNS service specified by domain still needs an initial lookup for that server name. A common loop is: resolving the proxy server requires DNS, DNS is routed through the proxy outbound, and the proxy outbound must first resolve its own server address. Break the loop by keeping a usable non-proxy resolution path for startup or using the client's explicit bootstrap settings.
When DNS queries succeed but a web page does not open, continue to the final connection instead of stopping at the lookup result. Routing may send the resolved IP to the wrong outbound; the remote side may not support the selected address family; the destination port may be blocked; or the application may not use core DNS. Conversely, a page opening does not prove that every DNS query followed the configuration, because the application may resolve names independently. For a strict diagnosis, compare the target form, inbound protocol and application proxy mode in the logs.
Caching, FakeDNS and troubleshooting boundaries
Some client and core setups use caching or FakeDNS to preserve domain mappings for transparent access. FakeDNS returns an internal mapped address, and the core restores the real domain when it receives the connection. If that address is handled by another application, system component or incompatible inbound, the target IP may look abnormal. Before enabling it, confirm that the access method actually requires it and ensure its address range does not overlap the local network.
The most effective DNS troubleshooting method is to replace layers one at a time: verify basic resolution with one clearly working standard server, restore domain groups, then add query strategy and dedicated routing. Change one variable per step and record the target domain, returned address and final outbound tag. For a more systematic setup guide, see the client chapter on the V2Ray usage workflow.
06 / POLICY
policy: connection timeouts, user levels and statistics
The two layers of the policy object
policy centralizes connection-lifecycle and statistics behavior. Its common structure includes levels and system. levels uses user levels as keys and defines values such as handshake, connection-idle and post-upload or post-download wait times; system controls whether inbound and outbound statistics are enabled. Most personal clients do not need complex levels, but understanding the structure explains why an idle connection is released or why the statistics module produces no data.
A level number is not a speed priority and does not automatically grant a user more bandwidth. It maps the level referenced by a user or protocol object to a policy set. If the configuration contains only level 0, connections using the default level use that set of values. Adding a level that no user references has no practical effect. When migrating a server-style configuration to a client, a common mistake is to keep a complex level table while removing the user objects that referenced it, leaving confusing redundancy.
Connection-lifecycle parameters
handshake generally controls how long the connection-establishment phase may wait; connIdle controls how long an inactive connection remains open; uplinkOnly and downlinkOnly define how long one side waits after its data ends for the other side. Confirm units and supported ranges in the current core's field definitions. An idle timeout that is too short can repeatedly rebuild long-lived connections, push notifications or download-control connections; one that is too long keeps dead connections consuming resources.
A larger timeout is not automatically more stable. If the server address is unreachable, an oversized handshake timeout only delays feedback. When the network changes frequently, keeping old connections longer does not restore them. Before adjusting policy, use the logs to identify the disconnect reason: if the remote side closes the connection, extending the local idle timeout will not help; if the application reconnects periodically, do not attribute every reconnect to core policy.
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
}
A statistics switch is not a statistics result
Statistics fields in policy only allow the core to collect the relevant dimensions. They also require a top-level stats object and an interface or client feature that can read the data. Setting every switch to true does not guarantee that the UI will show anything. Conversely, a client that generates statistics configuration for traffic display may also add an API inbound or internal routing rules; do not keep policy while deleting its supporting objects.
Statistics add some state-maintenance overhead, so a personal setup should enable only the dimensions it needs. If you care only about total outbound traffic, there is no need to enable per-user upload and download statistics as well. During a configuration audit, ask three questions: which dimension must be observed, which interface reads it, and which client page displays it? If the answers are unclear, keep the configuration simple instead of adding modules merely to make every field appear complete.
Handling client-generated policy
v2rayN, v2rayNG and v2flyNG may write policy fields for traffic statistics, connection tests or local management features. Before overriding the configuration by hand, disable those client features or use their supported customization entry points; otherwise the next launch may regenerate the settings. On a desktop, especially when system proxying, connection tests and statistics display run together, the runtime configuration usually differs from a single node link in a subscription.
Keep policy troubleshooting separate from network troubleshooting. If the core fails to start and points clearly to a policy field, check its type, level keys and support in the current core. If a connection opens but closes too early, compare the idle timeout with application behavior. If statistics are empty, inspect the complete collection chain. Separating these cases identifies whether the problem is configuration structure, connection lifecycle or client display.
When to keep the defaults
Normal browsing, command-line proxying and everyday subscription use usually do not require manual policy changes. Defaults are designed for general cases. Unless logs and reproducible steps show that a connection is affected by a policy timeout, do not change it in pursuit of “more speed.” Policy controls waiting and state; it does not improve remote-line quality or server throughput.
If an adjustment is genuinely necessary, record the current value and reproduction conditions, change one parameter at a time, and compare results in the same application and network path. If the connection problem does not change, restore the default and investigate the outbound and system network. The purpose of a configuration reference is not to change every field, but to give necessary changes a clear basis.
07 / LOG & STATS
Logs, statistics and runtime configuration: creating an observable troubleshooting case
The log field and log levels
log determines which runtime information the core outputs. Common levels range from detailed debugging information to warnings and errors. For everyday use, a concise level such as warning is usually enough; temporarily increase detail when reproducing a complex issue, then restore it afterward. Keeping the most verbose logging enabled permanently creates repetitive output, makes important errors harder to find and may consume additional disk space.
Logs usually include access records and error records. Access records answer “which connection entered, what was the target and which exit was selected”; error records answer “at what stage did it fail and why.” Looking only at the last line is misleading because the outermost error may merely say “connection ended,” while the real cause appears earlier in DNS, routing or handshake entries. Keep the full interval from core startup through the failure and record the action that triggered it.
{
"log": {
"access": "",
"error": "",
"loglevel": "warning"
},
"stats": {}
}
How an empty path is handled depends on the core and the client's startup arguments; GUI clients usually take over log output and display it in the interface. Do not assume that entering a file path by hand means the client will read that file. Windows, macOS, Android and Linux differ in application data directories and permission models, so prefer the client's built-in log window or export feature. If you need to reinstall a client, choose the appropriate platform version from the package downloads.
Read logs in stage order
Start by checking configuration parsing. If the log reports a JSON character position, unknown field or type error, the core has not reached network connections, so changing the node address is pointless. After the configuration loads, check inbound listeners and confirm the address and port. Then inspect DNS and routing to verify that the target is identified and reaches the expected outbound. Only afterward examine the remote connection, TLS or protocol handshake. Reading logs along the data flow turns a long stream of messages into clear stages.
Common error messages should not be interpreted without context. A timeout may occur during DNS, TCP connection or handshake; connection refused may come from a local port, remote port or intermediary; failed to find an available destination may involve outbound selection, resolution or policy. Use the surrounding module names, target address and tags as evidence instead of searching for one English phrase in isolation.
Runtime configuration versus saved configuration
The client UI saves nodes, subscriptions, routing modes and application settings, while the configuration actually passed to the core may be assembled dynamically at startup. Troubleshooting should inspect the runtime configuration, not just node fields in the subscription link. The client may add local inbounds, API, statistics, DNS, direct and blocking outbounds; system proxy settings are also outside the core configuration. If the runtime configuration is correct but no application traffic enters, check the system or application proxy instead of continuing to edit JSON.
When copying a runtime configuration for testing, account for temporary ports, internal client tags and paths. Before starting the core independently, replace environment-specific dependencies with settings available on the current machine. Conversely, importing a hand-written configuration back into a client may cause it to be reorganized. Choose one primary source for long-term maintenance: either manage settings in the client and extend them through supported custom rules, or maintain a complete hand-written configuration and avoid two sources overwriting each other.
stats and client traffic displays
The top-level stats object enables the statistics module, but the actual metrics also depend on switches in policy. Some clients read inbound, outbound or user-level data through an internal API and display it in the UI. Removing the API object or internal routing can stop statistics while proxy connections continue working. Treat this case—“the core works but the UI is empty”—separately from a failed node.
Statistics are useful for observing traffic direction and whether traffic passed through an entry point, not for scoring line quality. Throughput depends on the destination server, network path, concurrency and application behavior. Do not enable every statistics dimension just to see more numbers. Define the troubleshooting goal first—for example, confirming that an inbound received upload traffic—then enable the relevant dimension and simplify the configuration afterward.
Create a minimal reproduction
A useful troubleshooting record should include at least the client name, operating-system platform, core family, reproduction steps, failure stage, relevant tags and a reduced configuration structure. Do not publish node credentials or subscription contents. Keep the protocol type, field nesting and example addresses so others can still assess structural issues.
If the core exits immediately after reading the configuration, see Finding config.json errors through startup logs for further diagnosis. If the error centers on certificate invalid, serverName or the handshake stage, use the TLS certificate error troubleshooting checklist and check system time, server name and certificate chain in order.
08 / VALIDATION
Assembling a complete configuration, validation order and troubleshooting branches
Assemble a readable baseline configuration
A complete configuration should make tag relationships clear before adding more rules. The baseline below contains a local SOCKS inbound, a proxy outbound, direct and blocking exits, simple DNS and routing. The proxy-server values are examples and cannot be used to connect; the point is to show how the modules reference one another. In practice, GUI clients usually fill proxy-outbound details from a subscription, while hand-written rules are best merged through a client-supported configuration entry point.
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": [
"localhost"
]
},
"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": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000000",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "server.example.com"
}
}
},
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:category-ads-all"],
"outboundTag": "block"
}
]
},
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300
}
}
}
}
Layer one: syntax and structure validation
When a configuration will not start, first check whether the JSON is complete. Look for paired braces, commas between array members, double-quoted strings, and numbers and booleans using the correct types. The line shown by an editor may only be where the parser finally stopped; the missing comma may be on the previous line. Read the configuration again after each fix until no JSON parsing errors remain.
After syntax passes, validate the field structure. Check field names, nesting and support in the current core. A configuration from another core family, an old guide or a different client may contain unsupported fields. Xray and V2Fly share a common foundation but differ in protocols and features, so configurations cannot always be exchanged unchanged. For how the two core families relate and which clients they pair with, read Xray and V2Fly core families: relationship and selection guide.
Layer two: tag and listener validation
List every inbound and outbound tag, then check each reference. Every outboundTag should resolve to an outbound with the same name, and every inboundTag should correspond to an actual entry point. Tag case must match exactly, and duplicate tags make the configuration ambiguous. Then confirm that each inbound starts listening, its port is free, and the application's proxy address and protocol type match exactly.
If an application cannot access anything but the core has no corresponding connection log, focus on system proxy and application settings. On desktops, the system proxy may affect only programs that honor system settings; terminal tools, games and standalone browsers may have their own proxy entry points. Android clients generally take over traffic through the system network interface, so check application permissions, the active configuration and connection state. Platform-specific entry points are listed on the package downloads page.
Layer three: DNS, routing and outbound validation
Once a connection enters the core, record how the target appears in the log. If it is a domain, check domain rules; if it is already an IP, check IP rules and inbound identification. Then confirm the outbound tag actually selected. A wrong tag means the rule order, match scope or DNS result needs adjustment. If the tag is correct but the connection fails, inspect that outbound's server resolution, port, protocol and transport layer.
You can temporarily reduce the routing rules to locate the boundary of the problem. Keep only private-network direct access and one explicit proxy exit to verify the basic path, then restore domain categories, blocking rules and inbound-specific rules group by group. Apply the same method to DNS: start with one working server, then restore domain groups and dedicated exits. Restore one group at a time so the change that introduced the failure is clear.
Common troubleshooting branches
| Symptom | Check first | Next step |
|---|---|---|
| Core exits immediately after startup | JSON syntax, unknown fields, port conflicts | Reduce to a minimal configuration and restore it section by section |
| Application connects but there are no access logs | Application proxy type, address, port and system proxy | Confirm that the request enters the correct inbound |
| Domain rules do not match | Whether the target became an IP, sniffing and rule order | Observe domain and IP routing at the same time |
| All nodes fail after a DNS change | Initial resolution of the server domain and circular dependencies | Restore basic DNS, then add rules one at a time |
| TLS or REALITY handshake fails | System time, serverName and transport parameters | Compare field nesting with the server configuration |
| The correct outbound still times out | Server address, port, network path and address family | Distinguish resolution, connection and handshake timeouts |
Regression checks after editing
Once the configuration works again, do not verify only one website. Test at least a domain target, a direct IP target, a local network address, a site that should go direct and a site that should use the proxy, confirming that the rule boundaries behave as intended. If UDP is enabled, test it with an application that genuinely needs UDP. Then restart the client and confirm that the configuration is regenerated and loaded reliably, rather than working only as a temporary runtime file.
After a subscription update, check that custom rules remain, the current node still maps to the intended proxy tag and the client has not switched cores. v2rayN suits Windows, macOS and Linux desktop environments; v2rayNG uses the Xray core; v2flyNG is an Android alternative using the V2Fly core. Their interfaces differ, but the troubleshooting path is the same: verify the entry point, identification, routing, resolution, exit and handshake layer by layer.
Keep a plain-text change log stating why each edit was made, which modules it touched and what verification showed. Configuration files can be copied, but network conditions and client-generation logic change; only by knowing what a rule solves can you decide whether it is still needed. For quick operations, see the usage guide; for field names and protocol concepts, see the glossary; for specific errors, open the relevant blog article instead of packing every detail into an unmaintainable configuration.
Continue reading
After understanding the structure, go to the package downloads, quick-start guide, glossary or log-troubleshooting articles that match your task. Blog posts focus on specific problems; this page remains the unified reference for configuration structure.