CONFIG.JSON / FIELD REFERENCE
V2Ray Configuration Reference
JSON structure, traffic inbounds and outbounds, rule matching, DNS, and connection policies.
Use this page as a field-by-field reference, not as a first-connection guide. If you still need to install a client, import a subscription, or configure your system proxy, follow the Getting Started guide to establish a working connection first, then return here to understand the configuration. Use v2rayN on desktop; Android users can choose v2rayNG or v2flyNG. Find installation links on the client downloads page.
Scope of the examples: This guide uses the traditional JSON configuration format throughout. The complete baseline includes only a local SOCKS inbound and a direct outbound, so you can verify how the fields fit together; it does not include a remote node. All other code samples are explicitly marked as field snippets and cannot run as complete configurations. V2Fly and Xray do not support exactly the same extension fields, so check compatibility with the core you actually run.
01 / STRUCTURE
JSON Structure and Configuration Scope
A configuration describes how objects relate to one another; the core connects them into a processing chain. An application sends a request to an inbound, routing checks its destination and source, then selects an outbound to establish the connection. DNS can resolve domain names, policies govern connection resources, and logs record what happens. These sections are not script steps that run in the order they appear in the file: rearranging them usually does not change processing order, but the order of items within arrays can directly affect the result.
Identify the Configuration Actually in Use
Graphical clients such as v2rayN typically generate the runtime configuration from the selected node, routing, and DNS settings. Subscription groups, window layout, and speed-test settings belong to the client, not to the core's top-level fields. A file named config.json on disk is not necessarily the one currently in use. Check the client's runtime information, configuration viewer, or startup logs to confirm which core is running and where its configuration came from before making changes.
Editing a generated file by hand often affects only one run. When you switch nodes, update a subscription, save settings, or restart the core, the client may regenerate the file and overwrite your changes. For routine maintenance, use the client's routing, DNS, and custom-configuration options. If you need a fully custom configuration, first find out whether that mode still imports rules generated by the client; do not assume the two configurations will merge automatically.
Start with a Minimal, Standalone Configuration
The complete baseline below listens on a SOCKS port bound to the local loopback address and connects all traffic directly to its destination. Use it to check whether the core can load the configuration, the local port is reachable, and the tags line up. If the port is already in use, choose an available one. This example does not establish a remote proxy connection: test traffic still follows the device's own direct network path.
{
"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": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"inboundTag": ["local-socks"],
"outboundTag": "direct"
}
]
}
}
When extending this baseline, add one feature at a time. Start with an outbound that has its own tag, add a rule that references it, then check which requests match. Do not change DNS, enable sniffing, adjust policies, and change the default outbound all at once. The baseline is not meant to be feature-complete; it gives you a known-good state to return to and makes the cause of each change easier to identify.
Objects, Arrays, and Field Types
inbounds and outbounds are arrays that can each contain multiple objects; routing, dns, policy, and log are objects. Strings must use English double quotes. Boolean values must be true or false, and numbers and strings must match the field's expected type. Some port-range fields accept strings; that does not mean every port field accepts either type.
For compatibility across tools, the examples on this page use strict JSON: no comments, trailing commas, or duplicate keys. Some loaders allow extensions, but the fact that an editor can open a file does not mean the current core can load it. In particular, do not paste explanatory text outside quotation marks or copy typographic quotation marks from documentation. An error may point to where a problem was detected even when the missing comma is on the previous line.
| Field | Purpose | What to check |
|---|---|---|
inbounds | Local traffic listener | Listening address, port, and protocol |
outbounds | How destination connections are made | Default behavior and unique tags |
routing | Conditions mapped to outbounds | Rule order and tag references |
dns | Core name-resolution settings | Who resolves names and which route queries take |
policy | Connection and statistics policies | User levels and resource limits |
log | Log output settings | Log level, path, and sensitive data |
These six commonly used fields are not every possible top-level field. Statistics, management interfaces, and other extension modules may require additional configuration; do not add empty sections just to make the structure look complete. First work out whether the issue involves receiving traffic, matching rules, resolving names, or connecting, then consult the relevant section. This is more reliable than trying parameters found in random online examples.
02 / INBOUNDS
inbounds Configuration
An inbound defines where the core receives traffic and how it interprets that traffic. When a local app uses a SOCKS or HTTP proxy, it connects to a proxy inbound, not to the remote node address. A working inbound only shows that the app can reach the core; it does not prove that a remote outbound connection succeeds. Troubleshoot local listening and remote connectivity separately to avoid mistaking a port issue for a node issue.
Listening Address and Port
listen selects the network interface to listen on, while port sets the local service port. If only local apps need access, explicitly using 127.0.0.1 limits who can connect. Listening on an address reachable from all interfaces may expose the port to other devices on the local network; consider the firewall, access scope, and authentication as well. Listening on a public interface does not by itself make sharing work, and it should not be the default fix for a failed connection.
Two inbounds cannot listen on the same address and port for the same transport. The port may already be in use by another client instance or by a core process that did not exit cleanly. If binding fails, first close duplicate instances, then check whether the port is available. If you change the port, update the proxy address in your browser, system settings, or other apps too; otherwise they will keep connecting to the old port.
SOCKS and HTTP Are Not Interchangeable
The snippet below contains inbounds fields that can be added to the top level. The two inbounds use different ports and tags to distinguish their sources. Enabling UDP for SOCKS only means this inbound accepts the relevant requests; it does not mean that every app, outbound, and network path supports UDP. The HTTP inbound is for apps that use the HTTP proxy protocol. A reachable port does not make it a SOCKS proxy.
{
"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 proxy type configured in an app must match the inbound protocol. Sending an HTTP request directly to a SOCKS port can result in a disconnect, protocol error, or empty response; switching nodes will not fix that. Also check whether the app passes the destination domain to the proxy or resolves it locally and sends an IP address. This changes what destination information the core can see and can affect domain-based rules downstream.
Tags and Inbound Identification
tag is an internal configuration name, not a network address, and it does not determine listening behavior. Use stable, concise, descriptive tags—for example, separate names for local SOCKS and local HTTP. The routing field inboundTag must match the tag exactly, including capitalization. If you rename a tag but miss a rule reference, that rule will no longer match the inbound; traffic may match a later rule or use the default outbound instead.
Separate tags help distinguish different app entry points. For example, keep a regular inbound and add another for testing, then use its inbound tag to send test traffic to a specific outbound. This lets you verify a new route without changing global rules for every app. More inbounds also mean more ports and rule relationships to manage. Remove or disable unused ones so stale test paths do not linger in your long-term configuration.
What Sniffing Can—and Cannot—Tell You
Some requests reach the core with only an IP address. Sniffing can attempt to extract domain information from supported protocol data at the start of a connection, such as an HTTP host name or a server name in some TLS handshakes. It does not decrypt traffic, and it cannot identify every encrypted connection. The app's protocol, handshake, and privacy features all affect what is visible, so enabling sniffing does not guarantee that every domain rule will match.
{
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
The snippet above is a set of fields for a single inbound object, not a top-level object. destOverride affects how sniffed information can override the destination; check the core's supported options and behavior. If you only want sniffed information to inform routing without changing the connection destination, confirm whether the core provides a routing-only option for that purpose. Do not assume an extension field behaves the same across different cores.
To verify an inbound, first check the client logs for listening errors, then point an app that explicitly supports proxy settings at that inbound. If the core receives no request, focus on the app's settings and interception scope. If the logs show an incoming request, move on to routing and outbounds. On Android, network interception also depends on system permissions and per-app settings, and its controls differ from desktop system proxy settings.
03 / OUTBOUNDS
outbounds Configuration
An outbound defines how the core connects to a destination: it can connect directly, hand the request to a remote proxy, or reject it according to a rule. The inbound receives traffic; the outbound sends it, and routing connects the two. If a remote node is unavailable, the local inbound may still listen normally. Likewise, a node name in the client interface does not prove that the current core accepted its outbound settings. Verify successful configuration loading and successful connections separately.
The First Outbound Sets the Default
In traditional configurations, traffic not selected by a routing rule is usually sent through the first outbound in the outbounds array. This default matters: simply swapping two objects can change the path for every request that does not match a rule. Do not infer the default outbound from a tag such as direct or proxy. A tag is just a reference; actual behavior depends on the protocol, settings, and position.
The snippet below puts a direct outbound first, followed by a reject outbound. It illustrates the local baseline and how rules reference outbounds; it does not include a remote node. If unmatched requests should go through a remote proxy, redesign the first outbound or add a final fallback rule. Also check whether DNS queries, subscription updates, and local network access would be sent through that outbound too.
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
Direct, Reject, and Remote Protocols
freedom is the outbound protocol for connecting directly to a destination. Direct connections are still subject to the local network, firewall, and destination service status; bypassing a remote node does not guarantee reachability. blackhole rejects or drops matched traffic and is useful for explicit blocking rules. Depending on the protocol and response settings, an app may show a failure, an empty response, or a long wait. When troubleshooting, first check whether the request was routed to a reject outbound.
A remote proxy outbound also requires a server address, port, identity information, and transport and security settings. VMess, VLESS, Trojan, and Shadowsocks use different settings objects; changing only protocol does not convert one node type into another. Generate the corresponding fields from trusted node details or the client's import feature, then inspect the resulting configuration instead of guessing values for an incomplete network snippet.
The node address identifies the server to connect to. The TLS server name is used for handshake verification or related protocol behavior; the two do not have to be identical, but both must match the server deployment. Transport paths, host names, and security-layer server names also serve different purposes. When a handshake fails, check each layer separately rather than replacing all three values with the server IP. Disabling certificate verification can hide the problem and is not a routine troubleshooting step.
Check Core Support Before Assembling Fields
Xray and V2Fly share some legacy configuration concepts, but their extended protocols and transport options are not guaranteed to be compatible. If a node requires REALITY or a specific flow-control field, first confirm that the core actually launched by the client supports it. v2rayNG and v2flyNG use different core approaches. On desktop, check the current node and runtime information in v2rayN too; do not infer full feature support from the client name.
Protocol, transport, and security are related but distinct layers. VLESS is a proxy protocol; TLS and REALITY are security-related settings, and the network transport has its own parameters. Placing a setting at the wrong level can cause a configuration error or a negotiation failure after loading. For background on these layers, see How REALITY and XTLS Vision Work; use the fields supported by your actual core.
Reviewing Tags and Connection Paths
Every outbound referenced by a rule should have a unique tag. Duplicate tags make the configuration ambiguous, and you should not rely on how a particular tool handles them. After renaming an outbound, check routing rules, any outbound chains, and every other reference. Changing a node's display name in the interface may not change the outbound tag in the generated configuration. That is one reason the runtime configuration is more reliable than the node list alone.
To diagnose an outbound, first use a local direct baseline to test the destination service. Then switch to the remote outbound using the same inbound and test destination. If only the remote path fails, check server address resolution, port reachability, security handshake, and authentication. Different destinations may use different routes, so one successful webpage does not prove every outbound is working; one timeout does not prove the entire node is unusable.
For long-term maintenance, define the role of each default, explicit direct, explicit reject, and remote outbound. Validate a new outbound by itself, let a small number of rules reference it, and only then consider changing the default path. This prevents a misconfigured node from affecting every request and makes it easier to roll back only the relevant rules instead of rebuilding the entire configuration.
04 / ROUTING
routing Rules and Match Order
Routing maps request conditions to outbound tags. What matters is not the number of rules but their order, how their conditions combine, and what destination information is visible. Traditional field-based rules are generally checked from top to bottom; the first match selects an outbound, and later, more specific rules are not checked automatically. Put precise exceptions before broad rules and fallbacks at the end. A rule does not gain special precedence just because it appears later in the file.
How Conditions Combine Within a Rule
Different conditions in the same rule usually all need to match. For example, specifying both inboundTag and domain means the inbound and the domain must both match, not either one. A list under a single condition usually gives multiple alternatives, such as either of two domains. To express two independent sets of conditions, use two rules; otherwise you may accidentally write AND when you mean OR.
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"inboundTag": ["local-socks"],
"domain": [
"full:example.com",
"domain:example.org"
],
"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",
"fe80::/10"
],
"outboundTag": "direct"
}
]
}
}
This routing snippet is compatible with the baseline above. The first rule applies only to the specified SOCKS inbound; an HTTP inbound will not match it, even when requesting the same domain. The second rule lists loopback, private, and some local address ranges, but it is not a complete database of special-purpose addresses. For broader address classification, use a verified data set and understand its coverage and update process.
Choose the Right Level of Domain Matching
full: matches an exact domain name and is suitable for a single host; domain: matches the specified domain and its subdomains. Expressions without a prefix may be treated as keyword matches, which can cover more than expected. Regular expressions are also subject to JSON escaping, and overly broad patterns can be difficult to understand and maintain. Prefer an exact domain or domain suffix when it expresses the rule clearly.
Domain rules match destination information supplied with the request or identified by the core—not every resource loaded by the final webpage. A page may request resources from several domains, so routing its main page through one outbound does not mean its images, APIs, and videos use the same path. Test a single destination request when diagnosing rules; do not treat the overall result of loading a webpage as direct proof of how one domain rule behaved.
Domain Resolution Strategies Change Matching
AsIs does not resolve a domain just to match it against an IP rule; IP rules can still match when the request itself already contains an IP address. IPIfNonMatch attempts to resolve a domain if it does not match an existing rule, then checks the result. IPOnDemand triggers resolution when routing needs IP information. These are not system DNS switches or outbound domain-resolution settings.
A strategy that triggers resolution can change both latency and rule matches. If DNS returns addresses in different regions, IP classification rules may select different outbounds. Conversely, if an app resolves a domain outside the core and sends an IP address, changing the core's domain-resolution strategy cannot restore the original domain. Consider the inbound protocol, sniffing, and the app's resolution behavior together instead of repeatedly swapping DNS servers.
| Condition | Matches | Common misconception |
|---|---|---|
domain | Destination domain information | Does not match the path in a full webpage URL |
ip | Destination address or resolved address | Not the client's own address |
port | Destination service port | Not the local inbound listening port |
inboundTag | Tag of the inbound receiving the request | Not the node's display name |
network | Request transport type | Not the security protocol used by a webpage |
Organize rules with exceptions, categories, and fallbacks
Start with precise targets that must be rejected or sent through a specific outbound, then add local network and service categories, and handle remaining requests last. The right order depends on your policy, but each rule should have a clear reason for its position. A final catch-all rule prevents later rules from taking effect; if you omit one, make sure the first outbound is an acceptable default for unmatched traffic.
Using geosite: or geoip: categories requires the relevant data files and a valid category name. Syntax supported by the core does not mean the required data is present in your environment. If a category fails to load after an update, check the data-file path and category name before deleting all your routing rules. Generated files, built-in client rules, and custom rules may also be ordered differently, so always check the runtime configuration.
After changing rules, use logs to verify that the destination, source, and outbound match your expectations. If some sites work and others do not, compare the outbound actually selected before investigating DNS and the connection process. A client-wide toggle or a node's status color cannot tell you how a particular request was routed. For long-term maintenance, brief notes grouping related rules make rollbacks easier than continually adding duplicates.
05 / DNS
dns Resolution and Connection Paths
When troubleshooting DNS, first establish who is resolving the name. The app may query DNS itself, the operating system may resolve it, the core may use its built-in resolver, or a remote proxy may resolve the destination on the other end. The top-level dns section primarily configures the core's built-in resolver; it does not automatically change DNS settings for every app on the device. Even after you enter a server address, check whether the actual request uses that resolver.
Separate Where Names Are Resolved from How Queries Are Routed
With SOCKS, an app may pass the domain name directly to the proxy or resolve it locally and send an IP address instead. HTTP proxy requests can usually carry a domain, though an app may still resolve it in advance. Virtual-network interception also involves how DNS requests are captured. You need to know whether the core receives a domain or an IP before deciding whether domain routing, built-in DNS, or outbound resolution is involved.
This dns snippet demonstrates static host mappings, a server list, and address-family selection. The example uses public resolver addresses, but your network may not be able to reach them, or your organization may require internal DNS. Choose servers for your network rather than copying these addresses and assuming every resolution issue is fixed.
{
"dns": {
"hosts": {
"example.com": "192.0.2.10"
},
"servers": [
"1.1.1.1",
"8.8.8.8"
],
"queryStrategy": "UseIPv4"
}
}
192.0.2.10 is a documentation-only example address; the mapping does not point to a real website. Use static mappings with care: they pin an affected domain to a specific address and may break load balancing, service migrations, or certificate matching. Remove the example mapping after testing, then create production rules only for domains and addresses you actually need and have verified.
DNS Policy and Routing Policy Are Different
queryStrategy controls which address family the built-in DNS resolver prefers to query or return. Routing's domainStrategy controls whether it resolves a domain to an IP for matching. A direct outbound may also have its own domain-resolution strategy. All three relate to resolution, but at different points in the process. Changing top-level DNS does not guarantee that a direct outbound will use the result the same way, nor that every remote proxy outbound resolves its destination locally.
For a traditional direct outbound, you can configure a supported strategy to use built-in DNS results. The snippet below adds UseIP to the direct outbound, meaning it resolves a domain to an IP before connecting. This does not control routing match order or change other outbounds. Check the fields supported by your running core for valid strategy names and address-family options.
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
}
}
]
}
DNS Queries Need a Working Network Path
Network queries made by the built-in DNS resolver need a reachable path. Different server formats—such as plain addresses, encrypted DNS URLs, or the local system resolver—may use different connection mechanisms and do not necessarily follow the same routing rules. Tagging DNS requests and routing them separately is an advanced configuration; check how your core supports it. Do not assume that any DNS address you enter will automatically use the currently selected remote node.
Also avoid creating a dependency loop at startup. If the remote node address is a domain and the DNS server is reachable only through that remote node, the lookup needed to establish the connection may never complete. An encrypted DNS service hosted at a domain also needs an initial lookup. First make sure the node address and DNS server can be resolved through an available path; then decide where other domains should be resolved.
Address Families, Caching, and Split DNS
Having an IPv6 address on the device does not mean the IPv6 path to a destination works. Restricting queries to IPv4 can help narrow down some issues, but it is not a long-term answer for every environment. If only one address family fails, check routing, interfaces, and upstream connectivity. In dual-stack networks, the resolved addresses and the address actually used for a connection may differ; check the destination in the logs rather than only the first DNS result.
After changing DNS servers, cached results may remain in the app, operating system, core, or upstream resolver. To test, close existing connections, restart relevant components using the client's supported method, clear system caches if needed, and make a new request. Existing connections may keep using an old address even when the new settings were saved. Avoid clearing caches, changing nodes, and editing rules all at once; otherwise, even if the problem goes away, you will not know why.
When choosing resolvers by domain, the rule list, expected address ranges, and fallback settings can affect one another. Add complexity only when you genuinely need different DNS views, and document the purpose of each domain group. Internal domains in particular should use a resolver that can identify them. If a connection succeeds but pages still do not load, continue with the Connected but No Internet troubleshooting checklist to check system proxy settings, routing, and the destination service.
06 / POLICY
policy Connection and Statistics Policies
policy configures connection behavior by user level and some system-level statistics options. It is not a routing ruleset, does not select a node, and will not make a connection faster just because you lower a timeout. Many configurations work without manual policy changes. Adjust fields only when you have a specific need for resource management, connection retention, or statistics, and verify the effect against real traffic.
How Levels Relate to User Fields
levels uses level numbers as object keys, such as "0". The level field in a protocol user object selects a policy level, but user objects differ between protocols. Some local inbound settings also provide a default user level. Do not place level arbitrarily at the top level, or infer a user's level from the order of nodes in a list.
This policy snippet shows the structure for levels and system statistics settings. The values are for illustration, not recommended settings for every network. Cores may have different defaults or behavior, especially with connection reuse, different transports, and protocol implementations. Check what your core supports first. If you have no specific requirement, keeping the default policy is usually easier to maintain than copying a set of supposed optimization values.
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
}
What Timeout Fields Actually Control
handshake sets a time limit for the relevant connection handshake stage; it is not the total time from opening a webpage to loading it. If network latency, an upstream response, or protocol negotiation slows the handshake, a limit that is too short can terminate a connection that would otherwise succeed. When a handshake times out, check the server address, port, and security settings before deciding whether the limit needs adjustment.
connIdle controls how long idle connections are kept open. Chat, push notifications, and long polling can have extended periods of low traffic, so shortening the idle timeout too much may cause extra reconnects. On the other hand, retaining large numbers of unused connections can consume resources. Check whether idle disconnects cause problems for your apps rather than assuming fewer active connections means the setting is optimized.
uplinkOnly and downlinkOnly apply when one side of a connection has ended but the other direction can still transfer data; they are not general upload and download speed limits. Do not use them to set bandwidth quotas. Half-close behavior differs across protocols and transports, so if one-way transfers stop, investigate protocol behavior and logs instead of simply increasing these values.
| Field | Purpose | Check before changing |
|---|---|---|
handshake | Handshake time limit | Did the failure actually happen during the handshake? |
connIdle | Idle connection retention | Does the app need long-lived connections? |
uplinkOnly | Behavior after one direction ends | The protocol's half-close semantics |
statsUserUplink | User uplink statistics toggle | Are user IDs and a statistics module available? |
statsOutboundDownlink | Outbound downlink statistics toggle | Is there a tool to read the statistics? |
Statistics Toggles Are Not a Complete Statistics System
Enabling statistics fields in a policy only allows the relevant counters to be recorded; it does not automatically create a dashboard. Full statistics usually also require a statistics module, a suitable management interface, and a tool to read the data. User-level statistics also need identifiable user information. If the client already generates these settings, check its full implementation first; do not add another management listener on the same port and cause a conflict.
Traffic figures shown by a client may not match any single counter in the core. Interception scope, connection retries, protocol overhead, and where traffic is counted all affect the numbers. Before comparing traffic, establish which layer is counting, when counting starts, and whether all inbounds are included. Do not treat a number in the interface as a billing record from an upstream service or assume the counter is wrong without understanding its scope.
Test Against Real Workloads, Not Universal Settings
Validate policy changes against real usage. For short web requests, check the initial connection and follow-up requests. For long-lived connections, check whether data resumes after idle periods. For large transfers, confirm that one-sided connection closure and connection retention do not truncate the transfer. Change one field at a time and record the original value, test method, and result to distinguish policy effects from network fluctuations.
If the device is low on resources, also check for duplicate core processes, too many speed tests, or unnecessary background requests. Policy settings are not a universal resource-management switch. If there is no evidence that connection management is the bottleneck, cleaning up redundant tasks and unused inbounds is usually more direct than shortening every timeout. To restore a known baseline, return to the client's default policy and repeat the same tests rather than keeping undocumented values indefinitely.
07 / LOG
log Logs and Error Diagnosis
Logs show what happened to a particular request; one English error message alone rarely identifies the cause. Consider the request time, inbound source, destination address, outbound tag, and subsequent status together. Background apps constantly make connections, so when testing several websites at once it is easy to associate another request's error with your current action. Narrow down the traffic before reproducing a specific request.
Log Level and Output Location
loglevel controls the detail in error logs. Common values include debug, info, warning, error, and none. Use a moderate level during normal operation; temporarily increase detail to troubleshoot routing and connections. Detailed logs can be numerous and may include destination information. Restore the original setting after troubleshooting to avoid retaining unnecessary data.
{
"log": {
"access": "access.log",
"error": "error.log",
"loglevel": "info"
}
}
This snippet shows file-output fields. Relative paths are usually resolved against the process's working directory, which may not be the directory containing the configuration file. A graphical client may also capture standard output and display it in its runtime log window. If you cannot find a file, check how the core was launched, its working directory, and write permissions before concluding that the core is not running. For a fixed location, use an existing local path that is writable.
Access logs and error logs serve different purposes. Access records can help show that a request was received and where it went; error logs focus on failures. An access record does not guarantee that the destination content was fully transferred. Fields shown can also vary by core and log level. If the expected outbound tag is missing, temporarily increase log detail or check the runtime configuration rather than guessing the default path.
Read Common Errors Along the Processing Path
A connection refused error usually means that the endpoint at the destination address explicitly rejected the connection. If the destination is a local proxy port, check whether the core is listening. If it is a remote node port, check that the service is running and that the address and port are correct. This does not automatically mean authentication failed; authentication usually happens after the connection is established. First identify which layer the address in the log belongs to.
An i/o timeout means a read or write stage exceeded its wait time, but it can happen during connection setup, the handshake, or data transfer. An unreachable address, missing response, packet loss, or wrong protocol can all look similar. Check earlier entries for the same request to find the last stage that completed. Increasing every timeout usually just makes you wait longer; it does not fix the connection path.
For TLS-related errors, check the system clock, server name, certificate chain, and security settings. Connecting by IP and verifying a domain name may have different requirements; the transport-layer host name is not necessarily the certificate name. If the server requires a particular security protocol but the client is configured for another, the port may be reachable while the handshake still fails. Correct the combination of fields rather than disabling every verification option.
Handle Startup Errors and Runtime Errors Separately
A failure before startup usually points to JSON syntax, field types, an unrecognized protocol, a data file, or a port-binding problem. Get the core to start reliably before testing the network. For runtime failures, trace individual requests. If the client keeps starting and exiting, do not mistake repeated startup messages for normal operation. Find the first fatal error; subsequent connection failures may simply mean the core has already exited.
If a tag is missing, check rule references and the outbound array. If category data fails to load, check the data file and category name. If there is a permissions error, check the output directory and permissions required for interception. Do not delete an entire module just to clear an error. Match each error to a configuration object, fix it within that object's scope where possible, then repeat the same startup or request test.
Keep Useful Evidence and Remove Sensitive Details
Before sharing logs for help, remove identity details, full subscription URLs, remote node credentials, and destinations you do not want to disclose. Check screenshots for window titles, clipboard contents, and sidebars too. To keep the logs useful, retain field names, error types, relative timing, and anonymized tags. Do not share only the last line while omitting the context that explains it.
Record the test time, inbound, destination, expected outbound, and actual result to create a clear evidence trail. If the error does not change after editing a rule, confirm that the runtime configuration was updated, the old process stopped, and the test app is not reusing an existing connection. For more on error messages, see How to Diagnose v2rayN Runtime Logs; the final diagnosis should still be based on the current configuration and the same request.
Control how long log files are kept. For long-running setups, use the client's cleanup or rotation features to prevent disk usage from growing indefinitely. Whether deleting a file that is still being written frees space immediately depends on the operating system, so stop the relevant log output or shut down the core before maintenance. Logs should support reproduction and diagnosis, not record every connection forever.
08 / VALIDATION
Configuration Validation and Change Management
Validate a configuration in five layers: text, structure, startup, connection path, and workload. Parsing successfully does not prove the fields are valid; valid fields do not prove a port can listen; a listening port does not prove requests use the right outbound; and a successful connection does not mean every app is being intercepted. Check each layer in order to avoid repeatedly changing downstream settings while an upstream issue remains unresolved.
Save the Original Configuration and Record Your Goal
Before making changes, save a known-good configuration and note which client, core, and operating mode it applies to. Keep backups somewhere controlled; do not casually sync or publish files that contain identity information. You can include the purpose and local maintenance date in the filename, but avoid vague labels such as “final.” For a rollback, you need to know which inbounds, outbounds, and rules the backup corresponds to—not just that the file once opened successfully.
State the intended result of each change, such as “route this domain directly” or “add a test inbound,” then design a request that can verify it directly. If the goal is to fix one domain, do not also replace all DNS and connection policies. The smaller the change, the fewer differences you need to investigate if the test fails and the easier it is to decide whether to roll it back.
Layer 1: Check Syntax and Structure
Use your local editor's JSON diagnostics to check brackets, quotation marks, commas, and field types; avoid uploading the configuration to an unfamiliar online tool. Then check that each field is in the right place: sniffing belongs to an individual inbound, outbound settings belong to the relevant outbound, and rules belong in routing.rules. Structural mistakes can be harder to spot than misspellings because the file may still parse as valid JSON.
Next, check every reference: each outbound tag in a rule must exist, inbound tags must match exactly, category names must be present in the current data files, and log paths must be writable. Tags are not automatically synchronized variables; changing one side does not update the other. Keep a reference table of display names and actual tags so you do not mistake a client-interface label for a core field value.
Layer 2: Startup and Local Connection Path
Use the client's configuration checker or runtime information to confirm that the core can load the configuration. Command-line validation differs between cores, so this page does not present one startup option as a universal command. Once it starts, verify that the local port is listening, then point a proxy-aware test app explicitly at that inbound. If no request appears in the logs, check the proxy address, port, app settings, and system permissions.
You can first roll back to the complete direct-connection baseline in the first section and confirm that a local request can be received and sent. If the baseline also fails, there is no need to start with remote-node security settings. If it works but the custom configuration does not, restore one section at a time in this order: inbounds, outbounds, routing, DNS, and policy. Restart and test after each section to find where the behavior first changes.
Layer 3: Recheck Routing and Workloads
Choose one destination that should use a specific outbound, then choose another that should not be affected by the rule. Record the outbound actually used for both and check that the rule's scope is correct. Domain rules may be affected by subdomains, page resources, and redirects. Port rules must distinguish the destination port from the local listening port. Comparing tests gives stronger evidence that a rule behaves as intended than simply seeing a webpage load once.
| Platform | Check first | Then check |
|---|---|---|
| Windows desktop | Proxy destination and listening port | Whether the app follows the system proxy |
| macOS desktop | Proxy settings for the active network service | Interception permissions and status after switching networks |
| Linux desktop | App proxy settings and desktop environment settings | Working directory, permissions, and duplicate processes |
| Android | System network permissions and per-app scope | Background restrictions and conflicts with other network interception |
The same core configuration can behave differently across operating systems because file paths, listening permissions, and network interception controls may vary. App-specific network settings may not follow the desktop environment's settings either. Do not assume that a configuration that works on one device will work on another after only changing the node. When migrating, recheck the inbound and required permissions before reusing the protocol and routing logic.
Updates, Rollbacks, and Ongoing Reference
After updating the client or switching cores, keep the old configuration and change log, then check that the generated configuration still contains your rules. Subscription updates mainly affect node details; they are not usually a version-control system for the complete runtime configuration. If custom fields are overwritten, use a persistent configuration option supported by the client rather than making repeated edits to a temporary file.
When something breaks, first undo the most recent related change and repeat the same test. Once things work again, compare the configurations to identify the specific field instead of permanently replacing the entire file with an old version and stopping there. When cores differ, preserve the existing protocol and transport combination where possible. Changing the client, core, node, and rules in one rollback changes too many test conditions at once.
For routine connection setup, follow the main steps in the Getting Started guide. To switch platforms or choose a client, see the three-client comparison; find installers on the downloads page. This page is organized for field-by-field reference: check inbounds for receiving issues, outbounds for connection paths, routing for match problems, DNS for domain resolution, policies for connection retention, then use logs and comparison tests to verify your findings.