Configuration Fields / JSON Reference

V2Ray Configuration Reference

Inbounds, outbounds, routing, and DNS. Follow request flow to find fields, then verify changes against the logs.

For your first setup, start with the Getting Started guide to import a subscription and connect. This page covers configuration structure, field locations, and custom configuration troubleshooting; it is not a replacement for the client's everyday setup workflow.

Desktop: v2rayN first Xray / V2Fly Merge snippets into your config
Request flow
inbounds routing outbounds

DNS provides name resolution; policy controls connection behavior. The client also handles the system proxy, subscriptions, and generating the runtime configuration.

JSON Structure and Configuration Scope

Identify the configuration layer you’re editing

In the V2Ray ecosystem, “configuration file” can refer to more than one file. v2rayN stores server profiles, subscription groups, routing rules, and preferences, then generates a runtime config for the selected core. v2rayNG and v2flyNG also separate app data from the core’s runtime configuration. Changing a port in the interface doesn’t change the original subscription, and editing a temporary JSON file doesn’t mean the change will persist after the next launch. Before looking up fields, check whether the file is manually managed, exported by the client, or generated in a temporary directory at startup. Note which client and core produced it.

The examples on this page use V2Ray-style JSON and fields commonly found in Xray. V2Fly and Xray share a configuration foundation, but support for protocols, transport names, and extensions can differ. sing-box uses a different configuration format; v2rayN’s ability to switch cores doesn’t mean this JSON can be passed to sing-box unchanged. If you see an “unknown field” error, first identify the core that actually started, then check which fields it supports. Don’t infer core behavior from the client name alone. To get a client, choose the right option on the platform download page.

Objects, Arrays, and References

A JSON document has an object at its top level, with field names and strings enclosed in double quotes. Arrays, written with square brackets, hold multiple inbounds, outbounds, or rules; objects, written with curly braces, group related settings. Port numbers are usually numeric, while Boolean values use lowercase true or false. Standard JSON does not allow comments, trailing commas, or single quotes. Even if a tool accepts a commented extension, export standard JSON before sharing or migrating the file so another parser can read it.

This standalone baseline config provides a direct local connection for checking JSON parsing, a local listener, and log output. It does not include a proxy server. It listens only on the loopback address, does not set the system proxy, and does not create a desktop shortcut. Once loaded, the local SOCKS endpoint passes requests to the direct outbound. Reaching a destination only confirms that this direct path works; it says nothing about whether a proxy server in your subscription is available. Before giving the config to a client, check that it supports custom configuration and whether it will overwrite the local port.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

From Valid Structure to Correct Behavior

Successful parsing only confirms valid syntax. A routing rule can still fail or send traffic the wrong way if its outboundTag doesn’t exist or an inbound tag is misspelled. Tags are case-sensitive reference names within the same runtime config; they are not UI labels or server hostnames. List your inbound and outbound tags first, then check every rule reference. Array order also matters: ordinary routing rules are tested in order, and the first outbound usually handles requests that don’t match a rule. Don’t sort outbounds alphabetically without checking the effect.

FieldWhat it doesCommon pitfall
inboundsLocal listener and accepted protocolPutting the server address in listen
outboundsConnection destination or proxy serverIgnoring the default outbound order
routingMatches requests and selects an outboundReferencing a nonexistent tag
dnsBuilt-in resolver settingsAssuming it automatically takes over system DNS
policyUser-level connection policies and statistics togglesTreating time settings as bandwidth limits

When using the snippets below, merge each field into the appropriate top-level section instead of pasting several complete objects into one file. Don’t define routing or dns more than once in a top-level object: how duplicate keys are handled depends on the parser, which can make the UI and runtime behavior disagree. Change one feature at a time, keep a copy of the previous version, and check the startup log to confirm the result. If you only need to import a subscription and connect, let the client generate the config. Edit JSON manually only when the interface can’t express what you need.

inbounds: Inbounds and Local Listening

What listen, port, and protocol do

An inbound is where application traffic enters the core. listen sets the local address to bind to, port sets the port, and protocol determines what the inbound accepts. Desktop proxies typically listen on 127.0.0.1, allowing connections only from local processes. Setting listen to 0.0.0.0 usually binds to every IPv4 interface and may expose the endpoint to your local network. It doesn’t mean “use any server” or guarantee access from outside; firewalls and network conditions still apply. Don’t widen the listening scope unless you specifically need to share the proxy.

