CONFIGURATION REFERENCE

V2Ray Config File Reference.

JSON structure, inbounds and outbounds, routing rules, DNS, and connection policies.

Use this page to look up fields and troubleshoot configuration issues—not to set up your first connection. If you haven't imported a subscription yet, start with the Getting Started guide to configure your client. If you're already connected and want to understand the generated config or adjust routing, jump to the relevant section. For desktop, we recommend v2rayN; see the client download page.

Covers common V2Fly / Xray config structures. Examples note their intended use and compatibility limits; don't combine core-specific fields from different implementations.

01 / STRUCTURE

JSON structure and configuration scope

Check which config file is actually loaded

The config file tells the core how to accept connections, choose an outbound, and resolve domain names. It does not contain every client setting. v2rayN manages subscription groups, UI preferences, and system proxy options; the core typically reads the runtime config generated by the client. Before troubleshooting, check which core is selected, its working directory, and the config path recorded in the logs. There may be several files named config.json on disk. Editing a copy that isn't loaded won't change how current connections behave.

A JSON config is usually a top-level object, with modules identified by their keys. inbounds and outbounds are arrays because one process can accept connections on multiple local entry points and use multiple exits. routing, dns, and policy are objects. Don't infer nesting from the order of options in the UI. Even a correctly spelled field may be ignored if it's in the wrong place—or cause the core to reject the config.

Understand how the modules fit together

This basic config routes a local SOCKS inbound to a direct outbound; it doesn't include a remote proxy server. Use it to confirm that the local listener, request handling, and direct connection work as a complete path. An app must use this SOCKS endpoint for its traffic to reach the core. The two tag values are internal names you can choose yourself, but update every reference if you change them. A tag is not a protocol name and doesn't create an outbound automatically.

{
  "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": {}
    }
  ]
}

Follow the request through the config: the app connects to the local listener, the inbound interprets the SOCKS request, routing chooses an outbound, and the outbound connects to the destination. Without explicit routing rules, the first outbound commonly acts as the default. Adding a second outbound won't split traffic automatically; a rule or other explicit mechanism must select it. Understanding this default behavior is more useful than memorizing a long list of options.

Syntax and behavior are different checks

Standard JSON doesn't allow comments, trailing commas, or single-quoted strings. Write port numbers as numbers and boolean switches as true or false, not as quoted text. Backslashes are escape characters in strings, so paths and regular expressions are easy to get wrong. If an editor can format the file, its basic structure may be valid—but that doesn't prove the core supports a field or that your local settings match the remote server.

Most code snippets below show top-level modules to explain how fields relate; they aren't all standalone configs. When combining snippets, replace the existing module with the same name instead of adding duplicate keys to one object. Parsers may handle duplicate keys differently, which can make a file look updated even when runtime behavior hasn't changed. Keep commas and brackets correct inside arrays, too, so one outbound's settings don't end up inside another.

Check compatibility against the core you're actually running, not just the client name. A client may let you switch cores, and different builds may support different features. When looking up a field, note its module, full path, value type, and supported core. If an unknown field causes trouble, reduce the config to a minimal example. Avoid trial-and-error by adding switches found online; reproduce the issue with one request and trace it through the inbound, routing, and outbound to pinpoint the difference.

02 / INBOUNDS

inbounds: listener addresses and inbound protocols

The listen address determines who can reach an inbound

An inbound defines a local endpoint that can accept connections. listen sets the bind address, port the port, and protocol how incoming data is interpreted. For apps on the same device, bind to 127.0.0.1 by default. Binding to all interfaces may let other devices on your LAN reach the port. Actual access also depends on the firewall and network, so don't widen the listener without considering access controls.

The address and port must work together. The proxy address in an app must point to the core running on that device. A loopback address on a phone points back to the phone, not a nearby computer. Listening on the IPv4 loopback address doesn't mean the core is also listening on IPv6 loopback. If an app fails with localhost but works with a numeric loopback address, check name resolution and the listener's address family before blaming the remote server.

