Configuration Reference · Fields & Runtime Behavior

V2Ray Configuration Reference

Look up JSON, inbounds, outbounds, routing, DNS, and policy field by field. This page explains how the fields work together for a connection; it does not replace the initial client setup. If you still need to install the client, import a subscription, or configure the system proxy, start with the Quick Start Guide. If you can already connect and want to adjust rules or investigate a config issue, use the contents on the left to find the relevant section.

Examples cover common V2Fly / Xray JSON structures. sing-box uses a different configuration format, so these examples cannot be applied as-is. Unless noted otherwise, complete examples cover local direct connections and rule testing only; they do not include credentials for remote proxy servers.

JSON structure and configuration sources

Identify the layer you’re editing

A V2Ray config is not the same as every setting shown in the client interface. v2rayN stores subscription groups, UI preferences, and routing presets, then generates a runtime config based on the selected server and core. v2rayNG and v2flyNG also have their own app settings. Before editing, distinguish the original subscription content, the client’s saved settings, and the JSON actually read by the core. These layers may describe the same node without being stored in the same file.

Editing a temporary file generated by the client can lead to changes that work until the next launch, then revert. Usually the client has regenerated the config; the core has not randomly ignored a field. Make changes through the client’s routing, custom config, or node-editing options when possible. If you need to maintain a complete JSON file, use an entry point that explicitly supports full configs, keep a separate backup, and note which core is selected. Don’t treat a temporary file as the sole source of truth.

Top-level objects and nesting

Common top-level fields include log, inbounds, outbounds, routing, dns, and policy. Inbounds and outbounds are arrays; each object in an array represents a listener or an egress. The routing object’s rules field is also an array, and its rules are processed in order. A field’s location matters as much as its name: putting a valid field in the wrong object won’t make it work as intended.

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

This basic config exposes a SOCKS listener on the local machine and connects directly to destinations; it has no remote proxy egress. Use it to verify the listener and local connection path, not to test whether a subscription server works. The port is an explicit example. If a client already occupies the same address and port, starting another config will cause a conflict. Stop the existing process or use a test copy with both the listener port and the app’s proxy settings changed.

Valid syntax doesn’t guarantee correct behavior

Standard JSON requires double quotes around keys and strings. Don’t leave a trailing comma after the last item in an array or object, and don’t put explanatory comments in a file that will be passed to the core. Write booleans as true or false; ports are usually numbers. Backslashes in strings must be escaped, especially in manually entered Windows log paths. A local editor with JSON validation can catch unmatched brackets and missing quotes first.

Next, check references. A rule’s outboundTag must match the tag of an existing outbound; any inbound tag referenced by a rule must also exist. A tag is just a name used within this config—it doesn’t create proxy functionality. To avoid ambiguity, use short, descriptive English tags and keep them unique. When renaming a tag, search for every reference; don’t update the outbound definition while overlooking routing or statistics settings.

Also distinguish between a config being accepted and it actually taking effect. A core may allow certain defaults or reject fields because of implementation differences. After each save, check the startup log and active core rather than relying on the editor showing no errors. JSON formatting can confirm syntax, but not whether transport settings, server certificates, and the remote protocol match; verify those separately in the following sections.

Keep each change focused on one goal—for example, adjust only the routing order or change only the DNS resolver. Save the original config and briefly note why you made the change and what you expect to happen. If you change the inbound, outbound, and DNS at once, it’s difficult to identify the cause of a failure. The goal isn’t to make the file as short as possible, but to make each field’s purpose, references, and conditions for taking effect clear.

inbounds: how local apps connect to the core

Listen address, port, and protocol

An inbound receives traffic from apps and passes it to the core. On desktop, the most common options are local SOCKS and HTTP proxy listeners. Apps must use the matching address, port, and proxy type. listen sets the local address to accept connections on, port sets the listening port, and protocol determines what kind of request the listener accepts. Even when the port number is the same, an HTTP client can’t use a SOCKS listener as if it were an HTTP proxy.