SOCKS and HTTP are different local proxy protocols, so app settings must match the inbound. A browser configured to use an HTTP proxy can’t simply point to a SOCKS-only port. If a command-line tool uses SOCKS, check whether it supports resolving domain names through the proxy. v2rayN may provide a local mixed port implemented by the client and selected core. That doesn’t mean you can use a protocol named mixed in any V2Ray-style config; check the current core’s configuration reference.

Define SOCKS and HTTP side by side

This snippet contains two inbound objects. Replace the top-level inbounds array or merge them into it; don’t add them alongside an existing inbound using the same port. Both bind to the loopback address. SOCKS accepts UDP, while the HTTP inbound is for apps that support an HTTP proxy. Setting udp only means the inbound accepts relevant requests; it doesn’t guarantee that the outbound, server, and network path can forward UDP. If an app doesn’t send requests to the proxy, changing core routing won’t make it use these ports.

{
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    },
    {
      "tag": "local-http",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ]
}

The local port is not the server’s remote port. The former is where local apps connect; the latter is set in the server details under outbounds. They usually don’t need to match. If a port is already in use, close any duplicate client instances, check for a background core process, and then choose an available port. After changing the inbound port, update the app’s proxy settings and the client’s system proxy configuration too. Otherwise the core may start normally while the browser continues connecting to the old port.

What sniffing does—and doesn’t do

Sniffing attempts to extract domain names from supported traffic characteristics so routing can use them. It does not decrypt traffic and won’t identify a domain for every connection. In Xray, for example, the object below can go inside an inbound object; routeOnly means the sniffed result is used only for routing. Check your core’s documentation for support and recognized protocols. If an app already sends a domain name to the SOCKS inbound, routing typically has the domain information it needs; there’s usually no need to enable every sniffing option just for completeness.

{
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"],
    "routeOnly": true
  }
}

Android VPN mode receives traffic through a virtual network interface provided by the system, so users generally don’t need to enter a SOCKS port in each app. v2rayNG’s per-app proxy setting determines which apps use or bypass that path; it’s separate from the inbound JSON’s listen address. On desktop, distinguish system-level traffic capture from core-level handling too. First confirm that traffic reaches the core, then investigate routing. A “VPN connected” status doesn’t prove that every app uses the same entry point.

If you let devices on your local network connect, consider authentication, allowed sources, and firewall rules. Unauthenticated HTTP or SOCKS endpoints should not be exposed to untrusted networks. Devices connecting to the proxy need to know its address, and the host should restrict access to the intended subnet. Changing listen without accounting for network boundaries can expose more than you intended. For troubleshooting, test loopback access first, then allow local network access, and finally check the firewall. Opening access one layer at a time makes it easier to find the cause of a problem.

outbounds: Outbounds and Protocol Settings

Default outbound and explicit tags

An outbound determines how the core handles a request next. freedom is typically used for direct connections, blackhole for blocking, and protocols such as VMess for forwarding through a server. A tag is an identifier used to select an outbound in routing; it doesn’t become a working server by itself. An outbound tagged proxy could use any protocol, so check both protocol and settings to understand what it does. Requests that don’t match a routing rule usually go to the first outbound in outbounds. Putting direct first selects direct connections as the default path.

The snippet below defines direct and block outbounds. It’s useful for practicing routing references, but it doesn’t provide a remote proxy. With empty settings, blackhole handles blocked requests according to the protocol’s default behavior. If you need a specific response, check the current core’s protocol settings rather than using arbitrary HTTP text as a generic reply. Give each outbound a clear, unique tag such as direct, block, and proxy. During troubleshooting, readable labels matter more than saving a few characters.

{
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ]
}

Understanding server fields with VMess

A VMess outbound stores the server address, remote port, and user parameters in the vnext array under settings. address can be a server hostname or IP address; port is the port the server actually listens on; users describes the authentication user. The example shows the field structure only: example.net is a reserved documentation domain, and the all-zero UUID is a placeholder for illustration. Neither can connect to a real service. Use matching parameters from an existing server configuration or valid subscription. This is not a working node, and generating a random UUID won’t reveal server credentials.

