Configuration Reference · Systematic Guide

V2Ray Configuration Files: Complete Reference

Starting with the top-level JSON structure, this guide explains how inbounds, outbounds, routing, dns, and policy interact, along with key parameters and troubleshooting boundaries.

When configuring a client for the first time, follow the Getting Started guide to import a subscription, choose a node, and verify the connection. Use this page to understand the configuration generated by the client, review advanced parameters, and locate the relevant configuration section when the logs report an error.

On this page

01

Top-Level JSON Structure and Configuration Read Order

Confirm data types, tag references, and the processing chain before examining individual protocol parameters.

The top-level object is not an execution checklist

A V2Ray configuration file uses a single JSON object as its root. Common top-level fields include log, dns, inbounds, outbounds, routing, policy, and stats. The order of these fields in the file does not change processing order. When loading the configuration, the core parses the entire object first, then creates listeners, outbound objects, the router, and the name resolver. Putting routing before inbounds does not make routing run first; tags and rule references determine the actual relationships.

Both inbounds and outbounds are arrays because one process can listen on multiple local ports and retain multiple exits. Each object in an array usually has a tag. Routing rules refer to these tags through inboundTag, outboundTag, or balancerTag. Tags should be unique within the same configuration and use short, stable names such as socks-in, proxy, direct, and block. After changing a tag, check every reference as well; the configuration may parse successfully but later report that a routing target does not exist.

A readable minimal structure

{
  "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"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      }
    ]
  }
}

This example creates only a local SOCKS entry and two basic outbounds; it does not configure a remote proxy connection. Use it to study the hierarchy: protocol-specific parameters belong in the relevant object's settings, transport parameters usually belong in streamSettings, and connection reuse and other extensions have their own child objects. Do not move fields from another level into settings. Even valid JSON may cause the core to ignore fields that do not belong to the object or report a deserialization error.

Objects, arrays, and data types

Configuration errors are often caused by mismatched JSON data types rather than protocol problems. Ports are usually numbers, so write 10808 rather than the string "10808"; boolean switches use true or false, not quoted text. Domain and IP conditions in rules are usually arrays, so keep the square brackets even when there is only one item. Object properties need commas, but the final property must not have a trailing comma. Full-width quotation marks, invisible spaces, and curly quotes copied from rich text can also make the parser fail at a seemingly valid position.

When generating a configuration, clients often combine subscription nodes, routing presets, and local ports into the final file. v2rayN is useful for viewing and adjusting desktop configurations on Windows, macOS, and Linux; on Android, v2rayNG and v2flyNG usually save settings through their interfaces and generate a runtime configuration at startup. Interface labels do not always match the underlying fields word for word. For example, “bypass LAN” may become a geoip:private direct rule. When verification is needed, rely on the configuration actually exported by the client and its logs rather than treating a screenshot as the complete configuration.

A maintainable way to read configurations

When reading a long configuration, list every inbound and outbound tag first, trace each routing rule to its target tag, and then check DNS and transport parameters. Do not start with a deeply nested field and try to infer the entire connection path. A request first enters an inbound with its destination domain, IP, port, and possible protocol-detection results; the router selects an outbound based on the conditions; the outbound then connects to the target according to its protocol and transport settings. DNS may participate during rule matching or outbound connection setup, while policy affects connection statistics, idle timeouts, and session behavior. Following this chain separates “the configuration loads” from “the connection completes.”

02

inbounds: Local listeners and traffic entry points

An inbound determines which applications can connect, which local protocol they use, and what identifying information enters the router with each request.

listen, port, and exposure scope

The first fields to check in an inbound object are listen and port. When a desktop client provides a proxy for local applications, it usually listens on 127.0.0.1, meaning only the current device can connect. Changing the listen address to an external network interface expands the reachable scope and requires consideration of the local network boundary, system firewall, and authentication. If the proxy is only for a browser, terminal, or local application, there is no need to widen the listen scope.

The port must not already be occupied by another process. Two inbounds in the same configuration also cannot listen on the same port at the same address. Common clients create separate SOCKS and HTTP inbounds, or use an implementation with a mixed entry point. System proxy settings generally use an HTTP entry, while applications that explicitly support SOCKS can use the SOCKS port directly. If an application sends HTTP requests to a SOCKS port, logs may show an unrecognized handshake, a closed connection, or an invalid request format. This is not a remote-node failure; the local inbound protocol is wrong.

SOCKS inbounds and UDP