For local use only, prefer listening on 127.0.0.1. This IPv4 loopback address isn’t accessible to other devices through your computer’s LAN address. 0.0.0.0 listens on all IPv4 interfaces, significantly expanding access; it does not make the listener “secure by default.” IPv6 listeners require their own address and firewall checks. Don’t assume that binding to all interfaces automatically handles every network stack correctly.

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

These two listeners let different apps use whichever proxy protocol they support. udp: true allows the SOCKS inbound to handle relevant UDP requests, but the app must still support SOCKS UDP, and the outbound and remote endpoint must support the entire path. It does not automatically capture all system UDP traffic or change the behavior of apps that ignore system proxy settings. Inbound capabilities and system-wide traffic capture are separate concerns.

FieldWhat to checkCommon misconception
listenLocal loopback or a specific interfaceListening on all interfaces does not mean local access only
portDoes it match the value configured in the app?An open port doesn’t mean the proxy protocol matches
tagUsed to identify the inbound in rules and logsA tag doesn’t replace the listen address
settingsChoose fields for the inbound protocolDifferent protocols don’t share every setting

How sniffing relates to the destination

Sniffing extracts information such as domain names from protocol details in some connections. Common sources include the HTTP Host header and the server name in a TLS handshake. This can give routing a domain to match even when the app initially passes an IP address to the core. Not all traffic exposes a domain, and sniffing is not full decryption. Turning it on is no substitute for correct DNS settings and does not guarantee that every app’s domain can be recovered.

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

Add this inside the specific inbound object. In implementations that support it, routeOnly uses sniffed data for routing without replacing the connection destination. Check that the selected core supports it before use. If a field is rejected, don’t keep guessing at alternative names. First verify the listener with a basic config that has no sniffing, then add supported fields one at a time using that core’s documentation. Distinguish an unsupported field from a failure to connect remotely.

LAN sharing and system-wide traffic capture

To let other devices on the same network use your computer’s proxy, changing the listen address is only part of the setup. Check your computer’s actual LAN IP, firewall, network isolation, and authentication. Don’t expose the proxy port to the public internet or open an unauthenticated listener on an untrusted network. Other devices must use your computer’s LAN address, not their own loopback address; a phone’s loopback address always points back to the phone itself.

A system proxy setting tells apps that honor it to use a proxy; it doesn’t change how every app handles network traffic. TUN involves a virtual network interface, routing table, permissions, and DNS capture, and should usually be configured through the client’s dedicated feature. Don’t add a TUN field from one core to a different JSON format. If the system proxy works but a specific app still connects directly, first check whether that app supports system proxy settings, then consider whether TUN is needed.

To troubleshoot an inbound, first check whether the client started, then look for an address-in-use error in the logs, and finally verify the proxy type selected in the app. If a browser works through the HTTP listener but another program fails through SOCKS, check that program’s authentication, DNS behavior, and UDP requirements instead of immediately reinstalling the client. For LAN setup details, see Allow LAN Connections and Share a Proxy.

outbounds: egress, protocols, and transports

Outbound tags and the default egress

An outbound sends connections received by the core to their destinations. It can connect directly, pass traffic to a remote proxy server, or reject requests according to a rule. Common types include freedom for direct connections and blackhole for dropping traffic; VMess, VLESS, and Trojan describe protocols for communicating with a remote endpoint. A protocol name alone doesn’t provide a server—you still need a real address, port, and valid credentials.

When no other outbound selection mechanism applies, traffic that matches no routing rule typically uses the first outbound in the array. Changing the array order can therefore change the default route; it’s more than a formatting choice. Document the default outbound and add a clear fallback rule in the routing section. Defining a tag named proxy does not automatically route traffic through a proxy. The tag is only a reference; behavior depends on that outbound’s protocol and settings.

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

This set of outbounds is useful for local rule testing: ordinary requests connect directly, while requests matching a blocking rule are dropped. There is no proxy server here and no outbound tagged proxy. Before using routing snippets from another guide, make sure every referenced tag is defined. A rule pointing to a nonexistent tag may cause config validation to fail; the client won’t automatically create a missing server object.

Check protocol and transport settings separately

Remote outbounds typically have protocol and transport settings. Protocol parameters go in the relevant settings object, such as the server address, port, user ID, or password. Transport and security negotiation are usually described by streamSettings. Both layers must match the server configuration. Checking only the address and credentials isn’t enough: a mismatched transport, TLS setting, or path can let the port accept a connection while the protocol handshake still fails.