{
  "tag": "proxy",
  "protocol": "vmess",
  "settings": {
    "vnext": [
      {
        "address": "example.net",
        "port": 443,
        "users": [
          {
            "id": "00000000-0000-0000-0000-000000000000",
            "alterId": 0,
            "security": "auto",
            "level": 0
          }
        ]
      }
    ]
  }
}

This object alone is not a complete connection setup; it also needs transport and security settings that match the server. Setting the port to 443 does not enable TLS, and security under users is not the same as security under streamSettings: they belong to different layers. Modern VMess configs typically use an alterId of zero, but when migrating older settings, follow the server configuration. An incorrect system clock can affect authentication. If a handshake fails, check the system time and time zone before reviewing user parameters.

Don’t Reuse Fields Across Protocols

The user structures for VMess, VLESS, and other protocols are not identical. Pasting one protocol’s users object under another may cause a parsing error or produce a meaningless combination of settings. Similarly, the connection address, transport Host, and TLS serverName can differ: they’re used for the network connection, application-layer request, and certificate validation, respectively. Set each value only when the server requires it. Don’t overwrite subscription settings just to make all three names match.

FieldCommon locationWhat to check
tagOutbound objectMust match outboundTag exactly
address / portProtocol server settingsActual server address and listening port
idProtocol user entry that supports UUIDUse valid server details
streamSettingsOutbound objectMust match the server’s transport and security settings

Subscription updates can overwrite manual changes. Clients generally regenerate outbounds from the server profiles in a subscription, so editing a temporary JSON file won’t update the subscription and may be lost the next time you switch servers. For persistent customization, use the client’s custom configuration or template feature, and keep track of where the server details came from. If you only need to change routing, leave authentication fields alone. If you only need to change the local port, there’s no need to rewrite the entire outbound. Keeping changes focused reduces the risk of breaking working connection settings.

“Connectivity test passed” and “traffic works as expected” are not the same conclusion. A test may check only whether a connection can be established, or whether one URL or request works. Real apps may also use long-lived connections, other domains, and UDP. First confirm that the outbound can establish a protocol connection. Then check the access log to verify that app requests use the expected tag, and test the actual app. For the difference between a single share link and a subscription URL, see Share Links vs. Subscription Import. This helps you avoid editing fields at the wrong data layer.

streamSettings: Transport and Security

Keep protocol, transport, and TLS separate

The outbound protocol handles proxy requests and authentication. Transport determines how data travels between the two ends, while security layers such as TLS protect the connection and verify identity. These dimensions are related but not interchangeable. The same VMess protocol can use different transports, but the server may not support every combination. An option in the client UI doesn’t prove that the current server supports it. Troubleshoot one layer at a time: confirm the protocol, then the transport, then the security settings, rather than cycling through every combination in a menu.

streamSettings belongs inside the relevant inbound or outbound object; it is not an arbitrary top-level field. This section covers client outbounds and uses WebSocket with TLS to illustrate the structure. When network is ws, its settings go under wsSettings; when security is tls, its settings go under tlsSettings. Supported transport names and extensions vary by core and implementation. If you see an unfamiliar name or a deprecation warning, check what the current core supports instead of replacing it with a similar-looking field name.

{
  "streamSettings": {
    "network": "ws",
    "security": "tls",
    "tlsSettings": {
      "serverName": "example.net"
    },
    "wsSettings": {
      "path": "/proxy",
      "headers": {
        "Host": "example.net"
      }
    }
  }
}

Address, serverName, and Host

example.net and the path in this example illustrate the fields only; use the values provided by the server for a real connection. The outbound address determines where the connection starts. tlsSettings.serverName sets the TLS server name and is used for certificate validation. WebSocket Host sets the host name in the HTTP request. A reverse proxy may require different values for each. Record them separately when editing by hand; don’t copy the server label into every field. Matching domain names are common in some deployments, but they aren’t a universal configuration rule.

path is not arbitrary explanatory text. A reverse proxy may route WebSocket requests to a backend based on the path. A mismatch can return a regular webpage, a redirect, or an error. Case, the leading slash, and extra query parameters can all affect server behavior. If a connection closes right after it’s established, check the required path and Host first, then confirm that any intermediary proxy supports connection upgrades. Changing the local inbound port won’t fix a remote WebSocket path mismatch; they’re entirely separate settings.