What SOCKS and HTTP inbounds accept

A SOCKS inbound is for apps that support SOCKS proxies. An HTTP inbound accepts HTTP proxy requests, including tunnels created with CONNECT. Neither one rewrites browser traffic into a particular web protocol or enables the system proxy automatically. Set the app's proxy type to match the inbound. Don't enter a SOCKS port in a field that sends HTTP proxy requests and expect the core to figure it out. The example below keeps the two listeners separate for easier testing.

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

Enabling UDP for SOCKS means the inbound can accept the relevant requests; it doesn't guarantee that every UDP app will use it. The app must support SOCKS UDP, and the outbound and remote service must support the required forwarding. System proxy settings don't cover every app or arbitrary UDP traffic, so a working website and a failing real-time app don't necessarily mean the node is down. First check how the app connects, then verify that the rest of the path supports its transport.

Sniffing can add destination details

Some routing rules need a domain name, but an app may give the core only a destination IP. Inbound sniffing can try to read an HTTP hostname or the server name in a TLS handshake and provide routing with more information. It doesn't decrypt content and can't extract a domain from every connection. Sniffing protocol lists and options vary by core. Check the docs for the core you're using before adding fields; don't paste in a whole example written for a different one.

SettingMeaningWhat to check
listenLocal bind addressUse a loopback address for apps on this device
portInbound listen portMust match the app setting and be available
protocolInbound protocolMust match the proxy protocol sent by the app
tagInternal inbound tagMust match references in routing rules

Sniffing also raises a distinction between using a domain for routing and rewriting the connection destination. Some cores offer an option that affects routing only, useful when you want to preserve the original destination, but this field isn't supported by every core. When troubleshooting, turn off extra sniffing and get a clear domain or IP request working first, then enable options one at a time. Otherwise, the original destination and sniffed hostname can get mixed together, making routing logs harder to interpret.

Inbound startup failures often come down to a port conflict, a missing bind address, or insufficient permissions. Two clients on the same device usually can't listen on the same address and port at once. Don't widen the listener just to work around a conflict. Exit the client you aren't using, or choose a different port and update the app's proxy settings to match. See log troubleshooting for a fuller diagnostic workflow, or the Getting Started guide for system proxy setup.

03 / OUTBOUNDS

outbounds: protocols and transport settings

Outbound tags and default order

An outbound sends requests from the core to a destination or remote proxy server. protocol selects the outbound protocol, settings holds its parameters, and streamSettings configures the underlying transport and transport security. A tag is only an internal reference: an outbound named proxy doesn't become a proxy just because of its name. It must exist, have valid settings, and be selected by a routing rule or occupy the default position to handle requests.

A basic config often includes proxy, direct, and block outbounds. The freedom direct outbound connects to the destination from the device running the core; blackhole terminates matching requests. “Direct” doesn't bypass the local network stack or necessarily avoid a system-level virtual adapter. If TUN or another system-wide interception method is enabled, make sure the core's own connections have a bypass route, or they may loop back into the core.

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

Check node identity separately from transport security

A remote proxy outbound usually includes a server address, port, and protocol-specific credentials. VMess, VLESS, Trojan, and Shadowsocks use different identity fields; changing only protocol won't convert a config to another protocol. One benefit of importing a subscription is that the client can generate the right fields for each node type. When editing by hand, check every value against the service provider's details. Don't infer encryption, transport, or security settings from how a share link looks.

This snippet shows the usual structure of a Trojan outbound. proxy.example.com and the sample password are illustrative only and won't connect to a real service. Replace them with the address, port, password, and certificate name provided by your service. This serves a different purpose from the direct-outbound example above; don't stack both complete top-level objects together. Before using it, confirm that your core supports Trojan and the specified transport fields.

{
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "trojan",
      "settings": {
        "servers": [
          {
            "address": "proxy.example.com",
            "port": 443,
            "password": "your-password"
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "serverName": "proxy.example.com",
          "allowInsecure": false
        }
      }
    }
  ]
}