The server address, TLS server name, and Host used by some transports are not always the same. The address determines where the initial connection goes; the server name is used for TLS identity and certificate validation; Host or a path may be used for reverse-proxy routing. Don’t change them all to the same value just to make them look consistent. When importing a subscription, keep the provider’s full parameters and use the logs to identify missing or incompatible fields.

Parameter layerTypical settingsHow to verify
Protocol authenticationUser ID, password, encryption optionsCompare each setting with the server config
TransportTCP, WebSocket, gRPC, and moreConfirm the core supports the same transport as the server
TLS securityServer name and certificate validationCheck handshake errors and system time
Application layerPath, service name, HostCheck capitalization, prefixes, and reverse-proxy rules

Don’t mask the problem by weakening validation

If you encounter a certificate error, first check the system clock, then verify the server name, certificate validity period, and certificate chain. Skipping certificate validation changes the connection’s trust requirements and is not a routine fix. If a direct connection works but the remote proxy handshake fails, the inbound and part of the local network are already working. Focus next on the remote protocol and transport instead of changing every local port or clearing the entire routing table.

Some protocols also depend on extra handshake fields or dedicated security settings, which vary by core implementation. Seeing a protocol name in the client doesn’t mean every core supports the same parameters. If import produces errors such as unsupported protocol or unknown security type, first check whether the active core matches the node requirements. After switching cores, verify whether the client-generated transport fields have changed.

Multiplexing, keep-alive, and other optimizations aren’t always better when enabled. They change how connections are organized and can affect long-lived connections, server resources, and intermediate network devices. Before a basic connection works, stick with the provider’s recommended settings. Adjust them only when you have a specific, reproducible issue and server-side support. A successful short request doesn’t prove that long transfers, UDP, or every destination will work.

Troubleshoot outbounds in four stages: address reachability, transport handshake, protocol authentication, and destination access. Success at one stage narrows the search but doesn’t eliminate the need to check the next. For a node that times out completely, see Check System Time, Ports, and Transport. Before sharing logs or a config containing credentials, remove user IDs, passwords, and full subscription URLs. Keep only the protocol type and error category.

routing: match order and traffic routing

Understand rule matching through a single connection

Routing isn’t simply a list of domains. It selects an outbound based on information carried by a connection, such as its destination, inbound tag, and port. A common rule uses type: "field". Rules are checked in order, and the first match determines the outbound, so specific exceptions usually belong near the top. If a broad rule comes first, more specific rules below it may never run—a common reason a rule appears to have no effect.

Different condition fields in a single rule generally all have to match; multiple entries in a domain or IP array generally mean any one can match. For example, inboundTag and domain together mean a specific domain on a specific inbound, not two independent match conditions. To express “send all traffic from this inbound to one outbound, or send these domains there,” use two separate rules with clearly defined conditions.

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:example.com"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "direct"
      }
    ]
  }
}

This snippet depends on existing direct and block outbounds and on the runtime environment being able to read the relevant geodata. The first rule blocks an exact domain, the second handles private addresses, and the last explicitly sends all other TCP and UDP traffic directly. This example demonstrates ordering; it is not a general-purpose production policy. To add a real proxy outbound, define it first, then point the rules that need a proxy to its tag.

Domain rules and IP rules are different

full:example.com matches an exact domain; domain:example.com typically matches that domain and its subdomains. Unprefixed strings, regular expressions, and category data each have their own matching semantics and shouldn’t be treated as interchangeable. Blocking a main domain may not cover a separate asset domain, and a site may fail because one of its requests matched a rule. Check the actual request domain when troubleshooting, not just the address bar.

geosite: and geoip: reference data sets; they aren’t live internet lookups built into the core. Available categories, their contents, and whether the data files can be loaded all depend on the current data resources. If the logs report a missing category or data file, fix the resource or path first instead of repeatedly changing tags. A category rule’s match range can also change after a data update, even if the rule order hasn’t changed.

{
  "type": "field",
  "domain": ["geosite:category-ads-all"],
  "outboundTag": "block"
}