Troubleshoot by connection stage

A connection timeout means that some stage didn’t finish within the allotted time; it doesn’t prove that the authentication password is wrong. If the server address won’t resolve, check DNS first. If it resolves but a connection can’t be established, check the remote listener, firewall, and network path. If the connection reaches TLS, logs often point to a certificate or handshake issue. Only after proxy protocol authentication begins should user parameters be your main focus. This order helps prevent changing routing, DNS, and authentication settings all at once.

The transport layer also has advanced options for socket behavior, connection reuse, and specific transport extensions. These are rarely required for a first setup. Combining “optimization” settings from different sources can change connection lifetimes, introduce incompatible combinations, or make requests unrecognizable to the server. Start with a minimal configuration that matches the server, then test one added option at a time against a specific need. Without evidence of a problem, piling on settings is unlikely to make the connection faster.

Extensions such as REALITY are not drop-in alternatives to tlsSettings; they have specific core requirements and their own parameter constraints. v2rayNG uses the Xray core, while v2flyNG uses the V2Fly core. They don’t necessarily support the same protocols and extensions. Before migrating, check the features each server requires, then choose a client and core. Don’t import everything first and remove fields one error at a time. When using v2rayN on desktop, confirm which core is actually selected for that server.

To compare configuration changes, keep the server, network, and test destination the same. Change one parameter at a time and record how the log stages change. If you change the transport, server address, and routing mode in one test, restoring the connection won’t show which change helped. For repeated handshake failures, continue troubleshooting with Diagnosing Errors in Runtime Logs. Investigate speed separately after the connection is working; not every slow connection is a TLS problem.

routing: Routing Rules and Order

Understand when rules take effect

Routing processes requests that have already reached the core, then selects an outbound based on conditions. It won’t enable the system proxy for an app or receive connections that never entered the client. Ordinary field rules are evaluated in array order; the first match determines the outbound. Different conditions in a rule usually all need to match, while multiple entries in the same domain or ip array specify alternative matches. Define the traffic source, destination, and desired outbound before choosing fields. Don’t fill a list with rules and then guess their priority.

This partial routing config sends private addresses and geosite:cn domains to direct. It requires an outbound tagged direct, and the geoip and geosite references require compatible rule data in the runtime environment. If no rule matches, ordinary configurations fall back to the first outbound; they don’t automatically select one named proxy. To route all other requests through a proxy, configure the default outbound only after confirming that a working proxy outbound exists. Don’t assume behavior from a rule name alone.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      }
    ]
  }
}

domainStrategy and Matching Behavior

routing.domainStrategy controls how domains and addresses are handled during routing. AsIs generally matches only the information already present in the request. IPIfNonMatch attempts DNS resolution when no domain rule matches, allowing address rules to be checked next. IPOnDemand triggers resolution when matching requires address information. This setting is not a DNS server address, nor does it force all outbounds to prefer one address family. Consider whether you have address rules, whether domains resolve correctly, and how extra DNS lookups may affect troubleshooting and request flow.

Domain rules can match an exact hostname, a domain and its subdomains, a keyword, or a regular expression. full:example.net matches only that exact name; domain:example.net includes its subdomains; regexp: requires familiarity with regular expressions and JSON string escaping. For a single site, prefer the most specific matching form. Broad keywords may match unrelated domains, while complex regular expressions are harder to maintain. After making a change, test both destinations that should match and ones that should not.

{
  "type": "field",
  "domain": [
    "full:example.net",
    "domain:example.org"
  ],
  "network": "tcp",
  "outboundTag": "direct"
}

This example rule requires the destination to match one of the listed domains and the network type to be TCP. If a request contains only an IP address, the domain rule may have nothing to match. If sniffing provides a domain, its availability still depends on the sniffing and routing settings. Don’t confuse “the website uses this domain” with “the core received this domain.” The information visible to the core can vary if an app resolves names itself, uses encrypted DNS, or connects directly to an address. Checking the destination shown in the access log is often more useful than adding more domain entries.

Order from specific exceptions to the default path

Put narrow rules that need priority before broader ones. For example, handle a specific internal service before larger address or domain sets. If an earlier rule covers all TCP traffic, a later TCP exception for one site probably won’t take effect. Document why each rule exists, but keep notes in documentation or a client-supported comment field; don’t add Chinese comments to standard JSON. Keep a rollback copy when removing obsolete entries so you don’t accidentally break dependencies.