Transport determines how data is carried, TLS determines how transport security is negotiated, and the proxy protocol defines how the two sides exchange proxy requests. Match each layer to the remote server. Check which transport it accepts, whether paths are case-sensitive, and whether a specific hostname is required. Changing one layer on the client won't adapt the server. A mismatch can cause an immediate disconnect, a handshake failure, or a connection that hangs until it times out.

The certificate name and server address are separate fields

The server address locates the remote host; TLS serverName supplies the name used for the handshake and certificate verification. They may differ in some deployments. If you replace a domain with a numeric IP, you may still need to keep the server name covered by the certificate. Don't leave certificate verification disabled to hide a name mismatch. Check the system clock, certificate coverage, and configured name first, then investigate the certificate chain or network connection.

Connection reuse, low-level sockets, and advanced transport options are later optimizations, not prerequisites for a first working config. Get a node working with the defaults, then check for a specific issue such as repeated connections or dropped long-lived sessions and adjust only what the core supports. Applying a batch of “optimization” settings changes too many variables to reproduce failures. In particular, don't change protocol identity fields to improve a speed test; they aren't performance switches.

If an outbound starts but connections fail, distinguish DNS resolution failures from an unreachable remote port, a failed TLS handshake, and protocol authentication errors. There's little point checking a later layer until the earlier one works. For certificate issues, see the TLS handshake and SNI troubleshooting notes. If you only need to install or switch to a graphical client, choose one from the client download page; you don't need to find a separate core download.

04 / ROUTING

routing: rule order and traffic routing

Rules are checked in order, not automatically sorted by specificity

The routing module compares connection details against rules, then sends matching requests to the specified outbound. A common rule uses type: "field" to match a domain, destination IP, port, network type, or inbound tag. Rules are checked from top to bottom, and the first match usually determines the result. Put specific exceptions before broader rules. A rule that matches every connection at the top will make more precise rules below it ineffective, even if they're written correctly.

Different condition fields in one rule usually all have to match; multiple candidates in the same array usually mean any one can match. For example, a domain array can list several sites, while a port condition also requires the request to use a matching port. Don't pile unrelated conditions into one rule expecting them to work as alternatives. Put each rule into plain English first—what inbound, which destination, what network, and which outbound—then check that the config says the same thing.

Domains, exact matches, and IP ranges

Use domain: for a domain and its subdomains, full: for an exact domain match, and regexp: for regular-expression matching. Routing rules don't need webpage paths because proxy connections typically don't expose the full URL. A URL with a scheme and path won't work as a domain condition. For a known, fixed hostname, use a simple match; use a regular expression only when you need pattern matching. Simpler rules are easier to maintain and escape correctly.

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:telemetry.example.com"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "domain": ["domain:example.com"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": [
          "127.0.0.0/8",
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16",
          "::1/128",
          "fc00::/7"
        ],
        "outboundTag": "direct"
      }
    ]
  }
}

This snippet assumes outbounds tagged block and direct already exist. The first rule is an exception within the second rule's broader scope, so it must come first. The private-range example covers only the listed addresses; it doesn't include every special-use network. Add other ranges, such as link-local addresses, if your network needs them. Requests that match no rule continue to use the default outbound. A final direct rule doesn't automatically send every remaining request direct.

domainStrategy controls when routing resolves domain names

AsIs uses the destination information already available and doesn't proactively resolve a domain to match IP conditions. With IPIfNonMatch, the core may resolve the domain and try IP rules if no domain rule matches. IPOnDemand may trigger resolution when a rule needs IP information. These options affect routing decisions; they aren't DNS server lists and don't replace resolution needed to connect to an outbound. Changing the strategy can affect both DNS requests and routing results.

If an app gives the core only an IP address, adding domain rules can't restore the original hostname. Check how the app uses the proxy, where DNS resolution happens, and whether an appropriate sniffing option is available. Conversely, if the destination is already a domain, don't assume private-IP rules will match first; that depends on whether and when the current strategy resolves it. When troubleshooting, note whether the inbound received a domain or an IP. That's often more useful than looking only at the browser's address bar.