This rule works only if the data file contains the specified category. Before adding it, check whether an existing broader domain or catch-all rule already matches. Duplicating entries in a rule list won’t make them more effective; it just makes the config harder to understand. To test rule order, start with the exact reserved example domain shown earlier and confirm the outbound tag in the logs. Add category rules that depend on external data only after that works.

domainStrategy controls when DNS resolution is used for matching

AsIs tries to match using the address already provided by the connection. With IPIfNonMatch, the core can try resolving a domain if it didn’t match, then use the result for IP rules. IPOnDemand triggers resolution earlier when an IP is needed to evaluate a rule. These options control resolution timing during routing; they don’t configure app or system DNS, or every outbound’s destination address family. A more aggressive resolution strategy may also increase query volume and wait times.

If an app submits an IP address and no domain information is available, domain rules may not match. If it submits a domain but the strategy doesn’t trigger a lookup, IP rules may not have an address to evaluate. Check the target type received by the inbound before enabling sniffing or changing the resolution strategy. Don’t assume every miss is a rule-format problem: even a precise domain condition can’t match if the domain was never provided.

A practical layout puts local services and necessary exceptions first, business-specific outbound rules in the middle, and an explicit default behavior last. LAN rules still depend on the target environment; not every special-use address should be treated as ordinary public internet traffic. Test both destinations that should match and those that should not to catch overly broad rules. For other routing questions, see Troubleshooting.

dns: resolvers, query paths, and connection addresses

First, distinguish three kinds of DNS resolution

Before configuring DNS, distinguish between app-level resolution, the core’s built-in DNS resolver, and system DNS. An app may resolve a domain to an IP before passing it to the proxy, or pass the domain to the core for resolution later. Browsers may also use their own secure DNS. Changing the core’s dns object won’t automatically override queries from every app or the operating system. Without knowing where a query originates, it’s hard to tell which path the config affects.

The core’s built-in DNS is often used for IP-based routing matches and may also be called by an outbound’s resolution strategy. Defining it does not automatically start a system-wide DNS service. To send DNS requests from the system or other devices to the core, you also need the appropriate inbound and forwarding setup. Supported fields and behavior depend on the selected core. DNS capture after a client enables TUN is another layer; this object alone doesn’t prove that system queries are being intercepted.

{
  "dns": {
    "queryStrategy": "UseIPv4",
    "servers": [
      "1.1.1.1",
      {
        "address": "8.8.8.8",
        "port": 53,
        "domains": ["domain:example.org"]
      }
    ]
  }
}

This is a partial example of built-in resolver settings. The public resolver addresses show the structure; whether they are reachable depends on the network. domains affects resolver selection for specified domains; it does not create a proxy route for accessing them. Also consider fallback behavior: whether other servers participate depends on match priority, failure state, and fallback settings. Domain selection alone does not guarantee strict separation between query paths.

Configure DNS and routing strategies separately

queryStrategy can control the address family used for built-in DNS requests, such as IPv4 only or handling IPv4 and IPv6 where supported. It’s a different field from routing’s domainStrategy, and from domain strategies configured on some outbounds. When troubleshooting IPv6 access, check the DNS results, whether the system has a usable IPv6 route, and how the selected outbound uses addresses. Don’t just disable one setting and assume the issue is fixed.

If the network has no reliable IPv6 route but tries an unreachable IPv6 destination first, requests may appear to hang before succeeding. On the other hand, blindly restricting traffic to IPv4 can break resources available only over IPv6. Check the failed destination’s DNS results and connection errors before changing address-family behavior. Success with one dual-stack site doesn’t prove that every network condition is healthy; DNS records, connection fallback, and app caches can differ by destination.

LocationWhat it doesWhat it does not do automatically
dns.queryStrategyAddress family for built-in DNS queriesIntercept DNS from every app
routing.domainStrategyResolution timing for routing matchesStandardize resolution behavior across all outbounds
Outbound domain strategyHow the outbound selects a connection addressReplace system network routing
Client DNS captureIntercepts or forwards queries as implemented by the clientGuarantee coverage of all independent encrypted DNS queries

The query path must also be reachable