“Bypass mainland China” is a common name for a predefined client rule set, not a Boolean switch in JSON that handles all routing automatically. Its actual effect depends on rule data, rule order, the default outbound, and DNS behavior. Results for edge cases may differ between clients, cores, and rule data versions. For precise routing, inspect the runtime routing config rather than relying on a menu label. For how the system proxy relates to routing modes, see System Proxy and Bypass Mainland China Modes Explained.

When troubleshooting a single destination, first confirm that its request reaches the core, then check which outbound it matches, and finally verify that outbound can connect. Add a temporary rule for the exact domain as a comparison. If it still doesn’t match, check whether the request contains a domain, whether the config was reloaded, and whether a broader rule above it takes precedence. For local network access issues, also check the system proxy bypass list; the request may never reach the core. Desktop process-based rules depend on the platform and traffic-capture capabilities. Android per-app proxy settings are not a universal routing field.

dns: DNS and Routing Together

Distinguish system DNS from the built-in resolver

DNS translates domain names into addresses, but a single visit may involve more than one lookup. Apps may use the system resolver or their own resolution method; the core may also use built-in DNS for routing decisions or outbound connections. The top-level dns section configures the core’s resolver. Its presence alone does not take over DNS for the whole device. If the system still uses its existing resolver, that doesn’t mean the core’s DNS settings failed to load. First identify which process and stage produced the query you’re observing.

If a request reaches SOCKS with its domain name, the core or remote server may resolve it later. If the app resolves the name first and sends an address, the core may see only that address. These modes change what domain information routing can use and where errors appear. If a domain fails but its IP address works, first find out which side performs the lookup, then check the result. Switching every DNS server at once may hide the original problem without explaining why an app and a browser behave differently.

A resolver snippet to illustrate the fields

This snippet uses the local resolver, preserves the system’s current DNS path, and asks built-in queries to prefer IPv4 results. In hosts, example.test maps to an address reserved for documentation; it is not a real website. After merging this into the top-level dns section, a feature must call the built-in resolver for these settings to take effect. Support for query strategies and server options varies by core. If startup reports an invalid field, check the configuration reference for the core you’re running.

{
  "dns": {
    "hosts": {
      "example.test": "192.0.2.10"
    },
    "servers": [
      "localhost"
    ],
    "queryStrategy": "UseIPv4"
  }
}

hosts is useful for a small number of clear, stable local mappings, not as a long-term replacement for resolving dynamic sites. Many services rely on multiple addresses, regional routing, and failover. A stale fixed address can cause intermittent failures. With a static mapping, remember that TLS certificates are still validated against the domain; pointing it to a different address doesn’t give an arbitrary server the right identity. After removing a test mapping, account for app and system caches. Otherwise the next request may still use the old result, making it look like the change didn’t take effect.

DNS Strategy Is Not Routing Strategy

dns.queryStrategy and routing.domainStrategy have similar names but do different things. The former mainly limits which address families the built-in resolver queries; the latter determines whether routing resolves a domain to an address for matching. Some outbounds and socket settings have their own domain-handling options, which are also separate. Changing built-in DNS queries to IPv4 does not mean every app, the system resolver, and remote proxies will use only IPv4. To diagnose an address-family issue, check local DNS results, the actual connection destination, and the final outbound separately.

SettingPrimary purposeDoes not mean
dns.serversSelects built-in DNS serversEvery app uses these servers
dns.queryStrategyAddress-family strategy for built-in queriesThe entire device disables the other address family
routing.domainStrategyWhether routing resolves domains to match addressesA DNS server has been specified
dns.hostsStatic mapping for the built-in resolverThe operating system’s hosts file has been changed

When configuring encrypted DNS, make sure the resolver itself is reachable. If the resolver’s hostname depends on the same DNS path that isn’t working yet, you may create a loop. Startup can also stall if the proxy server’s hostname depends on DNS that must go through that same proxy. Work out how to resolve the server’s hostname initially, which outbound the resolver connection uses, and whether there’s a fallback you can understand. More complex settings are not automatically more reliable.