geosite and geoip refer to classification data; they don't query the network in real time. The core must be able to find the relevant data files, and the category names must exist. Missing data can prevent startup, and category updates may change results for the same config. Test a rule with one explicit domain or IP range before using a category reference. This separates rule syntax problems from data-file problems without having to guess at both.

When maintaining rules, document each rule's purpose and why it changed outside the config file; standard JSON doesn't support comments. Before removing an old rule, check whether later rules depend on its position, especially inbound-tag routing and catch-all rules. See the custom routing rules notes for matching syntax and precedence examples. For the difference between system proxy and global mode, see the FAQ on client behavior.

05 / DNS

dns: resolvers and query paths

First identify which layer is resolving names

A single visit may involve the app's own resolver, the operating system, the core's built-in DNS, and resolution on the remote proxy. The dns section describes how the core handles DNS; it doesn't automatically replace DNS settings for every app on the device. A browser may use its own encrypted DNS, or an app may resolve a hostname to an IP before sending a proxy request. To tell whether a DNS setting is taking effect, first identify which component is making the query. The DNS server shown in system settings isn't enough.

The core's DNS behavior also depends on routing strategy, outbound settings, and how the client generates its config. Some requests keep the domain and send it to the remote server; others are resolved locally to match IP rules. Both can happen at once. A webpage loading without a corresponding query in the logs doesn't necessarily mean the config is ineffective. Check whether the inbound received a domain or IP and which outbound handled the request, then work out where resolution should have happened.

servers, hosts, and address families

servers specifies DNS resolvers. Depending on core support, entries can be simple addresses or objects with domain conditions. hosts maps specific names to addresses, useful for controlled testing or fixed internal names. This example uses a numeric resolver address and maps a reserved example domain to a documentation-only address; the mapping isn't a live website. Using a numeric address reduces the chance that the core needs DNS just to find its DNS server at startup.

{
  "dns": {
    "hosts": {
      "lab.example.com": "192.0.2.10"
    },
    "servers": [
      "1.1.1.1",
      "8.8.8.8"
    ],
    "queryStrategy": "UseIP"
  }
}

UseIP allows queries for both IPv4 and IPv6 results. Check the documentation for your core for other supported query strategies. Restricting results to IPv4 can help narrow down some dual-stack issues, but it shouldn't be treated as a universal, permanent optimization. If your network needs IPv6, forcing IPv4 can rule out a working path. Having an IPv6 address doesn't prove that the network provides reliable end-to-end IPv6 connectivity; check address configuration and actual reachability separately.

A DNS server list isn't a list of connection outbounds

Listing multiple servers doesn't mean the core always tries them strictly in order as fallbacks. Domain conditions, caching, fallback behavior, and core implementation can all affect which resolver handles a query. For split DNS, specify which names go to which resolver and what happens to unmatched names. Domain conditions on DNS server objects may look like routing rules, but they work at a different layer: one selects a resolver, the other selects a connection outbound. Don't treat them as the same rule set just because the syntax looks similar.

Encrypted DNS protects traffic between the client and the resolver, but it doesn't guarantee that the connection uses the outbound you expect. Check how the current core sends those queries and whether the client applies separate DNS routing. If a DNS resolver is addressed by hostname and resolving that hostname depends on the resolver itself, you can create a startup dependency loop. Start with a numeric address you can reach reliably, then move toward the intended setup one step at a time.

SymptomCheck firstDon't assume
Domain fails, IP worksName lookup and result address familyIt's not necessarily a remote protocol error
Some sites time out intermittentlyCache, dual-stack paths, and lookup latencyNot necessarily a problem with every node
Domain rule doesn't matchWhether the inbound received only an IPIt's not necessarily a typo in the domain
Old result persists after a changeCaches at the app and OS layersIt doesn't necessarily mean the config wasn't loaded