When choosing a resolver, consider not just its address but how queries reach it. Ordinary UDP queries, encrypted DNS requests, and calls to the local system resolver may take different paths; some local resolution modes bypass normal routing. Don’t assume a query passes through a particular proxy based only on the protocol name. Check the DNS transport supported by the core, then verify it against the logs and network environment rather than treating the expected path as fact.

If you use a domain name for an encrypted DNS endpoint, consider the startup dependency: the resolver’s domain must be resolved before the endpoint can be reached. If that resolution depends entirely on the same resolver that isn’t available yet, you can end up in a loop. The same issue can arise if the proxy server’s address needs resolving but the query is required to go through a proxy that hasn’t connected. Leave a clear, reachable DNS path for startup.

DNS changes may not take effect immediately because results can be cached. The system, browser, apps, and core may each keep their own cache, so restarting one layer may not clear the others. Close existing connections, restart the relevant app if needed, and test with a destination whose expected result is clear. Don’t switch among several resolvers and judge the result from one page refresh: an existing long-lived connection may not have made a new query at all.

If a domain fails but a direct connection to its IP works, the problem is more likely related to DNS or a domain-dependent handshake. However, TLS certificates depend on the domain, so accessing an IP directly is not always an equivalent test. A better approach is to check separately whether the DNS query returns a result, whether the returned address is reachable, and whether the protocol handshake succeeds. Change only one variable at a time—DNS source or address family—and keep logs from before and after.

policy: user levels, timeouts, and statistics

Policies are linked to user levels

policy sets connection-handling options and some statistics switches. It does not route domains or determine whether a remote server accepts authentication. Common structures include levels and system. The former sets timeout and statistics options by user level; the latter is for system-level inbound or outbound statistics. A level is a configuration reference, not a paid tier or speed grade; a higher number doesn’t mean a faster connection.

Some protocol user objects can set level, which corresponds to an entry in levels with the same numeric string as its key. If no level is explicitly set, follow the defaults for that protocol and core. Defining a level that no user references won’t make every connection use it. To check whether a policy applies, first identify the connection’s user and level, then inspect the policy fields. The mere presence of an object in the config doesn’t mean it’s in use.

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

Time values here are in seconds. This is an example of interpretable policy settings, not a tuning recommendation for every use case. handshake limits the relevant handshake phase, while connIdle applies to idle connections. The one-way upload and download wait settings describe how long to keep a connection after one side closes. They aren’t total download-time limits and don’t replace an app’s own retry or timeout settings. Whether a field applies to a particular connection depends on the protocol implementation.

Timeouts can’t fix a protocol mismatch

A handshake can fail for many reasons: mismatched credentials, the wrong transport, a certificate error, or an unreachable destination can all terminate a connection. A longer handshake timeout gives a slow but valid negotiation more time; it can’t make an incorrect protocol correct. If the logs explicitly report rejected credentials or an unsupported field, increasing the timeout will only delay the failure. Fix known errors first, then consider whether the wait window is appropriate.

Idle connection cleanup also depends on app behavior. Messaging apps, remote sessions, streaming responses, and connections that send data only occasionally have different idle-time requirements. A very short threshold may cause background connections to reconnect repeatedly; a very long one may tie up more resources. Before changing it, confirm that idle cleanup is actually causing the disconnect—not a network switch, power-saving policy, server-initiated close, or timeout on an intermediate device.

FieldWhat it applies toWhen to adjust
handshakeProtocol handshakeSettings are correct, but negotiation is demonstrably slow
connIdleIdle connectionIdle cleanup is confirmed to disrupt the task
uplinkOnlyOne-way transfer phaseYou understand what happens after one side closes
statsUserUplinkUser upload statisticsStatistics module and user identification are configured

A statistics switch is not a statistics dashboard

Enabling statistics-related booleans won’t automatically create charts. Core-level user statistics typically require a statistics module, identifiable users, and a suitable query interface. Client traffic displays may use a separate collection method. Core user statistics, per-process client traffic, and total network-interface traffic are different measurements. Forwarding, protocol overhead, the statistics scope, and counter reset intervals can all produce different results.