Troubleshoot in three steps: confirm the domain resolves, check that the result is a usable address, then verify that the connection uses the expected outbound. If DNS succeeds but the connection times out, investigate the network path; don’t label every timeout a DNS error. If resolution fails, disabling certificate verification won’t help. When comparing DNS results, record the time and address family since dynamic routing can produce different answers. Keep a change only when you can reproduce a clear connection between it and the failure.

geosite data in routing describes domain collections; geoip data describes address collections. Neither is a DNS cache, and neither automatically resolves a domain to the right address. Loading rule data, resolving a domain, and connecting through an outbound are three separate checks. Transparent proxying, virtual addresses, and DNS routing add more moving parts. Start with a basic local proxy setup, then add these features one at a time. For routers or side gateways, see Xray Gateway Deployment Overview rather than reusing desktop listener settings.

policy: Policies, Logs, and Statistics

How user-level policies are linked

policy defines connection behavior and statistics-related settings. It is not another routing table or an app-specific bandwidth limiter. The levels object organizes settings by user level; JSON keys are strings, while level in a protocol user entry associates that user with a level. Levels matter only when the protocol and configuration path support them. An inbound tag is not a level name. If you don’t have a specific need, start with the core’s default policies instead of treating unfamiliar settings as general-purpose optimizations.

This example shows connection time settings for level zero. Values are in seconds and illustrate the structure; they are not recommended defaults for every network. handshake sets a timeout for the relevant connection handshake stage; connIdle applies to idle connections; uplinkOnly and downlinkOnly affect how long a connection waits after entering a one-way state. The protocols and stages affected by each parameter depend on the current core implementation. These settings don’t replace server health checks, and increasing them alone won’t restore an unreachable server.

{
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5
      }
    }
  }
}

The trade-offs of time settings

A very short idle timeout may end a connection during a temporary pause. A very long one may leave a dead connection holding resources for longer. The effect on long-lived apps also depends on app heartbeats, server timeouts, and network devices. When troubleshooting a disconnect, record how long it takes from the last valid data to the disconnect and check whether the interval repeats. Extending every timeout after a single disconnect won’t identify the cause and may delay cleanup of connections that have actually failed.

Handshake timeouts also need to be considered in context. Network congestion, a slow server response, and mismatched authentication can all leave a handshake incomplete, but require different fixes. Increasing the wait only widens the tolerance window; it won’t fix incorrect user parameters or an incompatible transport. Use runtime logs to identify the failing stage before making a small adjustment. If the result is just “fails after waiting longer” instead of “fails quickly,” restore the original value and check the address, protocol, and security settings.

Choose an appropriate log level first

log is a top-level setting; loglevel can be set to a level supported by the current core. For everyday use, warning or error is usually enough. For troubleshooting, temporarily switch to info, then use debug briefly if you need more detail. More verbose logs won’t speed up a connection and may include destination addresses, local paths, or user identifiers. Remove subscription URLs, authentication details, and other personal information before sharing logs. The original error and its sequence are more useful than a screenshot of the final “failed” message alone.

{
  "log": {
    "loglevel": "info"
  }
}

If you set explicit paths for access and error logs, make sure the runtime account can write to them, the directory exists, and there’s enough disk space. A desktop client may handle logging itself, so manually set paths might not appear in its log panel. Relative paths are based on the process working directory, not the folder containing the editor. To avoid accumulating excessive records, lower the log level after troubleshooting and delete files containing access records according to your retention needs.

Statistics switches are not a monitoring dashboard

stats, statistics options under policy, and the query capabilities of api work together. Enabling just one switch won’t automatically display a chart in the desktop client. Inbound, outbound, and user statistics have different scopes; user statistics may also depend on a user identifier field in the protocol. If your client already provides traffic statistics, use its interface rather than exposing a management API just to display numbers. Counters reflect a particular measurement point; they may not match ISP-billed usage or the actual size of files used by apps.

To evaluate an “optimization,” use the same app, server, and test destination. Record the baseline first, then change only one policy setting. Throughput depends on node load, route quality, protocol, app concurrency, and device performance; a few time values in policy don’t determine the speed of the whole connection. If the connection is stable but downloads are slow, use A Layered Troubleshooting Guide to Slow Speeds to distinguish node, network, and local issues instead of repeatedly shortening timeouts or enabling every statistics option.