Caching is an important variable in DNS troubleshooting. Reloading a client may clear only the core's cache; browsers and the operating system may still have old records. Existing connections won't be rebuilt just because a DNS result changes. To test a config, use a new connection, and if needed close and reopen the relevant app. Record the time and query name. Don't refresh repeatedly and mix results from different cache layers, then conclude that a resolver never took effect.

DNS timeouts can also result from routing loops. For example, a query may go to a proxy outbound that needs DNS itself to connect, or a virtual adapter may feed the core's own traffic back into it. Adding more public resolvers usually won't fix this. Trace the connection dependencies instead: check the paths to the resolver and proxy server, and the route for the core's own connections. Complex split DNS is reliable only when each prerequisite works independently.

When changing DNS in v2rayN or v2rayNG, start with the client's built-in settings, then check the generated config so that UI options and custom snippets don't overwrite one another. For a first connection, keeping the number of variables low is usually best. To test a specific routing requirement, use one fixed domain and observe resolution and access separately. See the FAQ for common questions about system proxy coverage and apps that resolve names independently.

06 / POLICY

policy: timeouts, user levels, and statistics

Policy isn't node priority

policy sets connection resource and statistics behavior, such as handshake timeouts, idle timeouts, half-close retention, and traffic counters. It doesn't choose an outbound by node name or replace routing rules. If a connection is unstable, first confirm that the outbound can connect; then check whether a policy timeout is involved. Increasing every timeout just makes some errors take longer to show up.

Numbers in levels are user-level keys, and JSON object keys still need quotation marks. A level isn't an OS permission, and a higher number doesn't mean faster. The protocol user's level is associated with the corresponding policy level; if none is set, a default level is commonly used. Many setups use a local inbound without authentication or multiple user levels, so defining a long list of high-numbered policies may not change anything.

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

Understand timeouts by connection stage

The time fields in the example are usually measured in seconds; check the docs for your core. handshake limits the time allowed for the handshake, while connIdle controls how long an inactive connection is kept open. uplinkOnly and downlinkOnly control how long one direction remains open after the other side closes. They apply to different stages, so one field can't explain every connection drop.

A handshake timeout that's too short can cut off a connection before it completes during high latency or brief congestion; one that's too long delays failure feedback. A short idle timeout can affect apps that wait a long time for occasional notifications, while a long one holds more connection resources. Reproduce the issue in a specific app and check whether data really stopped before the disconnect. Then adjust relevant fields slightly. Don't blame an idle timeout for a transfer that was still carrying data.

Half-closes are common in network connections. One side stopping transmission doesn't always mean the other side has finished responding, so half-close timeouts can affect how some requests finish. Adjust them only when logs and app behavior point to that stage. If the connection never passed authentication or the server terminated the session, changing client-side timeout fields won't fix a protocol mismatch or make the remote server keep the connection open.

Statistics require an end-to-end collection path

Enabling upload or download statistics for users doesn't mean the client will immediately show those numbers. The statistics module, user identifiers, API service, and client-side reading logic all need to work together. A traffic figure in the UI may come from a virtual adapter or another source, so seeing a number doesn't prove a policy field is active. The example keeps statistics disabled and shows only where the fields belong, without adding API or collection settings.

System-wide and per-user statistics have different levels of detail. Inbound statistics may aggregate several kinds of requests on the same listener; user statistics depend on user identities the protocol can recognize. To measure traffic by app, enabling user statistics alone may not be enough. Consider how the inbound is split, how apps connect, and whether the client can display the data. Don't expose a control API on your LAN just to get a number in the UI; restrict its listening scope to what you actually need.

If startup fails after enabling statistics or another advanced module, check for missing supporting modules, fields unsupported by the current core, and duplicate config generated by the client. Don't keep two independent API definitions and try to fix every error by changing ports. Revert to a minimal config with connection policies only, then add statistics one piece at a time. This separates connection handling from monitoring and keeps the collection setup from disrupting normal use.