{
  "tag": "socks-in",
  "listen": "127.0.0.1",
  "port": 10808,
  "protocol": "socks",
  "settings": {
    "auth": "noauth",
    "udp": true,
    "ip": "127.0.0.1"
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"],
    "routeOnly": true
  }
}

auth controls authentication for a SOCKS entry; when listening only on the loopback address, noauth is commonly used. udp determines whether SOCKS UDP requests are accepted. Enabling it does not mean every application will automatically send UDP through the proxy. The application must support SOCKS UDP, and the remote protocol and transport chain must also handle the traffic. When ip is used for the return address in a UDP association, it should match the actual reachability requirements. A loopback address is intuitive for a local entry; if the entry serves other devices, verify that the address is reachable by the requesting device.

HTTP inbounds and system proxy settings

{
  "tag": "http-in",
  "listen": "127.0.0.1",
  "port": 10809,
  "protocol": "http",
  "settings": {
    "allowTransparent": false
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"],
    "routeOnly": true
  }
}

An HTTP inbound handles standard proxy requests and CONNECT tunnels, making it suitable for browsers and software that follows system proxy settings. It is not the same as the HTTP service used by a website server: the application must know explicitly that this is a proxy port. Terminal commands, development tools, and background services may not read the desktop system proxy, so a working browser does not prove that every program uses this inbound. If “web pages work but the command line connects directly,” check the target program's proxy options or environment variables before examining the V2Ray configuration.

The role and limits of sniffing

Traffic sniffing recovers the destination domain from some connections, allowing domain-based routing rules to match. With a TLS connection, for example, an application may resolve a domain locally before connecting to an IP address; if the inbound sees only the IP, a domain-only rule may not match. With sniffing enabled, the core can identify the destination domain from handshake information. destOverride specifies the protocol types that may be identified, while routeOnly means the result is primarily used for routing decisions rather than forcibly replacing the final connection target.

Sniffing is not general-purpose decryption, and it cannot guarantee that every connection exposes an identifiable domain. Non-standard protocols, changing encrypted handshakes, application-level encapsulation, or direct IP access may leave only an IP condition. Routing should account for both domain and IP rules rather than relying entirely on sniffing results. If an application behaves abnormally with sniffing enabled, disable it for that inbound or split the traffic into separate inbound tags so routing can handle each entry independently.

How multiple inbounds work together

The most useful purpose of multiple inbounds is separating traffic sources. For example, keep http-in for a browser and socks-in for development tools, then use inboundTag to apply different routes. This is more reliable than guessing an application's source from its domain. Each entry should have a clear tag, and its port should remain consistent across the client interface, system proxy, and application settings. After changing v2rayN's local port, the old system proxy value or terminal environment variable will not necessarily update automatically; check both the listener logs and the application's configuration.

Entry type Common use Priority checks
SOCKS Browsers, terminals, and development tools that support SOCKS Protocol type, port, UDP support
HTTP System proxy and software that supports HTTP proxies CONNECT support, system proxy port
Dedicated entry Apply different routes by application category Tag uniqueness and inboundTag rules
03

outbounds: Protocols, servers, and the transport layer

An outbound describes where a request leaves the device and which protocol, identity parameters, and transport method are used to connect to the remote endpoint.

Outbound tags and three basic destinations

A practical configuration usually includes at least three outbounds: proxy, direct, and block. The proxy outbound connects to a remote server from a subscription or manual configuration; the freedom outbound accesses the target directly; the blackhole outbound terminates matching traffic. They can use tags such as proxy, direct, and block. Tags have no special meaning by themselves; behavior is determined by protocol, but stable names make routing rules easier to read.

Array order may affect the default outbound used when no routing rule matches. To avoid relying on vague defaults, place the primary proxy outbound clearly and write explicit rules for LAN traffic, blocklists, and destinations that should connect directly. Clients may add extra outbounds when generating a configuration, such as a DNS-specific outbound or an intermediate outbound for chained proxies. Before editing manually, check whether other objects reference these tags; do not delete an outbound based only on its name.

VLESS outbound example

{
  "tag": "proxy",
  "protocol": "vless",
  "settings": {
    "vnext": [
      {
        "address": "server.example.com",
        "port": 443,
        "users": [
          {
            "id": "00000000-0000-4000-8000-000000000000",
            "encryption": "none",
            "flow": ""
          }
        ]
      }
    ]
  },
  "streamSettings": {
    "network": "tcp",
    "security": "tls",
    "tlsSettings": {
      "serverName": "server.example.com",
      "allowInsecure": false
    }
  }
}

