JSON structure overview and configuration loading order
The main V2Ray and Xray configuration can be viewed as a JSON object describing a traffic-processing pipeline: inbounds accept connections, routing determines where they go, outbounds establish connections, and DNS and policy objects add name resolution and resource controls.
The top-level object is not a list of execution steps
The outermost configuration is a single JSON object. Common keys include log, dns, inbounds, outbounds, routing, policy and stats. Their order in the file usually does not determine execution order: JSON objects describe structure by key names, not workflow by writing position. Order matters mainly inside arrays. For example, routing.rules is checked from top to bottom, and the first matching rule selects the outbound. Multiple inbounds and outbounds are referenced by their respective tag values.
When reading a configuration, a reliable approach is to list every tag first, then follow the references between them. An inbound tag may be used by a routing rule's inboundTag; an outbound tag may be selected through outboundTag; and a DNS server may connect to routing rules through its tag. A tag is only an internal configuration identifier and does not create routing behavior by itself. If a rule names a tag that does not exist, startup may fail, or traffic may have nowhere to go when that path is reached. The exact behavior depends on the core and the field involved.
{
"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": "blocked",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
Arrays, objects and value types
inbounds, outbounds and routing.rules are arrays, so they use square brackets. A single inbound, outbound or rule is an object and uses curly braces. Ports are numbers, not strings with units. Boolean values must be true or false. An omitted field is not equivalent to a field set to null: omission lets the core apply its default behavior, while an explicit null value matters only where the field definition allows it. Many startup failures are caused not by protocol problems but by mismatched brackets, missing commas between objects, Chinese quotation marks around strings, or an array field written as a single string.
Standard JSON does not allow comments or a trailing comma after the last member. Online examples often use // comments for explanation, but remove them before copying a core configuration. v2rayN, v2rayNG and v2flyNG may maintain their own configuration layer outside the graphical interface; nodes, subscriptions and routing settings are converted before being passed to the core. Before editing by hand, confirm whether you are changing a client-managed file, a generated runtime configuration or a configuration read directly by a standalone core. Similar filenames do not make these the same object.
Increase complexity gradually from a minimal configuration
When troubleshooting, keep one local inbound, one clearly working outbound and the smallest possible routing set, then add DNS, sniffing, multiple transports and statistics policies step by step. Adding several modules at once can hide the source of an error. A failed connection might be caused by remote parameters, a domain resolved through the wrong DNS path, or an earlier rule sending traffic to blocked. Minimizing the configuration does not mean deleting required security fields; it means shortening the chain of variables. Keep the server-required address, port, user ID, transport and security parameters unchanged, and temporarily remove only optional rules that do not affect connectivity.
Before saving, confirm that the text is UTF-8 encoded, the file contains one root object, and key casing matches the field definitions. outboundTag and outboundtag are different names, and protocol names should not be inferred from translated interface labels. After checking the structure, validate the inbound, outbound and routing sections one at a time. This is easier to interpret than repeatedly changing ports inside a large configuration.
inbounds, listening scope and protocol settings
An inbound defines where the core receives connections, which protocol it uses to interpret them, and whether it should sniff the destination address. Desktop clients commonly create local SOCKS and HTTP inbounds, while mobile clients may use a VPN service to send traffic into the core.
listen, port and local exposure
Each inbound needs an explicit protocol and its settings. Common fields include tag, listen, port, protocol, settings, sniffing and streamSettings. When listen is 127.0.0.1, only programs on the same device can connect to the port, making it a suitable entry point for a browser, system proxy or local tool. If the inbound listens on every network interface, other devices on the same LAN may also attempt to connect, so consider the OS firewall, authentication and actual sharing requirements. Without a clear reason to share on the LAN, a loopback address is usually the cleaner boundary.
port must not already be used by another program. The same address-and-port combination cannot be listened to by two running inbounds. Graphical clients often assign adjacent ports to SOCKS, HTTP or mixed entry points, but these are not protocol-defined values. Copying a tutorial's port to another device may conflict with a development server, an old client process or another network tool. When an error such as “address already in use” appears, close the duplicate instance or change the listening port in the client settings, then update the system proxy target.
{
"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": {}
}
]
}
The boundary between SOCKS, HTTP and transparent entry points
A SOCKS inbound carries proxy connections initiated by applications. With udp enabled, it can also accept requests that follow the SOCKS UDP forwarding flow; this does not mean arbitrary system UDP traffic will automatically enter that port. An HTTP inbound mainly handles applications that support HTTP proxy settings. A system proxy usually affects only software that reads the system setting, not every connection on the device. Transparent proxying or virtual-network-interface access requires cooperation between OS network rules and the client, with more complex fields and permissions. Do not simply replace the protocol name of a normal SOCKS inbound and expect it to work.
In v2rayN, the interface creates the local entry point and controls the Windows, macOS or Linux system proxy state. In v2rayNG and v2flyNG, the Android VPN service sends traffic from selected applications into the core. The same node outbound parameters can therefore move between clients, while the inbound section is often tied to platform integration. When migrating, keep the server parameters and let the target client generate its own local entry point instead of overwriting its entire runtime configuration. Check platform availability and client roles on the Client Downloads page.
What sniffing does and where it can misjudge
Sniffing attempts to recover a domain name from early connection data so domain-based routing can handle requests that initially contain only an IP destination. The common http and tls values in destOverride correspond to recognizable HTTP host information and TLS handshake names. Sniffing does not decrypt application content and cannot recover a domain from every connection. Changes in encrypted handshakes, non-standard protocols, connection reuse or applications that use an IP directly may provide no usable domain.
If enabling sniffing rewrites an application's destination and causes failures, place that application's traffic in a separate inbound or limit the override types, then compare logs with sniffing enabled and disabled. Do not treat sniffing as a replacement for DNS: DNS determines how a domain is resolved, while sniffing attempts to identify the original domain after the connection arrives. They operate at different stages. If a routing rule contains both domain and IP conditions, also consider domainStrategy to determine whether the core will resolve a domain for routing.
| Field | Common values | What to check |
|---|---|---|
listen |
127.0.0.1 |
Whether other devices really need access |
port |
A valid numeric port | Whether it conflicts with another program or inbound |
protocol |
socks、http |
Whether settings belongs to the selected protocol |
tag |
A unique custom name | Whether routing references match exactly |
outbounds, node parameters and chaining
An outbound sends a connection selected by routing to its destination. It may connect to a remote protocol server, access the destination directly, reject the connection, or hand the traffic to another outbound for further processing.
Check the protocol fields as one complete set
A remote outbound is usually defined jointly by protocol, settings and streamSettings. For VLESS, for example, the server address and port are under vnext, while the user ID is under users. The transport, security layer, server name and related parameters are in streamSettings. Matching only the address, port and user ID does not prove that the configuration is consistent. The client and server must also agree on transport, TLS-related security settings, path, hostname and applicable extensions.
Configuration interfaces often spread these fields across pages such as “Address,” “User,” “Transport” and “Security,” while JSON places them in neighboring objects. When subscription import behaves unexpectedly, inspect each field on the client's details page instead of relying on the displayed node name. A node name is usually a local note and is not part of the protocol handshake; structured fields determine the connection. When migrating parameters manually, do not treat URI-encoded text as its raw JSON value, and do not turn an empty interface value meaning “default” into the string "default".
{
"outbounds": [
{
"tag": "remote-vless",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "11111111-1111-4111-8111-111111111111",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "server.example.com"
},
"wsSettings": {
"path": "/example-path",
"headers": {
"Host": "server.example.com"
}
}
}
},
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "blocked",
"protocol": "blackhole"
}
]
}
direct and blocked are explicit handling paths
A freedom outbound is normally used to connect directly to the destination and is often tagged direct. A blackhole outbound terminates connections selected by a rule and is often tagged blocked. These tags are conventional names and can be changed, but every routing reference must be updated. Adding freedom to the outbound array does not automatically make local addresses direct; a routing rule must send the relevant traffic to it. Likewise, defining blackhole does not block any domain until a matching rule references it.
When no rule matches, the core usually selects the default outbound, although the exact behavior depends on the configuration and core implementation. To keep large configurations understandable, write explicit rules for important traffic and use readable outbound tags. Names for remote, direct and blocked outbounds should describe their purpose instead of using confusing consecutive numbers. Clear names also make outbound identifiers easier to match in logs.
Outbound chains, proxy settings and loops
Some cores allow an outbound to establish its connection through another outbound using proxySettings or a similar mechanism. This is useful for deliberately designed chained exits, but it adds layers to DNS, handshakes and fault diagnosis. Every segment needs a unique tag, and the chain must not form a loop such as A pointing to B while B points back to A. Consider how the remote server's domain is resolved as well: if the DNS lookup needed for the first connection depends on an outbound that has not yet been established, the configuration can enter a persistent timeout loop.
When troubleshooting a chained configuration, first make the single outbound responsible for the final physical connection work independently, then add earlier segments one at a time. If the log only says “connection closed,” use the outbound tag to identify the failing layer. Remote rejection, DNS failure, a mismatched TLS server name and a local route selecting the wrong outbound can all appear as an application unable to open a page. Layer-by-layer testing preserves more evidence than replacing every protocol parameter at once.
On desktop, use v2rayN to manage nodes and core configuration. On Android, choose v2rayNG or v2flyNG according to the core you need. To compare the roles of Xray and V2Fly, read Xray and V2Fly Core Differences. Supported fields may vary between core branches, so confirm which core the target client actually uses before migrating a complex configuration.
routing rules, matching conditions and priority
The routing object does not create connections. It receives the destination information identified by an inbound and selects an outbound from the rule array. Understanding “top to bottom, first match wins” is central to maintaining split-routing configurations.
How the rule array is matched
The common rule type in routing.rules is field. It can filter connections by domain, ip, port, network, protocol, inboundTag and user, then select a destination with outboundTag. When one rule contains conditions from multiple categories, all of them generally must match; multiple values in an array for the same field generally mean that any one value may match. Do not inspect only a domain list. Check whether the same rule also restricts the port or inbound tag.
Rules are checked from the first array item onward. Put specific exceptions before broad rules. For example, a private test domain that must use a remote outbound should come before a larger direct-domain set. If the broad rule matches first, the exception never runs. You can add a final catch-all rule or rely on the default outbound, but an explicit final rule is usually easier to review. Move only one group at a time and record the matching tag before and after the change, so “the rule did not match” is not confused with “the outbound is unavailable.”
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "hybrid",
"rules": [
{
"type": "field",
"domain": [
"full:intranet.example",
"domain:local.example"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"protocol": [
"bittorrent"
],
"outboundTag": "blocked"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "remote-vless"
}
]
}
}
Domain syntax and IP conditions
full: matches a complete domain name and suits a single known hostname. domain: generally matches a domain and its subdomains. regexp: enables regular expressions, but complex expressions increase maintenance costs and make mistakes with dots, boundaries and escaping more likely. Interpret unprefixed entries according to the core's definition. Rule-set identifiers such as geosite: and geoip: depend on rule data available to the core; the existence of a name does not guarantee that the local data contains the corresponding entries.
IP rules can contain individual addresses or CIDR ranges. geoip:private is commonly used to keep private address ranges, LAN devices and local services away from remote outbounds. If a domain resolves to a private address, whether it reaches the IP rule also depends on domainStrategy. With AsIs, routing does not actively resolve a domain for IP matching. With IPIfNonMatch, a domain-rule miss may trigger resolution followed by IP matching; more aggressive strategies may resolve earlier. The more aggressive the strategy, the tighter the coupling between routing and DNS.
Split routing by inbound, network and protocol
inboundTag is useful for sending different local entry points to different outbounds. For example, a test entry can always use one remote outbound while the normal system proxy continues to follow domain rules. It also isolates temporary experiments: add a separate port and tag without changing the main entry point. network distinguishes TCP from UDP, but whether the remote protocol and transport fully carry that network must be checked separately. protocol conditions depend on what the core identifies and are often related to inbound sniffing; do not assume every connection can be classified reliably.
The routing result is also affected by client interface modes such as “Global,” “Rules” or “Direct.” A mode may switch an entire routing template or change the default outbound, rather than simply editing one rule. For the scope of three common modes, see System Proxy, Global Mode and Mainland-China Bypass Mode Explained. When debugging custom rules, keep the client mode fixed; switching modes may regenerate the configuration and make manual edits appear ineffective.
| Condition | Best used for | Common pitfall |
|---|---|---|
domain |
Exact domains, suffix ranges and rule sets | Ignoring prefix semantics or rule order |
ip |
Individual addresses, CIDR ranges and IP rule sets | Not considering whether the domain is resolved during routing |
inboundTag |
Isolating a path by local entry point | Referencing a missing or differently spelled tag |
network |
Distinguishing TCP from UDP | Treating network type as an application protocol category |
dns configuration, server selection and routing
DNS configuration determines which servers the core queries when it needs to resolve a domain and which resolution path should take priority for specific domains. It is separate from OS DNS, application-level resolution and resolution performed during routing.
First identify who initiates the query
An application may resolve a name through the operating system and pass only the destination IP to the proxy. It may instead pass the original domain to a SOCKS or HTTP inbound, or use its own encrypted DNS mechanism. Only queries that enter and are resolved by the core are directly controlled by the top-level dns object. If a configuration change has no visible effect, first confirm whether the inbound receives a domain or an IP, and whether sniffing recovered the domain. Changing dns.servers cannot force every system program to abandon its own resolution path.
servers may contain simple addresses or objects with properties such as address, domains, expectIPs and skipFallback. A simple array suits one path; object form suits server selection by domain. A server list does not always behave like the traditional fallback model where the second server is used only after the first fails. Domain filters, fallback settings and the core implementation jointly determine which server is queried. Before designing complex DNS, validate two clearly assigned servers first, then add domain scopes and fallback restrictions.
{
"dns": {
"hosts": {
"router.example": "192.168.1.1"
},
"servers": [
{
"address": "localhost",
"domains": [
"full:router.example",
"domain:internal.example"
],
"skipFallback": true
},
{
"address": "1.1.1.1",
"domains": [
"domain:public.example"
]
},
"8.8.8.8"
],
"queryStrategy": "UseIP"
}
}
hosts, domains and expected results
hosts provides static mappings for explicit names and is suitable for a small number of stable local services, not a large dynamic domain table. Static mappings usually have high priority; if an address changes and the mapping is not updated, one name may continue pointing to the old address. Check the operating system hosts file, host overrides in the client interface and dns.hosts in the core configuration together to avoid conflicts between layers.
domains in a server object filters the domains sent to that server. Its syntax resembles routing domain conditions, but the purpose differs: the former selects a resolver, while the latter selects a connection outbound. expectIPs can constrain the range of expected returned addresses to test whether a result matches expectations; it does not rewrite arbitrary responses into that range. A constraint that is too narrow may exclude valid responses and trigger fallback. skipFallback affects how the server participates in fallback. Before combining these options, map the four steps: domain selection, server query, result check and connection routing.
How DNS queries select an outbound
The DNS server address itself also requires a connection. If it is a domain, that domain must first be resolved; if it is an IP, that layer is skipped, but routing still selects the network outbound. More complex configurations assign a tag to DNS traffic, then use routing to send it to a specific outbound by tag or protocol. Avoid loops: a remote outbound may depend on domain resolution while that resolution is required to use the remote outbound that has not yet been established.
To build an understandable path, first give the remote server address a stable base resolution path, then decide how ordinary destination domains should be routed. If domainStrategy and DNS rules are used together, routing may trigger DNS resolution while evaluating IP conditions. A problem that looks like routing may actually come from DNS server selection or fallback results. In logs, follow the sequence of query, returned address, outbound selection and connection target rather than looking only at the final timeout.
Caching and IPv4/IPv6 selection
queryStrategy limits or favors returned address families, with supported values varying by core. Forcing one address family may work around incomplete local connectivity, but it may also exclude a reachable destination. Check whether the operating system has connectivity for the family before restricting queries. The client, core and system resolver may all cache results, so retrying immediately after a DNS change may not issue a new query. You can restart the relevant client process and establish a new connection, but clearing caches should not be treated as a long-term configuration strategy.
For “the domain is unreachable but its IP works,” first verify that the query returns an address, then check whether the returned address is sent to the intended outbound by routing. For intermittent failures affecting some domains, compare the DNS server, address family and outbound tag selected during successful and failed attempts. Changing servers tests the resolver path but does not replace checking rule conditions. For the main client connection workflow and verification steps, return to Getting Started.
policy, connection timeouts and statistics switches
policy does not choose which node handles traffic. It sets connection timeouts, idle periods and statistics switches for user levels and system behavior. Although it sits outside routing selection, it affects long-lived connections and observability.
How level and levels are referenced
policy.levels is an object keyed by user level. The level number in a protocol user's configuration references the corresponding policy; without an explicit value, the default level is generally used. A level is not a speed rating or an automatically ordered permission tier. It simply maps a group of user connections to a group of policy parameters. Changing the level from 0 to 1 does not automatically improve performance; it matters only when levels["1"] is defined with different fields.
When the client initiates local connections, most users need only the default level. Multiple levels are more common in server configurations that distinguish different inbound users. Even then, base the design on an actual management need instead of creating nearly identical policies for every user. More policies mean more references to miss during migration. Search for every level, confirm that the corresponding key exists in policy.levels, and remember that JSON object keys are written as strings.
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true,
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"stats": {}
}
Handshake, idle and one-way connection timeouts
handshake controls how long the initial connection setup may wait. If it is too short, a busy device, slow network or multi-stage handshake may be closed before completion; if too long, failed connections consume resources longer. connIdle determines how long a connection remains open without activity. Messaging, push services, remote terminals and streaming may have different idle patterns, so do not choose every long-connection value based only on web browsing.
uplinkOnly and downlinkOnly govern the lifetime of a connection after only one direction remains active. They are not upload or download speed limits and do not allocate bandwidth to either direction. If an application is disconnected during one-way transfer, inspect these values. If it disconnects after both directions have been idle, connIdle is a more likely factor. Remote servers, intermediary network devices and the application itself may also close connections, so local policy is only one part of the investigation.
Statistics require a complete enablement chain
statsUserUplink and statsUserDownlink control per-user statistics, while fields under policy.system control inbound and outbound statistics. Setting a Boolean to true does not necessarily make data appear in the client UI. A top-level stats object, the relevant API or client reading logic may also be required. Conversely, the absence of a UI display does not prove that the core is not counting. Distinguish between a counter that is disabled, a counter that exists but is not being read, and a reading interface that does not match the client's display.
Statistics add state maintenance, so enable them according to diagnostic and management needs. A personal desktop configuration can stay simple if it does not consume statistics. To determine whether an inbound or outbound is carrying traffic, temporarily enable system-level counters and verify the tags against the logs. Do not use statistics as the sole measure of connection quality: they show data volume through a processor, not handshake latency, application response or DNS selection.
How to validate policy changes
After changing connection timeouts, test a scenario that reliably reproduces the problem. For example, leave a long-lived connection idle beyond the original threshold, then send data and observe whether it is rebuilt. Do not infer idle behavior from rapidly refreshing a web page. If only one protocol or application is affected, first confirm that its connection actually maps to the user level you changed. A client that generates configuration may overwrite manual policy settings, so verify the final runtime value in the active configuration or logs.
policy is suited to clear lifecycle and statistics requirements, not to fixing incorrect routing, transport or remote parameters. If a connection cannot be established at all, return to the outbound and transport layers. If only certain domains fail, check routing and DNS. If the connection is established but drops after a consistent idle period, inspect policy. Following this order prevents relaxed timeouts from hiding a real handshake failure.
streamSettings, transport methods and security parameters
The remote protocol defines identity and request format, while streamSettings defines how that data is carried over the network connection. Even with matching protocol parameters, the connection cannot complete if the transport layer differs.
network determines the settings object that follows
streamSettings.network specifies the transport type. Common configurations may use TCP, WebSocket, gRPC or mKCP. After choosing a network, use its corresponding settings object, such as wsSettings for WebSocket or the service-name settings for gRPC. Keeping fields from another transport does not combine the two automatically and can make the configuration misleading. When migrating a node, confirm the transport type shown by the client, then check its dedicated fields one by one.
The transport type is not an option the client can optimize independently. The client must connect using the same transport, path or service name that the server is listening for. Repeatedly switching among TCP, WebSocket and gRPC usually only adds variables. A safer approach is to fix the transport from the source configuration, verify that the address resolves and the port is reachable, then check the path, host fields and security layer.
{
"streamSettings": {
"network": "grpc",
"security": "tls",
"tlsSettings": {
"serverName": "edge.example.com",
"allowInsecure": false,
"alpn": ["h2"]
},
"grpcSettings": {
"serviceName": "example-service",
"multiMode": false
},
"sockopt": {
"tcpKeepAliveIdle": 100
}
}
}
WebSocket path, Host and TLS name
In a WebSocket configuration, path is the HTTP handshake path. Whether it begins with a slash or includes a query string must match the server entry point. headers.Host is a WebSocket handshake header, while tlsSettings.serverName is used for TLS server-name verification. They may be the same or may differ because of the deployment design. Treating them as one field and replacing them together can make one layer pass while the other fails.
The server address determines the first host contacted, the TLS name determines the name used for certificate and handshake verification, and the WebSocket Host is handled at the HTTP layer. Troubleshoot them in connection order. If the address accepts a TCP connection but TLS fails, check the system clock, server name and security parameters. If TLS succeeds but WebSocket is rejected, check the path and Host. Loosening certificate verification merely reduces error visibility and should not be a routine fix.
gRPC, mKCP and transport-specific fields
A gRPC configuration usually requires an exact serviceName and depends on HTTP/2 negotiation. The service name is a configuration value, not a node note, and should not be turned into a URL-style path by adding slashes. If an intermediary does not support the required connection method, it may close the connection immediately after the handshake. mKCP is a UDP-based transport and depends on whether the current network can carry UDP reliably; TCP reachability does not prove that the UDP path works.
When there are many transport-specific parameters, start with the structure generated by the client from the node information, then modify only fields tied to a clear requirement. Internet examples may span different cores and releases and include renamed fields. v2rayN generally manages a desktop core, v2rayNG follows an Xray core path, and v2flyNG targets the V2Fly core. The basic concepts of a transport may be similar, but extension fields are not necessarily identical. If support is uncertain, remove nonessential extensions first and retain the core fields required by the server.
security, TLS and Reality layers
security selects the security layer, and its settings object must match that choice. TLS parameters belong in tlsSettings, while Reality uses the separate settings object defined by the core. You cannot switch only the security string and keep every old field. The server name, public-key parameters, short ID and fingerprint selection are handshake requirements for a specific security method and must come from the same valid configuration.
Security-layer errors and user-identity errors can both appear as a failed connection test in the interface, so logs must identify whether the failure occurred during DNS, TCP, TLS or protocol authentication. If the server port cannot be reached, changing the server name is pointless. If TCP is established but the security handshake fails, do not start by changing routing rules. Identify the failing layer before changing parameters to avoid altering the error message without fixing the cause.
| Layer | Representative fields | What must match the remote side |
|---|---|---|
| Target connection | address、port |
Host and listening port |
| Transport | network |
Transport type, path or service name |
| Security layer | security |
Server name and corresponding handshake parameters |
| Application protocol | protocol、settings |
User identity and protocol attributes |
Configuration validation and troubleshooting by layer
Effective troubleshooting is not a cycle of randomly replacing parameters. It confirms which layer the connection reaches and where it stops. Check structure, listening, resolution, routing, outbound handling and application settings in a fixed order.
Layer 1: confirm that JSON can be read
When startup fails, first find the first structural error in the client or core log and record its line number and field path. Common issues include missing commas, mismatched brackets, unterminated strings, fields placed in the wrong object, numbers written as strings and comments in standard JSON. An editor may point to the line after the real error because the parser does not know the previous item is incomplete until it reads later characters. Check the end of the object before the reported line as well.
A structurally valid file can still contain invalid field semantics. Correctly formed JSON may include fields the core does not recognize, incorrect protocol settings or nonexistent tags. Use the object path in the log to return to the relevant section and confirm whether the field belongs to an inbound, outbound or transport setting. If the client rewrites the configuration at every startup, you are editing a generated artifact; change the source settings in the client interface or its custom configuration entry point instead. See Getting Started for the complete first-use workflow.
{
"log": {
"access": "",
"error": "",
"loglevel": "warning"
}
}
Layer 2: confirm that traffic enters the right inbound
After the configuration loads, check that the local listening address and port appear. The application's proxy address must match the inbound, and SOCKS and HTTP types cannot be mixed. If the system proxy is enabled but the application still connects directly, confirm that the application reads system settings. Conversely, if the client has disabled the system proxy but a browser extension retains its own proxy, traffic may still enter the old port. During troubleshooting, keep only one entry method active to reduce duplicate proxies and unclear paths.
If the port is not listening, check whether it is occupied, whether the client core has started and whether another process is reading the configuration. If the port exists but the log shows no access at all, the problem is usually between the application and the inbound. If the log records a destination, the entry point is working and you can continue to routing and outbounds. On mobile, also check the VPN service state and per-app proxy scope; excluded applications do not pass through the v2rayNG or v2flyNG core path.
Layer 3: trace DNS, routing and outbound tags
Send a request to one fixed test domain and record, in order, whether the core receives a domain, triggers resolution, matches a rule and selects an outbound. If only domains fail while IPs work, focus on DNS and domain rules. If every destination reaches the wrong outbound, inspect broad rules and the default path. If one application fails, compare its inbound tag, network type and sniffing result. Do not use several changing test websites, or caching, address families and domain-rule differences will add variables.
If routing selects the correct outbound but the remote connection fails, check address resolution, port reachability, protocol user parameters, transport type and security layer. Increase the log level moderately during troubleshooting, then restore a level suitable for daily use so excessive output does not hide key events. Logs may contain destination domains, internal addresses and local paths; remove unrelated personal configuration data before sharing excerpts.
Layer 4: distinguish client state from core state
v2rayN, v2rayNG and v2flyNG all include a client management layer, while the core is only the component that processes connections. A successful subscription update means the client fetched and parsed the subscription; it does not mean every node can connect. A failed latency test does not necessarily mean all application connections fail, because the test method, target address and actual application protocol may differ. Evaluate subscription updates, node selection, core startup, system or VPN entry-point status, and whether the real request passes separately.
The Avalonia desktop edition of v2rayN supports Windows, macOS and Linux, while the WPF edition is for Windows only. Their interfaces and system integration differ. For choosing a desktop edition, see v2rayN Desktop and WPF Editions Compared. For Linux installation and login-session autostart, see How to Install v2rayN on Linux. Platform differences mainly affect the client layer and should not be used as a reason to change node protocol parameters.
Keep a change log that can be rolled back
Change one logical layer per round and save a working copy before each change. Name local records in an order such as “structure, inbound, DNS, routing, outbound, transport, policy,” and note the expected result alongside the actual log. If one change modifies several fields, even a restored connection will not reveal which field mattered, making future migration harder. Restore an entire version from the same round instead of mixing old routes with new tags.
For subscription-managed nodes, make changes through the client's provided editing entry point and understand whether subscription updates overwrite local changes. Long-term routing and DNS rules should go into a custom configuration layer supported by the client instead of repeatedly editing temporary runtime files. As the configuration grows, remove unused tags, duplicate rules and expired experimental inbounds so every path in the logs maps to a current purpose.
From the reference configuration back to client operation
Once the minimal configuration works, restore domain rules, DNS routing, multiple outbounds and policy settings in sequence, repeating the same fixed test after each group. If the problem appears only after restoring one group, the search is narrowed to that group and its referenced objects. When reinstalling or switching clients, use the Client Downloads page to choose the appropriate Windows, macOS, Android or Linux entry. Its FAQ section also covers package selection, processor architectures and version changes.
A configuration file is a set of interdependent declarations, not an isolated collection of parameters. Inbound tags affect routing conditions, routing policies may trigger DNS, DNS queries require an outbound, and the transport security layer ultimately determines whether the remote connection can be established. Reading along the dependency chain turns “unable to connect” into a verifiable structure, entry-point, resolution, rule or handshake problem. After one documented, layered investigation, the same method can be reused for future node migrations and client upgrades instead of starting over with random changes.