Don't judge a policy change by a single latency test. Short requests, long-lived connections, and sustained transfers are affected at different stages, so one test can't cover every behavior. Check that a new connection completes, an idle connection resumes, and ongoing transfers aren't cut off; restore the usual log level afterward. If you can't explain what a value fixes, there's no reason to keep it. A simpler config is easier to check when moving to another core.

07 / CLIENT INTEGRATION

How clients generate and manage configuration

How UI options become runtime settings

v2rayN, v2rayNG, and v2flyNG all separate the graphical interface from the core that handles connections. The client stores nodes, subscriptions, and user preferences, then converts the current selections into a runtime config for the core. This reduces the need to edit fields by hand, but the generated file may not be the lasting source of configuration. Switching nodes, restarting the core, changing routing mode, or updating a subscription may regenerate it. Direct edits to a runtime file can be overwritten the next time the client runs an update.

For desktop, we recommend v2rayN; choose its graphical features based on the core and your system. On Android, you can use v2rayNG with the Xray core, or v2flyNG as an alternative with the V2Fly core. Custom config workflows vary between clients, and UI labels may change, so follow the version you have installed. If an option is missing, don't assume another client's complete workflow applies. First confirm which app and core you're using.

Subscriptions, nodes, and full configs are different

A subscription URL usually points to a refreshable set of node data. A node share link describes one server's protocol and connection settings. A full JSON config may include inbounds, outbounds, DNS, and routing. They aren't interchangeable import formats. Pasting a full config into a field that accepts only node links—or putting a subscription URL in an outbound server address—won't work. Identify what the content is for before importing it; that's more useful than trying every button after an import fails.

Config sourcePrimary purposeWhat to watch for
Subscription groupFetch and update multiple nodesUpdates may overwrite manual edits to nodes
Client settingsManage inbounds, routing, and runtime preferencesCheck whether changes require a core restart
Runtime configLoaded by the current core processMay be overwritten the next time it's generated
Custom full configTakes over modules as supported by the clientCheck which UI settings still apply

Some clients let you import a full config; others support partial custom settings or a routing-rule manager. First find out whether the client replaces the whole config, merges an object, or only appends array items. The same snippet can behave differently under each method: replacing the outbound array can remove existing exits, while appended routing rules may be shadowed by earlier rules. Don't stop at confirming that the file was saved; check the generated config for the expected tags and order.

System proxy and TUN are different ways to route traffic

A system proxy tells apps that follow OS settings where to send proxy traffic; it doesn't cover every program automatically. TUN uses a virtual network interface and related routes to pass traffic through the client. Both may use the same core, but they're not the same JSON switch. The client usually manages TUN permissions, the virtual interface, and system routes. Adding an unfamiliar protocol name to a standard inbound array doesn't enable system-wide traffic interception.

When multiple proxy tools run at once, check which one changed the system proxy, took over the default route, and manages DNS. After one client exits, another may still hold a port or leave system settings in place. If apps lose internet access after you close a client, first check whether the system proxy still points to a stopped local listener. Then check the virtual adapter and system routes instead of immediately reinstalling everything.

Verify the config source with one small change

To see whether a custom setting is being applied, choose an easy-to-observe change that won't expose service details—for example, change the local inbound port and update a test app to match, or add a clear internal tag to an existing outbound. Restart the relevant connection and check the listener and logs. If the generated config is unchanged, focus on the client's config source or merge behavior rather than the remote network. If the generated config is correct, move on to connection-level troubleshooting.

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "inboundTag": ["http-in"],
        "outboundTag": "direct"
      }
    ]
  }
}

This partial snippet demonstrates routing by inbound tag and assumes an inbound named http-in and an outbound named direct already exist. It doesn't create either one and shouldn't replace existing routing before you review it. When maintaining custom settings, keep a separate note of their purpose, supported core, and required tags. Don't put subscription URLs or passwords in public notes. Before moving to another device, export necessary settings and check how the client stores sensitive data.