address is the remote server address and may be a domain or an IP; port is the remote listening port. The id in users is an identity identifier and must match the server configuration. VLESS encryption is commonly set to the value required by the protocol and is not the same as TLS at the transport layer. Fill in flow only when the server explicitly enables the corresponding flow-control mode; do not copy it casually from another node.

streamSettings describes how protocol data is carried. network must match the server; security determines whether TLS or another security layer is enabled; serverName is used for the certificate name and handshake target. The remote address, handshake name, and actual certificate name may serve different purposes. Do not assume they are interchangeable merely because they are often identical. allowInsecure controls certificate verification; a normal configuration should keep verification enabled. When the certificate name does not match, check the node parameters, system time, server name, and intermediate network instead of hiding the problem by disabling verification.

How REALITY connection parameters correspond

{
  "streamSettings": {
    "network": "tcp",
    "security": "reality",
    "realitySettings": {
      "serverName": "www.example.com",
      "fingerprint": "chrome",
      "publicKey": "example-public-key",
      "shortId": "0123456789abcdef",
      "spiderX": "/"
    }
  }
}

REALITY settings usually appear in realitySettings. serverName, publicKey, shortId, and the server-side settings must correspond exactly; a single copied value can cause the handshake to fail. fingerprint specifies the client's handshake fingerprint option and should use a value supported by the core and server design. The public key in an example only illustrates field format and cannot be used for a real connection.

When a REALITY node cannot connect, first distinguish network inaccessibility, an incorrect clock, mismatched parameters, and misrouted traffic. Confirm that the remote address and port are reachable, then verify the server name, public key, short ID, and flow-control parameters. Finally, check whether a direct or block rule is handling the target server address incorrectly. Repeatedly switching settings while focusing only on the protocol name rarely identifies the real problem.

Direct, block, and DNS-specific outbounds

[
  {
    "tag": "direct",
    "protocol": "freedom",
    "settings": {
      "domainStrategy": "UseIP"
    }
  },
  {
    "tag": "block",
    "protocol": "blackhole",
    "settings": {
      "response": {
        "type": "none"
      }
    }
  }
]

freedom means the current device connects to the target directly. Its domainStrategy determines whether and how a domain is resolved when the direct outbound connects, but it is separate from the top-level routing.domainStrategy: the former applies while the direct outbound establishes a connection, while the latter applies during route matching. blackhole terminates matching connections and suits explicit block rules. After a block, the application usually sees only a failed connection, so use routing logs to confirm that block was selected.

The boundary between subscription nodes and hand-written configuration

v2rayN, v2rayNG, and v2flyNG convert subscription content into their respective node models, then generate the underlying outbound configuration. Subscription updates may overwrite protocol parameters, so automatically generated files are not a good place to maintain server fields long term. To adjust local routing, DNS, or inbounds, prefer the client's custom configuration, routing presets, or override mechanism. If the client does not support a field, first check whether the selected core family recognizes it before choosing a fully custom configuration.

v2rayN is the preferred desktop choice because it provides node management, routing settings, and runtime logs in one place, making it easier to compare the fields in this chapter. Choose a platform-specific package from the installation packages page. On Android, choose between v2rayNG and v2flyNG according to your core requirements; their configuration-generation details may differ, so after importing the same subscription, rely on each client's runtime logs.

04

routing: Rule matching and traffic-splitting order

Routing does not change the protocol itself; it sends traffic to an already defined outbound based on request characteristics.

Rules are matched from top to bottom

routing.rules is an ordered array. When a request reaches the router, rules are generally checked from top to bottom, and the first match determines the destination outbound. Put narrow, clearly intended rules first and broad rules later. For example, direct LAN traffic should precede a broad proxy rule, and a specific domain block should precede a general domain-proxy rule. If a rule proxying every port comes first, a later direct-LAN rule will never take effect.

The rule type is usually field, followed by conditions such as domain, ip, port, network, protocol, and inboundTag. When one rule contains several types of conditions, the combined conditions must be satisfied for a match; multiple values in the same field usually mean that any one of them may match. Packing unrelated conditions into one rule can make it narrower than expected. A clearer approach is to split them into several rules with one purpose per rule.

A common basic traffic-splitting structure

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "domainMatcher": "hybrid",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "protocol": ["bittorrent"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": [
          "domain:example.net",
          "full:status.example.org"
        ],
        "outboundTag": "proxy"
      }
    ]
  }
}