Be especially careful when adding an API inbound or control interface just to display traffic. Restrict control interfaces to trusted access; don’t expose every listening address just to query them from the LAN. Most users can rely on the client’s existing logs and status features without setting up another interface. Statistics are disabled in this example to keep the focus on connections and rules; that doesn’t mean they must be disabled in every environment.

On Android, connections that drop when an app goes into the background often involve system restrictions on background activity, not the core’s idle policy. Check v2rayNG’s VpnService permission, background activity permissions, and the manufacturer’s battery-saving settings separately. See VpnService, Battery Optimization Exceptions, and Per-App Proxy for details. Don’t use extremely long timeout values as a substitute for the right system permissions.

Tune policies against a clearly reproducible case: note which app is involved, how long the connection was idle, whether the network changed, and which direction was still transferring data. Then compare the results after the change. Without these details, it’s hard to tell whether the policy made a difference or the environment simply recovered. For everyday browsing and subscriptions, the client’s reasonable defaults are usually easier to maintain. Start with the inbound, outbound, and routing settings that directly affect connection behavior.

Client-generated configs and core differences

The same UI option doesn’t mean the same JSON

v2rayN targets desktop environments. Its system proxy, TUN, routing presets, and core selection can lead to different runtime behavior. v2rayNG mainly uses the Xray core, while v2flyNG is an Android alternative using the V2Fly core. They support similar node concepts, but their app data directories, import workflows, background behavior, and generated configs differ. Don’t overwrite one client’s entire data folder with another client’s.

V2Fly and Xray share a configuration lineage, and some JSON fields are similar, but their extensions, supported protocols, and field changes aren’t identical. sing-box uses a different format: inbound types, outbound definitions, DNS, and routing actions all follow its own conventions. A protocol name being the same doesn’t mean you can copy its config word for word. When migrating, map the behavior you need to the new format and rebuild the fields rather than mechanically swapping protocol names.

ComponentApplies toMigration checklist
v2rayNWindows, macOS, and Linux desktopsActive core, routing presets, and system permissions
v2rayNGAndroid with the Xray coreVpnService, per-app rules, and background restrictions
v2flyNGAndroid alternative with the V2Fly coreProtocol support and field compatibility
Complete custom configDepends on the entry point in the specific clientWhether it overrides generated config and client-managed settings

Understand system proxy, routing, and TUN as separate layers

The system proxy determines how apps that support it pass requests to a local listener. Core routing decides which outbound handles requests that have reached the core. TUN captures a broader range of system traffic through a virtual network interface. These operate at different layers. Changing routing rules won’t suddenly control an app that never reaches the core. Enabling TUN won’t guarantee that every core rule is correct or fix invalid remote credentials.

In a client, “global” usually refers to the default outbound at a particular layer or the scope of traffic capture; its exact meaning depends on the current mode. It may change core routing or simply name a system proxy mode. Check the generated config, actual inbound, and logs instead of relying on the button label. If a LAN service becomes unreachable, check both whether the core is sending private addresses to the wrong outbound and whether TUN has changed how the system routes local traffic.

TUN requires appropriate permissions and may conflict with other virtual networking software. If the network stops working after enabling it, turn TUN off to restore basic connectivity, then check permissions, virtual interfaces, DNS, and routing conflicts. Don’t change remote server settings at the same time. If system proxy mode works, part of the connection path is already established. Keep the node unchanged and focus on the traffic-capture layer; this is usually more effective than reimporting all subscriptions.

Subscriptions, custom rules, and update overwrites

Subscriptions usually provide node details, not necessarily local routing, system proxy, or DNS settings. Some import formats include a more complete config, so check the content type before importing. Node parameters changing after a subscription update is normal synchronization, but locally edited nodes may be overwritten. Keep lasting exceptions in the client’s supported local rules or a separate config rather than manually changing the same field after every subscription refresh.

Check how complete custom configs are handled too: the client may run the file as-is or add a listener port, logging, a control interface, or statistics settings. Don’t assume the file contents are the final runtime config. To investigate a problem, note the import source and client selection, then review the final startup log. An exported config isn’t necessarily safe to share; it may contain server addresses, authentication fields, and subscription URLs. Remove sensitive details before sharing it.