For your first connection, follow the Getting Started guide to import a subscription, choose a node, and test it before taking over the full config. For an overview of v2rayN's settings and log panels, see v2rayN main window overview. This page explains fields and boundaries; use the current client UI as your guide, and don't replace a stable default config just to make it match an example.

08 / DIAGNOSTICS

Log checks and configuration rollback

First confirm that the core started successfully

The first key question when troubleshooting a config is whether the core stays running. If it exits immediately after startup, read the earliest error before testing a webpage. Common causes include invalid JSON, an unsupported protocol or field, a listener conflict, or a missing data file. Later “connection refused” messages may just be a consequence of the core exiting. Fixing those app-side symptoms won't fix the original startup error.

If a log shows a file path, confirm that it belongs to the current running instance. Multiple clients or leftover processes may write separate logs, and an old window may not show the latest test. Note a specific restart time, keep only the relevant lines from the current instance, and check that the logged config path matches the file you edited. If the client shows startup details, use them to identify the module first, then locate it in the config instead of replacing every similar-looking field.

Temporarily increase log detail

To trace a connection, temporarily set the log level to info if your core supports it. Use a more detailed debug level only after narrowing down the issue. Logs may contain destination domains, addresses, or user information, so remove sensitive details before sharing them. More logging doesn't always help—especially when several apps are online and normal requests bury the failed one. For testing, use one app and one specific destination.

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

This snippet replaces only the top-level log module; it isn't a complete standalone config. If the log detail doesn't change, check whether a core restart is required and whether the client overrides the log setting. Restore the usual level when you're done to avoid recording unnecessary browsing details. If writing logs to a file, check that the directory exists and the process can write to it. A log file creation error is separate from a connection problem.

Check each layer: inbound, resolution, routing, outbound

Once the core is running, check whether the test request reaches the expected inbound. If there's no record, check the app's proxy type, address, port, and system proxy settings. If the request arrived with the wrong destination, check the app's resolution method and sniffing. If the destination is right but the wrong outbound was selected, check rule order, tags, and domain strategy. Only investigate server resolution, connection, TLS, and protocol authentication after confirming the expected outbound was selected.

StageCommon causesNext step
Config loadingBrackets, field types, core compatibilityRestore a minimal config and add modules one at a time
Local listenerPort in use, address, or permissionsIdentify the current listener and check the app's endpoint
RoutingConditions don't match or an earlier rule takes precedenceTest one specific destination and check the outbound tag
Remote connectionResolution, reachability, certificate, or credentialsCheck settings for each connection stage separately

A “timeout” means only that a stage didn't finish within the allotted time; it doesn't identify the cause. A timeout resolving the server hostname, connecting to the destination port, and negotiating the protocol call for different checks. If the logs show only the final message, temporarily increase detail and reproduce the issue once to find the earlier stage. Don't change DNS, transport, and credentials all at once; even if that happens to fix it, you won't know which change helped.

When verifying a fix, check both a new request and the app's actual behavior. An existing long-lived connection may keep using the old outbound, and a browser cache can make a page look fine without sending a new request. Close the relevant connections and test again. Confirm the request used the expected inbound and outbound, and that the app's other required requests also work. A real connection test can help check server reachability, but one successful run doesn't prove that every domain, protocol, and app follows the intended routing.

Roll back to a state you can explain

If several changes have made the cause unclear, return to the latest config that starts successfully and keep only one inbound, one confirmed working outbound, and the fewest necessary rules. Restore DNS, routing, policy, and advanced features one at a time, testing the same destination after each change. This isn't a recommendation to leave the config stripped down permanently; it breaks a complex problem into observable steps. Add features only when each change has a clear, testable result.

For more detail on startup error categories, see How to diagnose config errors from Xray logs. If the core starts but you're unsure whether the proxy is working, follow the first connection test notes. Still have questions? Check the FAQ by symptom. The goal of a config reference is to make every field's purpose clear, not to make the file longer.

Download V2Ray clients