geoip:private matches common private address ranges and helps keep router, printer, and LAN service traffic from taking a remote route. geosite:private handles the corresponding private-domain set. GeoIP and GeoSite come from different data sources: the former classifies IP ranges, while the latter classifies domain collections. If the data files are outdated, classifications may lag behind; see How to update GeoIP and GeoSite data files.

domain: matches a specified domain and its subdomains, full: matches only the complete hostname, and keyword: matches by keyword. Prefer full: for an exact service, and use domain: for a site and its subdomains. Keyword matching is broad and can accidentally match unrelated domains with similar names, so use it only when the scope is clear and logs have confirmed the result. Plain strings in rules may have different default interpretations depending on the core and configuration context; prefixes reduce ambiguity in hand-written configurations.

How domainStrategy affects domain and IP rules

AsIs means the routing stage handles the original destination as much as possible. When a request arrives with a domain, domain rules can participate; when it contains only an IP, a domain rule generally cannot recover the name on its own. IPIfNonMatch resolves the domain and tries IP rules only after domain rules fail to match. IPOnDemand may trigger resolution earlier when an IP-based rule requires it. These strategies affect DNS timing, route matches, and connection latency, so do not choose one by name alone.

If you want domain sets to take priority and IP geolocation rules to act as a fallback, IPIfNonMatch is usually easier to understand: check the domain first, then resolve it and check the IP if nothing matches. If the configuration contains many IP rules and needs an address early, consider IPOnDemand. If you do not want the router to resolve domains proactively for matching, use AsIs and ensure that inbound sniffing or the application request provides enough destination information.

Split traffic by port, network, and inbound tag

{
  "type": "field",
  "inboundTag": ["socks-in"],
  "network": "tcp",
  "port": "443",
  "outboundTag": "proxy"
}

Ports can be a single value or a range, such as 80,443 or 10000-20000. A port describes the target service port, not a specific website or application; many services share port 443, so port-only routing is usually broad. network can distinguish TCP from UDP, while inboundTag routes based on the local entry through which the request arrived. Creating separate entries for different types of software and routing by inbound tag is often more general than guessing process names.

No match and unintended matches

When no rule matches, the request uses the default outbound. Confirm the default behavior from the configuration structure and core implementation; do not assume it is always direct or always proxied. For troubleshooting, enable an appropriate runtime log level and record the request target, inbound tag, and final outbound tag. If a rule does not match, check whether the request contains a domain or IP, whether sniffing works, whether Geo data exists, and whether the rule prefix is correct. If the wrong rule matches, scan down from the top of the array to find the first rule that covers the target.

After adjusting routing, test a few explicit targets separately: one LAN address, one domain intended to connect directly, one domain intended to use the proxy, and one blocked target. Do not use “most pages open” as proof that traffic splitting is correct, because the default outbound may conceal rule errors. Node latency tests only describe the result of specific test requests and cannot replace rule-by-rule verification. For cases where system proxy and terminal traffic differ, continue with Troubleshooting browsers and command-line terminals separately.

05

dns: Resolution paths, server selection, and routing

DNS is more than a list of servers; it also affects domain rules, IP rules, and the final outbound connection.

First distinguish system resolution from built-in resolution

When an application connects, the domain may be resolved by the application or operating system first, or passed to the local proxy as a domain. In the former case, the inbound may see only an IP; in the latter, V2Ray's built-in DNS and routing strategy can participate directly. With TUN mode enabled, more system traffic may enter the client's processing chain, but whether a request is handled by built-in DNS still depends on the generated inbound, DNS hijacking, and routing configuration.

Therefore, setting dns.servers does not mean that every name lookup on the device will automatically use it. A browser's secure DNS, an application resolver, the system cache, and terminal tools may each follow a separate path. When troubleshooting resolution, answer three questions first: who initiated the query, which server received it, and whether the returned address was ultimately used by routing rules. Changing only the DNS server without confirming the path often produces no visible change.

Basic DNS object example