On macOS, Windows, and Linux, the same JSON can still depend on different file paths, directory permissions, firewalls, and system proxy entry points. Put log files somewhere the client can write to; don’t paste an absolute path from another operating system. For installation directories and runtime requirements, see First-Time v2rayN Installation and Setup. If startup fails, first determine whether the process crashed or the config was rejected.

If you need to install or switch clients, choose a package on the Downloads page by operating system and processor architecture. For desktop use, start with v2rayN. Don’t keep switching clients and cores just to test one field; config differences and environment differences will compound. Break migration into small, reversible steps and keep the last working config so you can identify which feature or layer changed.

Complete config example and step-by-step verification

A baseline for local direct connections and precise blocking

Here is a complete JSON config for local testing: SOCKS and HTTP listeners on loopback, direct connections for ordinary traffic, and a blocking outbound for an exact example-domain match. It requires neither remote proxy credentials nor category data files, making it a useful baseline for understanding routing. If the client is already using the same ports, stop its core first or use a separate test copy with free ports and update the app settings to match.

{
  "log": {
    "loglevel": "warning"
  },
  "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": {}
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:example.com"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "direct"
      }
    ]
  }
}

For testing, use a full-config entry point supported by the client, confirm the intended core is running, and check the config load result. If startup fails immediately, check syntax, unsupported fields, and occupied ports first. If startup succeeds but no app requests appear, check whether the app is actually connecting to the local listener. Don’t change DNS or remote transport yet; the request may not have reached the point where those settings matter.

Run positive and negative tests in order

First, in an app that explicitly supports HTTP proxies, set the address to 127.0.0.1 and the port to 10809. Visit a destination that is normally reachable directly on your current network. Next, visit the exact example domain targeted by the blocking rule and check whether the result matches. Finally, temporarily remove that rule, reload the config, and compare. Make sure the request reaches the proxy with its domain intact; if the app resolves it to an IP first, the domain rule may have nothing to match.

Negative testing matters too: destinations outside the blocking rule should remain reachable. One failed destination doesn’t prove the rule is correct; the listener itself may be unavailable. Close existing app connections during testing, and account for browser cache, secure DNS, and independent proxy extensions that may change the path. If you’re unsure what an app is doing, run the full comparison in one app first to reduce variables, then test other programs.

Read logs by connection stage

Use logs to locate the stage where a connection fails, not to collect as much data as possible. Keep the routine log level low and raise detail temporarily when reproducing a problem. Check that the inbound tag, destination, matched outbound, and final error all belong to the same request. With multiple apps connecting at once, adjacent log lines may describe different connections. If you see many repeated connections, narrow the test scope first so background app activity isn’t mistaken for the result.

SymptomCheck firstDon’t change these first
Core won’t startJSON, supported fields, occupied listener portsRemote site routing categories
No inbound requestsApp proxy type, address, and portpolicy timeouts
Wrong outbound selectedDestination type, rule order, and tagsServer credentials
Outbound is correct, but the handshake failsTime, certificates, transport, and protocol settingsAdding duplicate routing rules

Logs may contain domains you visited, LAN addresses, and server details. Restore the normal log level after troubleshooting and delete detailed logs you no longer need. When describing a problem to someone else, keep the error category and failure stage, but remove credentials, full subscription URLs, and personal browsing records. Don’t publish the entire client data directory; it may contain historical nodes, group details, and other account-related information in addition to runtime configs.

Restore your real configuration one step at a time

After the local baseline works, restore one known-good remote outbound without adding complex routing. Verify the remote connection, then add one clear routing rule, followed by category data and DNS. Tune policies last. At every step, test requests that should and should not match. If a problem appears, reverting to the previous working copy is much easier than repeatedly experimenting with a file that has accumulated several layers of changes.

You can reuse the same verification sequence after subscription updates, core changes, and client upgrades. Don’t assume that an open window means the entire config is active, or that one accessible webpage proves every protocol works. Confirm that inbounds, outbounds, rules, and DNS each produce the expected results before relying on the config for its intended use. For everyday setup, return to the Quick Start Guide. For a specific issue, continue with Troubleshooting.

Download v2rayN