Validate, Migrate, and Roll Back Configuration

Keep reproducible change records

Before editing, save the current working setup and write down the specific problem you’re trying to solve. Examples: “an app isn’t using the proxy,” “a domain uses the wrong outbound,” or “DNS fails after switching networks.” These relate to the entry point, routing, and DNS respectively; don’t treat them all by reimporting every subscription. Record the client, the core actually in use, the traffic capture method, and the fields you changed. There’s no need to copy authentication details. Keep in mind that saved copies may contain private server information, so don’t upload them publicly.

Validate manually edited JSON at three levels: syntax, core compatibility, and runtime behavior. At the syntax level, check brackets, quotes, commas, and data types. At the core level, check supported fields, tag references, and loaded resources. At the behavior level, confirm that the real app enters through the expected inbound and uses the right outbound. Passing the first two checks doesn’t guarantee the third. Conversely, one webpage opening doesn’t prove the config is correct; it may have connected directly or used a cache. Observe the actual request path before drawing conclusions.

Start and test in a consistent order

Close duplicate client instances or test processes first to avoid local port conflicts. Load the config and check the startup log for syntax errors, unknown fields, missing rule data, or listener failures before sending a clear test request from an app. If startup fails, don’t keep changing routing modes. If startup succeeds but there’s no access log entry, check the app’s proxy entry point. If a request is logged but uses the wrong tag, check the routing order. This process separates “no request received” from “request received and handled incorrectly.”

  1. Keep the original config. In a copy, change just one group of fields and note what you expect to happen.
  2. Load the copy and check parsing, tag references, rule data, and listening ports.
  3. Send a request to a fixed test destination and check the destination and outbound tag in the logs.
  4. Test a destination that should be unaffected to make sure the exception rule hasn’t widened its scope.
  5. Restore your usual apps and log level, then check whether the change persists after restarting the client.

Choose test destinations that cover your actual needs; there’s no need to test every website at once. Include at least one destination expected to connect directly and one expected to use a remote outbound. Add an internal service if you need local network access. Don’t rely only on whether a page opens: browser caches, existing connections, and app-specific proxies can all affect the result. After changing routing or DNS, close and reopen the relevant app so new requests use the updated config. If you clear a cache, note what you cleared so the comparison remains meaningful.

Tell recoverable errors from compatibility issues

For an “unknown field” error, confirm the config format matches the current core before checking field spelling and nesting. For “address already in use,” look for duplicate processes and port conflicts rather than changing servers. For certificate errors, check the system time, server name, and certificate chain. If rule data fails to load, check whether the client manages the files correctly. Error messages vary by core, but the same principle applies: identify the failing stage, then change the corresponding field.

SymptomCheck firstDon’t change first
Core fails to startJSON syntax, field compatibility, listening portWebsite domain rules
No app requests in the access logSystem proxy, app entry point, traffic capture scopeRemote authentication user
Unexpected outboundRule order, destination format, tag referencesTLS certificate verification
Changes disappear after restartGenerated config vs. persistent settingsNetwork route

When moving from v2rayN to v2rayNG, start by migrating supported server profiles, then set up per-app proxy and routing again on mobile. Desktop system proxy settings, file paths, and window preferences aren’t portable core configuration. For a move to v2flyNG, also check whether the V2Fly core supports the protocols and transports your servers require. A successful subscription import only shows that the format was recognized; it doesn’t guarantee that every entry will run on the target core. Revalidate the setup using the same checks after migration.

If the client can export share links, full configurations, or backups, first check what each export is for. A share link usually describes one server and won’t preserve routing, DNS, or system settings. A backup may include more app data, so restoring it can affect more settings. Remove server credentials and subscription details before sharing an export for troubleshooting. If the issue is only about routing, share the routing section, relevant tag definitions, and redacted logs instead of passing a full backup around.

Rolling back means more than pasting the old text back. Restore the system proxy, previous core selection, log level, and app entry point too. Also undo any local network listener or management API you added. Restart after the rollback and verify the original test destination to confirm the baseline is restored before trying again. For everyday setup, return to the Getting Started guide and follow the main workflow. To switch device platforms, use the client download page. A clearly documented working baseline is easier to maintain than a collection of configuration snippets from unknown sources.

Download V2Ray Client