{
  "dns": {
    "hosts": {
      "domain:internal.example": "192.0.2.10"
    },
    "servers": [
      {
        "address": "1.1.1.1",
        "domains": ["geosite:geolocation-!cn"],
        "skipFallback": true
      },
      {
        "address": "223.5.5.5",
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      "localhost"
    ],
    "queryStrategy": "UseIP"
  }
}

hosts provides static domain mappings and suits controlled tests or fixed internal addresses, but it should not replace continuously changing public resolution. The example address only illustrates the structure. servers can contain simple addresses or objects with domain scopes, expected IP ranges, and fallback controls. The destination of a query depends on both list order and matching conditions. Multiple servers do not simply compete simultaneously; behavior is affected by domain conditions, fallback logic, and the core implementation.

domains specifies the domain set that a server should handle preferentially, using syntax similar to routing domain rules. expectIPs checks whether returned addresses fit the expected classification; it is not a general replacement for an address filter. If the result does not meet the condition, the resolver may try another server. Missing or outdated data files also reduce the accuracy of Geo-based expectations. skipFallback affects whether a server participates in fallback, so understand the current server groups before enabling it rather than applying it uniformly.

queryStrategy and address-family selection

queryStrategy controls the address-family strategy used for queries. Common meanings include considering all available addresses, using IPv4 only, or using IPv6 only; the exact supported values must match the current core. The choice must fit the device's network conditions. If the local network has no usable IPv6 path but IPv6-only queries are forced, the domain may resolve successfully while the subsequent connection fails. Conversely, when a network is reachable only through a particular address family, do not blindly rely on the default result.

In a dual-stack environment, failure may also occur because DNS returns multiple addresses but the application or outbound tries an unreachable address first. Observe the DNS response, outbound selection, and connection logs together. Do not attribute every timeout to DNS: if the logs show an address and the connection phase times out, the problem has moved to the outbound network or remote service. If the logs show a failed lookup, no usable record, or unmet expected conditions, focus on the DNS section.

Which outbound handles DNS queries

A DNS server is itself a connection target and may be sent through a direct or proxy outbound by routing rules. If the DNS server is specified as a domain, resolving that server's domain can also create a bootstrap-resolution problem. To reduce circular dependencies, basic DNS server addresses are often specified as directly reachable IPs, or handled through a dedicated outbound and explicit routing rules. With HTTPS-based resolvers, also consider the endpoint domain, certificate name, and its own resolution path.

You can assign DNS queries a dedicated outbound tag and route them using protocol or inbound-tag conditions. Keep the path clear: a request enters built-in DNS, the DNS query reaches the server through the specified outbound, the result returns to the router for matching, and the business traffic then uses its business outbound. If DNS queries are mistakenly sent through a proxy outbound that depends on the same DNS result, startup may wait in a loop. Client presets usually handle basic dependencies; when overriding them manually, do not delete unfamiliar DNS routes.

Caching, Fake DNS, and TUN scenarios

System caches, application caches, and built-in resolver caches may all exist at once. After changing DNS, the old result may not disappear immediately when you revisit the same domain. For verification, restart the target application and client, or use a domain that has not been queried before to observe the full process. TUN mode may use Fake DNS to map domains to reserved addresses and restore the domain when traffic enters the chain for domain-based routing. These reserved addresses are not real remote IPs and should not be used directly for GeoIP classification.

Fake DNS helps when the system submits only IP traffic and the domain information is missing, but DNS interception, the mapping table, and the TUN inbound must remain consistent. If an application receives an address but cannot connect, check whether the same running instance recognizes the mapping, whether the request bypassed TUN, and whether another rule sent the reserved address direct too early. For the system-level traffic takeover logic and client entry points in TUN mode, see How TUN mode takes over system traffic.

06

policy: Session timeouts, statistics, and system-level controls

policy does not choose nodes; it defines operating boundaries for connection lifecycles and statistics.

levels and user-level references

policy.levels uses user levels as keys and applies policies to connections at each level. User objects in some inbound or outbound protocols may include a level, which is then used to find the corresponding policy. A level is not a speed rating or an automatically ordered permission rank; it is simply a numeric key for referencing a policy set. If no level is explicitly assigned, the default level is usually used, but confirm the exact behavior from the current configuration and core implementation.

A single level can consistently control handshake time, idle connection time, and upload/download statistics. Do not create many unreferenced levels just to make the configuration look complete. For a client-generated single-user outbound, it is usually enough to understand whether the default level enables statistics or changes timeouts. Multi-user servers use levels more often for differentiation, but this page focuses on reading client configurations, so pay particular attention to policies that may unintentionally shorten normal connections.

Policy object example

{
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5,
        "statsUserUplink": false,
        "statsUserDownlink": false,
        "bufferSize": 4
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  },
  "stats": {}
}

handshake limits how long the connection setup phase may wait. If it is too short, network fluctuations, name resolution, or a slightly slow remote handshake may be terminated prematurely; if it is too long, unreachable connections take longer to report failure. connIdle describes how long a connection may remain open without data transfer. Long-lived applications, push messaging, remote terminals, and continuous-transfer tools are more sensitive to idle time, so do not tune it based only on web browsing.

uplinkOnly and downlinkOnly control how long a connection is kept after only one direction of data remains. They manage connection lifetime and do not limit bandwidth. bufferSize affects the data-buffering strategy for each connection; increasing it blindly may raise memory use, while reducing it too far may hurt throughput. Most client users should keep the defaults provided by the core or client and adjust them only when logs and reproducible tests show a connection-lifecycle problem.

Understand statistics switches in pairs

statsUserUplink and statsUserDownlink control per-user statistics, while the entries in policy.system control inbound and outbound statistics. Writing only "stats": {} does not necessarily collect every metric automatically; the corresponding policy switches must also be enabled. Conversely, enabling statistics without any interface or API reading the values only creates extra recording work. Whether a desktop client displays traffic statistics also depends on how it starts the core and reads the statistics API.

Statistics are useful for observing traffic direction in the current running instance, but they should not be treated as an authoritative source for subscription quotas, billing records, or remote-server traffic. Restarting the client, reloading the configuration, or switching cores may reset local statistics. When troubleshooting missing traffic numbers in the interface, first confirm whether the client is designed to read core statistics, then check the stats object and system switches rather than changing protocol parameters.

How policy relates to timeout errors

A timeout in the logs may occur during name resolution, a TCP connection, TLS or REALITY handshake, proxy-protocol handshake, or application read/write. policy explains only some session timeouts. If a connection consistently stops after a short interval, check handshake and connIdle; if the logs explicitly say that the remote address timed out, check the network and outbound first; if only one application's long-lived connection drops periodically, compare its idle interval with the policy values.

Before changing timeouts, record the stage and timing pattern of the error. Simply making every value very large delays failure feedback and keeps dead connections consuming resources longer. Making every value smaller harms slow networks and long-lived connections. Keep the default policy as a baseline, change one item only for a reproducible case, and retest the same target after restarting the client.

System policies and configuration portability

Different clients and core families may support different policy fields or defaults. The core used by v2rayN, the Xray core commonly used by v2rayNG, and the v2fly core associated with v2flyNG may behave differently even where their fields are compatible. Before copying a complete configuration to another client, check whether the target client permits a fully custom configuration and whether it rewrites policy at startup.

Portable configuration is not about using as few fields as possible; it is about clearly separating standard structure, core extensions, and client-generated content. Inbound ports, log paths, and TUN parameters often depend on the platform; server protocol parameters are usually portable; interface state and runtime configuration may not correspond one-to-one. When moving between platforms, establish a working baseline by importing the subscription first, then migrate routing, DNS, and policy item by item instead of replacing the entire file.

07

Configuration loading, client overrides, and validation

Separating syntax, structure, and real-connection validation quickly narrows the scope of a problem.

Layer 1: Confirm that JSON parses

Syntax validation answers only whether the configuration is valid JSON. Check quotation marks, commas, square brackets, and curly braces in pairs, and verify that numbers, booleans, and strings use the correct types. JSON does not accept comments or trailing commas. When copying a configuration from a webpage, chat, or document, save it first as a plain-text UTF-8 file to avoid curly quotes and invisible control characters.

Syntax errors usually report a line and column, but the actual mistake may be on the previous line. For example, an error reported at the start of a new property is often caused by a missing comma after the preceding item. For an end-of-file error, first check whether an object or array is still unclosed. Editor bracket matching is helpful, but it cannot replace checking data types and field hierarchy.

Layer 2: Confirm that tags and fields are in the right places

Valid JSON may still be an invalid V2Ray configuration. The second validation layer checks top-level field names, protocol-specific settings, transport-layer streamSettings, and routing references. Common structural errors include placing tlsSettings at the outbound root, writing a single outbound as an object instead of an array, referencing a nonexistent outboundTag in a rule, and writing a string array as one string.

The way unknown fields are handled varies by core: some reject the configuration immediately, while others may ignore the field. Ignoring a field is harder to troubleshoot because the configuration appears to start successfully even though the expected feature does not work. If the logs show unknown field, failed to parse, failed to build, or a missing tag, return to the relevant object level first instead of continuing to test network connectivity.

Layer 3: Confirm which configuration the client actually loaded

v2rayN, v2rayNG, and v2flyNG may all generate a runtime configuration at startup based on interface settings. The file edited by the user is not necessarily the one ultimately read by the core, especially when subscription nodes, routing presets, system proxy, and TUN mode are enabled together. Before validating, use the client logs to confirm configuration generation and core startup, and check the client's export, preview, or runtime-directory options.

Subscription updates usually replace node parameters but may not replace local routing; a fully custom configuration may bypass some interface settings. Identify whether the current mode is “subscription nodes plus client template” or “fully custom configuration.” Mixing the two often causes a port changed in the interface not to appear in the runtime file, or hand-written routes to be regenerated by a preset. For a separate troubleshooting guide, see Failed subscription updates and automatic update settings.

Establish a connection baseline in stages

  1. Verify startup: Confirm that the core has started, the local SOCKS or HTTP port is listening, and there is no port conflict or configuration parsing error.
  2. Verify the local entry: Use an application that clearly supports proxies to connect to the local port, then check whether the access log shows the corresponding inbound tag.
  3. Verify a single outbound: Temporarily use simple routing and confirm that the target request completes through the proxy outbound without interference from complex rules.
  4. Restore DNS and routing: Add LAN, domain, GeoIP, and block rules one group at a time, recording the outbound ultimately used by each request.
  5. Restore system takeover: Enable the system proxy or TUN last so more applications enter the processing chain, then verify the browser and terminal separately.

This sequence divides the problem into five layers: startup, local entry, remote outbound, traffic splitting, and system takeover. If the first layer is incomplete, there is no reason to analyze the remote protocol. If the local entry receives no request, check the application's proxy settings. If a simple outbound works but routing fails after restoration, focus on DNS or rules. If manual proxying works but system takeover fails, focus on system proxy state, TUN permissions, and routing conflicts.

Log levels and useful information

During troubleshooting, log.loglevel can be raised to a level that includes more detail. More detailed logs make inbounds, routing, and outbounds easier to observe, but they also produce more output. There is no need to keep maximum detail enabled during normal use. Capture troubleshooting logs around one clear test, from core startup through the target request's success or failure, rather than mixing different configurations in a large history.

{
  "log": {
    "access": "",
    "error": "",
    "loglevel": "info",
    "dnsLog": true
  }
}

The exact log path and output method may be controlled by the client. Whether an empty string means console output should also be confirmed from the current core and client behavior. dnsLog helps observe built-in resolution, but only queries handled by built-in DNS appear there. For a complete guide to reading logs, see Common V2Ray runtime log errors and how to locate them.

Change records beat repeated guesswork

Change one field group per test round and record the before value, after value, test target, and log result. For example, after changing domainStrategy from AsIs to IPIfNonMatch, test only a domain that requires an IP rule; do not also change the node, DNS server, and TUN mode. If the result gets worse, you can roll back precisely; if it improves, you know which change took effect.

After upgrading the client or switching cores, repeat the minimal validation process instead of assuming the old configuration remains fully compatible. Do not invent or rely on fixed version numbers to determine field support; check the core type selected by the client, startup logs, and configuration errors. If you need to reinstall the client, choose the Windows, macOS, Android, or Linux entry from the installation packages page.

08

Common error diagnosis and long-term maintenance

Work from the layer where the error occurs instead of randomly switching options across the entire configuration.

Startup failure: check syntax, ports, and references first

When the core exits immediately after startup, check three categories first. The first is JSON syntax, including missing commas, mismatched brackets, and incorrect data types. The second is local resource conflicts, such as a listen port occupied by another process, an unwritable log directory, or a TUN device that cannot be created. The third is configuration-reference errors, such as a nonexistent routing target tag, missing required protocol fields, or an extension unsupported by the current core.

Follow the order shown in the logs. A parsing error occurs while reading, so fix the file first; an address conflict occurs while listening, so check duplicate clients and local ports; a missing outbound occurs while building routes, so verify tags. Only after the core clearly reaches a running state should you investigate remote connections. A client interface showing “not connected” is not enough to identify the layer; inspect the final portion of the error log.

The local port exists, but the application has no traffic

First confirm that the application uses the correct proxy type and port. HTTP and SOCKS proxies are different entries, and a system proxy does not guarantee that terminals or background services use it. Then check whether the application bypasses local addresses, has its own resolver or proxy settings, and whether the request actually reaches the access log. If this request is completely absent from the log, the problem lies between the application and the inbound; an inbound record means the traffic has entered V2Ray.

If only some applications fail, configure an explicit SOCKS or HTTP entry for the affected application as a comparison. If a manual entry works but the system proxy does not, check the operating system's proxy state and exclusion list. If a browser works through the system proxy but the terminal does not, configure a proxy option supported by the terminal tool. Do not change the remote-node protocol just because one application bypasses the system proxy.

Domains fail while IPs work

Prioritize the resolution path. Confirm whether the application submits a domain or an already resolved IP, whether built-in DNS receives the query, whether the server returns a usable address, and whether queryStrategy selects an address family reachable on the current network. If the domain rule depends on sniffing, check whether the inbound can identify the destination name. With Fake DNS, verify that the reserved address is restored by the same running instance.

If DNS has already returned an address but the connection times out during the outbound phase, the problem is no longer “no resolution result”; it is the address's connection path, routing, or remote service. If expectIPs rejects the returned address, check the Geo data and server groups. If only a recently changed domain still uses the old address, consider application, system, and built-in caches; restart the relevant processes or test a new domain to rule out caching.

The rule looks correct but does not match

Start with the request's actual form: does the router receive a complete domain, a subdomain, or an IP? Does the rule use full:, domain:, or a keyword? Is a broader rule above it matching first? Are the Geo data files available? Similar-looking rule text and targets do not guarantee identical matching semantics. full:example.com does not match other subdomains, while an overly broad keyword may match several unrelated targets.

Temporarily moving the problem rule toward the top of the array can show whether another rule is masking it, but review the overall order again after testing. A more reliable method is to enable routing logs and record the outbound tag ultimately selected for the request. If the request contains only an IP, a domain rule naturally cannot match; check sniffing, the application's resolution behavior, or add a suitable IP rule instead of repeatedly rewriting the same domain string.

Node parameters are correct, but the handshake fails

Confirm that the server address and port are reachable, then check protocol identity parameters, transport, and the security layer. For TLS, check the server name, system time, and certificate errors. For REALITY, check the server name, public key, short ID, fingerprint, and flow-control parameters. Other protocols likewise require every client parameter to correspond to the server. Do not combine transport fields from different nodes into one configuration, because each layer may depend on the others.

If failure begins immediately after a subscription update, keep the old node for comparison and check whether the address, port, transport, or identity parameters changed. Do not guess missing fields manually. If every node fails at once, check the local network, system time, core startup, and routing first; if only one node fails, its parameters or remote state are more likely to be responsible.

Organization principles for growing configurations

Over time, configurations accumulate dead tags, duplicate domain rules, overlapping Geo rules, and unused inbounds. When cleaning up, map the current processing chain first: list every inbound tag, outbound tag, each rule's target, and the purpose of every DNS server. Delete objects only after confirming that nothing references them. Rules with the same target and outbound can be merged, but do not pack different purposes into one opaque rule just to reduce line count.

Use a stable naming convention for tags and order routes as “special blocks, direct LAN traffic, specific proxies, default handling.” Group DNS server objects by applicable domain scope, and keep only levels that policy actually uses. When the configuration format does not support comments, maintain a separate record describing each tag's purpose, the last verified scenario, and how the client generates the file. This makes future changes safer.

Client updates and configuration migration

Before updating the client, save the currently working configuration, routing presets, and subscription settings. After updating, run a minimal connection test with an existing node before checking advanced features. If the client changes its core, watch startup logs for unknown fields and deprecation notices. Do not delete underlying configuration just because interface labels changed; export the newly generated runtime file and compare its top-level sections with the old configuration.

When moving from one desktop platform to another, server protocol parameters can usually be restored through the subscription, but local listen ports, system proxy settings, TUN permissions, and log paths must be configured again for the new platform. When moving to v2rayNG or v2flyNG on Android, import the subscription first, then restore routing and DNS for the target core. Platform differences mainly concern system takeover and file locations; do not add platform-specific guesses to the server outbound.

Build a fixed troubleshooting checklist

Startup layer

JSON, field hierarchy, port conflicts, tag references, and runtime permissions.

Entry layer

Application proxy type, local port, system proxy, TUN takeover, and access logs.

Resolution and routing layer

Domain or IP, DNS responses, rule order, Geo data, and the final outbound tag.

Outbound layer

Server reachability, identity parameters, transport, security layer, and handshake logs.

Recording every failure in these four layers prevents repeated work. When the logs show rejected, determine whether the rejection occurred at the local entry, in routing, or remotely. For timeout, identify the timeout stage. For identity or handshake errors, return to the corresponding outbound parameters. If the cause is still unclear, visit Troubleshooting and continue by category: fundamentals, installation and configuration, Advanced, and Troubleshooting.

The goal of configuration maintenance is not to fill every optional field, but to give each section a clear purpose and prove its result through logs. Keep a simple, reproducible baseline first, then add routing, DNS, TUN, and policy. When something breaks, restoring each layer from the baseline is more reliable than randomly deleting fields from a complex configuration.