CONFIG.JSON / FIELD REFERENCE

V2Ray JSON Configuration Reference

Structure overview, inbounds and outbounds, routing and DNS, and connection policies.

Use this page as a reference for configuration fields and how they interact. For your first install, subscription import, and node selection, start with the Quick Start Guide. To understand generated configs or diagnose routing issues, use the contents to jump to the relevant section.

log inbounds outbounds routing dns policy

JSON Structure and Configuration Sources

First identify what generates the configuration, then decide where to make changes. v2rayN, v2rayNG, and v2flyNG are graphical clients that assemble node, subscription, routing, and local settings into the configuration the core needs to run. Settings saved in the client, data returned by a subscription, and the JSON used at runtime are different layers. A subscription link is not a configuration file, and the client database is not the core config. When troubleshooting, find the config used by the active connection rather than relying on an old exported file.

Top-Level Object and Request Flow

A typical config has a single object at its root. Its keys identify feature modules, whose values may be objects or arrays. inbounds and outbounds are arrays because a process can provide multiple entry points and exits. routing is an object; its rules property contains the ordered rules array. Understanding the hierarchy matters more than memorizing key order: top-level keys usually work in any order, but the order of routing rules and outbounds can affect behavior.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [],
  "outbounds": [],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": []
  },
  "dns": {
    "servers": ["localhost"]
  },
  "policy": {
    "levels": {}
  }
}

This is a structural outline, not a working forwarding config: empty inbounds have no port to receive requests, and empty outbounds have no destination. Requests typically follow the path “application → inbound → routing → outbound.” DNS may resolve domain names along the way, logs record events, and policies affect connection resources and timeouts. These modules are interdependent: an outbound tag that is spelled correctly but never referenced will not handle any traffic.

Valid Syntax vs. Valid Configuration

In standard JSON, keys and strings use double quotes, Boolean values are lowercase true or false, and numbers are usually unquoted. Do not add a trailing comma after the last item in an array or object, or put explanatory text in the code. Some implementations accept extensions, but use standard JSON when sharing configs between clients. Duplicate keys are especially easy to misread: parsers may keep different values, so include each key only once in an object.

A successful syntax check only confirms that brackets, types, and characters are valid; it does not prove the protocol settings will work. The core must also check protocol names, supported fields, tag references, and listening resources. Even if it starts successfully, connections can fail because of mismatched server addresses, credentials, or transport parameters. Treat “config parses,” “process starts,” “request succeeds,” and “rules behave as expected” as four separate checkpoints. Verify them one by one rather than using a single website visit to judge everything.

SectionPrimary PurposeFirst Things to Check
inboundsAccept application requestsListen address, port, and protocol
outboundsForward or reject trafficOutbound type, tag, and remote parameters
routingChoose an exit based on conditionsRule order and tag references
dnsProvide built-in domain resolutionQuery path and resolution results
policyControl connection resources and timersLevel assignment and timeout limits

Core Differences and Configuration Boundaries

Project V provides the ecosystem context for related tools. V2Fly and Xray are separate core implementations and should be treated as such. They share configuration concepts, but that does not mean every new field, transport combination, or default is identical. Extensions such as REALITY must use fields supported by the core in use; do not copy a section specific to one implementation into another config without checking. The examples in this guide use a common basic structure. Before using extended features, confirm which core the client currently runs.

Graphical clients typically generate a config when you switch nodes, update a subscription, or reconnect. Direct edits to a temporary generated file may be overwritten the next time one of these actions occurs. For persistent changes, use the client's routing, DNS, or custom configuration options where possible, and confirm how they are merged. Save the original settings first, note the fields you change and the expected result, and change one layer at a time so you can roll back accurately. For help choosing a client, see the client and platform downloads page.

Logging and Diagnostics

Logs help explain what happened during a particular run; they do not prove that every connection is healthy. If the config fails to start, check the error log first. If an application connects to the local port but cannot complete a request, compare access records, the destination, and outbound behavior. Core, client UI, and system service logs may come from different processes and vary in timestamps and detail. Before comparing records, make sure they belong to the same run so an old error is not mistaken for a current issue.

Log Levels and Output Locations

loglevel controls how much detail error-related logs include. Common values are debug, info, warning, error, and none. For everyday use, keep logging at a moderate level and raise it briefly to reproduce an issue. More detail improves visibility; it does not change rule matching or fix a handshake. If the issue occurs in only one application, narrow down the reproduction instead of logging every connection for an extended period.

{
  "log": {
    "access": "./access.log",
    "error": "./error.log",
    "loglevel": "warning"
  }
}

This section must be merged into the top-level object. Both paths are relative, and their actual locations depend on the process working directory, which may differ from the directory containing the JSON file. A desktop client launched from a shortcut, as a service, or from a terminal may use a different working directory. If a file cannot be found, check how the process starts and whether it has directory permissions before assuming logging is disabled. Setting the path to a writable location is more direct than repeatedly raising the log level; do not solve this by granting broad access to the entire application directory.

If the client already captures core output, specifying an additional file may not be necessary. When no file is specified, check the actual core and launch method to determine where output goes. Some interfaces show only a summary; others have a separate access-log toggle. If log files keep growing, configure rotation or cleanup in the runtime environment rather than assuming the core will cap their size. Set retention based on troubleshooting needs, and restore the previous log level after reproducing the issue to avoid filling the disk with long-term logs.

Build a Minimal Timeline of Evidence

A useful reproduction needs a clear start and end. Stop existing connections, note the restart time, then make one request to a known address. Check for inbound traffic, destination identification, rule selection, and outbound errors. Implementations do not always log each step, so do not expect every log to use the same wording. Focus on correlating the time, destination, and result. Refreshing several pages in a row adds preconnections, background requests, and cached responses, making the logs harder to interpret.

Common errors make more sense when traced by layer. A failed listener often points to a port conflict, an unavailable address, or a permissions issue. DNS errors point to the resolution path. A remote connection timeout may involve reachability or transport conditions, while authentication or handshake errors call for comparing client and server parameters. A final timeout message alone does not identify the failing layer. Look for the first meaningful error before it, rather than copying only the last line. Repeated messages from retries are not necessarily separate causes.

Distinguish Missing Logs from Traffic Not Reaching the Core

If there are no access records, first check whether the application is actually using this inbound. A browser may read the system proxy settings, while a command-line tool may use only its own arguments; existing connections may also keep using their original path. Reconnect the application for testing and explicitly specify the proxy address to reduce variables. If the request never reaches the core, changing outbounds, DNS, or routing will have no effect. For the difference between the system proxy and core routing, see System Proxy and Routing Modes.

If requests appear in the logs but use an unexpected outbound, check rule order and tag references in the routing section. If the outbound is correct and only some destinations fail, compare their address families, transport protocols, and DNS paths. Logs are one diagnostic clue and should be paired with repeatable tests. Save one successful and one failed record, then compare them point by point. This is usually more effective than changing the port, resolver, and routing mode all at once.

Inbounds: Local Entry Points

An inbound defines where the core receives requests and which protocol it uses to interpret application traffic. Common desktop options include local SOCKS and HTTP proxies. The port is simply the entry point; it does not determine whether traffic exits through a remote node, nor does it automatically route every application through the proxy. Applications must connect to the port themselves, or have their requests forwarded by a mechanism such as the system proxy. Before troubleshooting, check which protocol the application expects. Do not enter a SOCKS port in a setting that accepts only an HTTP proxy.

Listen Address, Port, and Tag

listen sets the bind address, port sets the listening port, protocol sets the inbound protocol, and tag provides a reference for later use. To allow local access only, explicitly listening on 127.0.0.1 limits the scope. This is the IPv4 loopback address; it is not the same as all interfaces or the IPv6 loopback address. If the application uses a different address family, check that both sides use compatible addresses rather than relying on the name “localhost.”

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

This example lets local applications use a SOCKS inbound without authentication and enables UDP forwarding support. udp does not mean that all UDP traffic on the system is intercepted. The application still needs to use the relevant SOCKS mechanism, and the outbound must be able to handle that traffic. A successful web page test usually proves only that the current TCP request worked; it says nothing about voice calls, real-time communication, or other UDP applications. If results differ, check application support, inbound capabilities, and the outbound path separately.

LAN Access and Security Boundaries

Allowing LAN connections takes more than changing the listen address to 0.0.0.0. Consider the host firewall, network interfaces, authentication, and permitted sources as well. Binding to all interfaces expands the reachable scope, which can change when the machine joins a different network. If access is needed on only one LAN interface, bind to its address and restrict sources with the firewall. Unless sharing is an explicit requirement, loopback binding is easier to manage and prevents other devices from using the local inbound by mistake.

Multiple processes cannot bind the same port on the same address and transport at once. A leftover client process, another tool using the same port, or two configs running simultaneously can all cause a bind failure. Close unnecessary instances first, identify which process owns the port, then decide whether to use another port. If you change the inbound port, update the application's proxy settings and the system proxy too. Changing only the JSON while leaving the old proxy address in place will usually result in a connection refusal, not a protocol handshake error.

Sniffing and Destination Overrides

Sniffing attempts to identify domains and other destination information from request data. For example, an application may resolve a domain to an IP address before sending that IP through the inbound; information in an HTTP header or TLS handshake may help recover the domain. This can make domain-based routing possible, but identification depends on the protocol data available and cannot recover a domain from every encrypted connection. If no identifying data is available, the target uses a special handshake, or the protocol is unsupported, routing may have to rely on the IP and other conditions.

destOverride controls how identified data affects the destination. Do not conflate “identify the domain” with “use it only for routing.” The available controls and default behavior vary by core. If you want sniffing to affect matching without rewriting the connection destination, first confirm that the implementation supports this behavior. To diagnose a change after enabling sniffing, test the same destination with sniffing on and off, then compare the destination and matching rule. Do not change the node and DNS at the same time.

Multiple inbounds can serve different purposes. For example, give test requests their own tag, then use inboundTag to route them to a dedicated outbound. A tag is a logical name; it does not create a port or enable the system proxy. Use names that describe their purpose rather than a series of hard-to-distinguish numbers. Test the listener first, then add sniffing and routing so you can separate inbound issues from policy issues. For basic graphical client setup, see the Quick Start Guide.

Outbounds: Choosing an Exit

An outbound determines how a request is handled after it is selected. It may connect directly to the destination, send traffic to a remote server using a proxy protocol, or actively reject the connection. Outbounds are not simply another name for a node list: direct and block are outbounds too, and neither requires a remote server. A config can contain multiple outbounds, but only the default path or those referenced by routing participate in request handling. Check which outbound was selected first, then inspect its settings to keep troubleshooting focused.

Basic Outbounds and Default Behavior

In a typical config, if no routing rule selects an outbound, the first outbound in the array is used by default. Changing the array order can therefore change where unmatched traffic goes. Document the purpose of the default outbound and give each referenced outbound a unique tag. Keep tag spelling and capitalization consistent throughout the config. A tag is not necessarily the same as the display name in the UI, so do not infer the generated tag from a node label.

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

In this example, direct is first, so requests go out through the local network unless another rule applies. blackhole blocks traffic and is used only when routing selects block. It is not a backup route and will not take over automatically if direct fails. Adding two outbounds to an array does not create failover. Load balancing, probing, and chaining are separate capabilities that require explicit configuration and support.

Keep Protocol and Transport Settings Separate

A remote proxy outbound typically has both protocol-level and transport-level settings. The former describe the server, port, authentication, and protocol features; the latter describe the underlying connection and its security parameters. Key fields must match on the client and server. A reachable remote port proves only that a network connection can be established, not that the authentication and transport combination is correct. When a handshake fails, compare the full parameters provided by the service instead of resetting every field to a familiar-looking default.

VMess, VLESS, Trojan, and Shadowsocks have different authentication structures and capabilities; changing only the protocol name does not convert one into another. Combinations involving TLS or REALITY must also be checked against the core's support. For help choosing between protocols, see Comparing Four Proxy Protocols by Use Case. This page does not provide a “universal node” with made-up remote credentials. The complete test config uses a direct outbound to isolate local configuration issues, not to simulate a working remote connection.

Domain Targets and Connection Handling

Whether a domain received by an outbound is resolved locally, by the built-in DNS, or remotely depends on the outbound protocol and its settings. freedom's domainStrategy and routing.domainStrategy have similar names but operate at different layers. The former affects how the direct outbound handles a domain target; the latter controls whether routing resolves a domain for matching. Setting the routing strategy to resolve IPs does not mean every outbound uses the same resolution process.

With multiple outbounds, distinguish the final destination from the proxy server's own address. The server's domain must also be resolved and connected to. If that traffic is routed through a path that depends on the server itself, a circular dependency can result. Graphical clients often handle necessary exclusions or direct routing, but custom configs require you to trace how the initial connection is established. If repeated timeouts occur before the handshake completes, first check server address resolution, underlying reachability, and outbound selection.

Observed BehaviorCheck FirstDo Not Assume
Local connection works, remote connection failsOutbound tag, server settings, transport conditionsThat DNS is the cause
No routing rule matchesDefault outbound and array orderThat all outbounds will be tried in turn
Direct works, proxy failsRemote path and protocol combinationThat the inbound is misconfigured

Edit node parameters through the client's node editor where possible, so subscription updates do not overwrite manual changes. If custom rules depend on a fixed outbound tag, confirm that the client generates it consistently and that it remains valid when you switch nodes. If the client regenerates tags, its routing options are usually more reliable than copying a runtime config. Keep a known-good baseline for comparison to distinguish an outbound failure from a routing selection error.

Routing Rules and Matching

Routing checks requests in rule order and sends matches to the specified outbound. It is not a system proxy switch and cannot make traffic appear in the core if it never entered. If an application still uses its normal network despite a valid rule, check the inbound first. If the request reaches the core but exits through the wrong route, check rule order, match fields, and tag references. Separating traffic interception from core routing is essential to understanding UI modes such as “Global” and “Bypass mainland China.”

Rule Order, Conditions, and Destination Data

Conditions within a rule generally all need to match—for example, a specified inbound tag and a port restriction—while multiple candidates in the same type of array provide alternatives. Once a rule matches, later ordinary rules do not override it. Put broad rules after specific exceptions; otherwise, the exceptions never get a chance to match. Domain conditions require usable domain information, while IP conditions require a destination address or a resolution result. A request that has only an IP will not automatically match a domain list.

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

The example first blocks a dedicated test domain, then handles private addresses, and finally sends the remaining TCP and UDP requests from a specified inbound to direct. blocked.example is a sample domain, not a credential to replace. The outbound tags here must match the ones defined in the previous section. Because the third rule is broad, add any destination-specific exception before it. A rule placed after it may never have a chance to match, even if the syntax is valid.

Domain Syntax and Rule Data Files

full: matches an exact domain, domain: commonly matches a domain and its subdomains, and regexp: specifies a regular expression. A domain without a prefix has its own matching semantics; do not assume it means an exact match. To avoid unintended matches, choose a prefix that directly expresses what you need rather than replacing a clear exact domain or suffix with a complex regular expression. Remember that JSON string escaping also applies to regex; backslashes must be escaped accordingly.

geoip: and geosite: refer to rule data, not live online searches. Before using them, confirm that the core can read the required data files and that they contain the referenced categories. The existence of a category does not mean it covers every target, and updates can change its matching scope. If a site uses an unexpected outbound, compare the actual domains visited, related resources, and data contents. A single page often makes multiple independent requests, so the address bar alone is not enough.

When domainStrategy Resolves Names

AsIs matches using the target information already available without actively resolving a domain for IP rules. IPIfNonMatch tries to resolve the domain and apply IP conditions if no domain rule matches. IPOnDemand triggers resolution when matching requires IP information. These settings control how information is obtained during matching; they are not a fixed ranking of “faster” to “more complete.” Resolving too early adds dependencies, while resolving too little may prevent expected IP classifications from being considered.

Pay particular attention to catch-all rules. If an earlier rule already matches every request, IPIfNonMatch may find a match before reaching the resolution stage you expected. The problem is not that DNS is broken; matching has already ended. Design the resolution strategy around rule order rather than switching a single field. Private-address rules also match only addresses available to the core. If a domain resolves to a private address, verify when that resolution occurs.

A client’s “Bypass mainland China” mode is usually a set of rules, not a single option that behaves identically across all cores. Config templates can use different domain lists, IP categories, and default outbounds. To understand what it actually does, inspect the active rules rather than relying on the mode name. For help choosing a mode, see System Proxy, Global, and Bypass Mainland China Modes. If speeds differ, distinguish rule selection, resolution paths, and the remote route rather than attributing everything to the mode name.

DNS and Name Resolution

Start by asking: which component makes the query, where does it go, and who uses the result? The operating system resolver, a browser's own resolution mechanism, the core's built-in DNS, and remote server resolution may all be involved. Changing the core's dns settings does not replace every application's resolver or route all system queries through the core. These settings affect results only when the relevant request path actually uses the built-in resolver.

DNS Servers and Static Mappings

servers lists the servers or related settings used by built-in DNS; hosts defines static domain mappings. The example below maps a test domain to the loopback address and sends other queries through the local resolution mechanism. Static mappings are useful for isolated tests, not for permanently pinning frequently changing service addresses. Services may return different results based on region, load, and address family. A fixed address bypasses those choices and may stop working when the service changes its infrastructure.

{
  "dns": {
    "hosts": {
      "full:manual.example": "127.0.0.1"
    },
    "servers": [
      "localhost"
    ]
  }
}

This config does not make every browser resolve the test domain to loopback. The request must actually use the core's built-in DNS. If the direct outbound uses the system resolver or the application has already resolved the domain, the result may differ. When testing a static mapping, verify the query path too. Remove the test mapping when finished so a failed loopback connection is not mistaken for a problem with the real destination. In production, every mapping should have a clear purpose and a traceable maintenance history.

Routing Resolution vs. Outbound Resolution

Routing may resolve a domain to apply an IP rule, while the outbound may still use the domain to establish the connection. A routing lookup does not guarantee that the final connection uses the same resolved address. Whether a direct outbound uses built-in DNS depends on its domain strategy; whether a proxy outbound resolves the target locally depends on its protocol and settings. For a request, record separately what information routing used and what the outbound used to connect. A single DNS log will not tell the whole story.

An application may also resolve a domain first and then send the IP to a SOCKS inbound. The core may not know the original domain; sniffing can sometimes recover it, but is not guaranteed to. To test domain handling in the core, use a method that passes the domain to the proxy and confirm that the application has not replaced it with an address. To compare local and proxy-side resolution, control that variable explicitly rather than guessing which browser, system, or client cache is involved.

DNS Transport and Circular Dependencies

Specifying a DNS server address does not automatically send queries through a remote proxy. Different DNS server formats may use different transport paths and should be analyzed separately. If an encrypted DNS endpoint uses a domain name that must be resolved before it can connect, determine how that initial lookup works. A circular dependency can occur if the DNS server requires a proxy path while the proxy server's domain depends on that DNS server. Make sure the initial connection has a clear, reachable path.

Do not replace multiple resolvers and change the routing strategy at the same time. Record the current returned address first, then change one part of the query path and test the same target again. Multiple DNS servers are not necessarily tried one at a time in the order written; behavior can depend on matching conditions, concurrency, fallback, and implementation details. If you need to select a resolver by domain, check which server-object fields the active core supports. Do not assume that “primary” and “backup” mean the same thing here as in another tool.

SymptomLayers to ConsiderHow to Verify
Address unchanged after changing DNSApplication cache, system resolver, existing connectionCheck whether the query reaches built-in DNS
Domain fails, IP address worksResolution failure or hostname-dependent handshakeDistinguish DNS failure from server name requirements
Some destinations take a long time to connectAddress family, returned addresses, route conditionsObserve resolution and connection setup separately

Address Families and Cache Boundaries

When both IPv4 and IPv6 addresses are available, the outcome also depends on the local network, outbound support, and connection selection logic. Do not disable an address family just because one request took a long time. First check which addresses were returned, which connection attempt failed, and whether a fallback occurred. A successful lookup only means that addresses were returned, not that they are reachable. Likewise, a reachable IP does not mean you can access a site by IP instead of its domain; TLS server names and virtual hosting may still require the original domain.

Caching makes tests more consistent but can hide configuration changes. After editing, start a new request and, if necessary, clear the relevant cache using the application's supported method. Compare records from before and after a core restart separately. Restarting all software should not be your only diagnostic step; identifying which layer holds the cache explains why the issue occurs. If overall performance is slow, see Layered Troubleshooting for Slow Speeds and measure DNS wait time separately from remote transfer speed.

Connection Policies

Connection policies manage timers and resources; they do not choose routes or replace an application's own timeout settings. If connections drop frequently, do not start by increasing every timeout. First determine whether the handshake failed, an established connection sat idle, or one side finished sending. Different fields apply to different stages, and applications, operating systems, and remote devices may each have their own timers. Policy changes are meaningful only once you know when the disconnect occurs.

Policy Levels and User Association

policy.levels organizes policies by level. JSON object keys must be strings, such as "0". Protocol configs that support user levels can associate a policy through the relevant level field; the exact scope depends on the protocol implementation and the generated config. Defining a policy for a level does not mean every connection automatically uses it. When troubleshooting, check the level assigned to the connection's user and the default behavior when no level is specified.

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

The example uses seconds to illustrate connection-stage limits; these are not recommended values for every network. handshake sets the wait limit for the relevant handshake stage, connIdle limits idle time, and uplinkOnly and downlinkOnly apply to wait limits during one-way transfer stages. Understand these fields in the context of the core documentation and connection state; they are not simply equivalent to a browser's page-load timeout.

Handshake, Idle Time, and Half-Close

A failed handshake may be caused by mismatched parameters, not just a slow connection. If authentication or security settings are wrong, a longer handshake timeout only delays the failure. A small increase makes sense only when evidence shows that the connection genuinely needs more time to establish. Compare connection start, first data received, and error timestamps. Also check which layer the log calls a handshake: an application session, proxy protocol, and underlying transport are not the same stage.

An idle connection is not the same as a slow one. A long-lived application may expect a session to stay open without sending data for a while; other applications use heartbeats to keep it active. If a policy timeout conflicts with the heartbeat interval, the connection may drop at regular intervals. Check how long it takes to disconnect, whether any data was sent, and whether the remote host or an intermediary closed the connection first. Increasing the local idle timeout will not stop the remote end from closing its own session, nor will it recover a connection lost during a wireless network switch.

A half-close means one side has finished sending while the other may still have data to deliver. Some protocols rely on this phase to complete a response, so a short one-way wait limit can truncate data. On the other hand, setting every limit too high can tie up resources with connections that are no longer useful. Reproduce the issue with the specific application, check whether the response is complete and which side closes first, then adjust the relevant field. One successful page load does not prove that long-lived or one-way transfer scenarios are covered.

Statistics and Buffers Are Not Speed Switches

Policies may include fields related to user or system traffic statistics, but enabling a counter is only one part of the setup. Displaying that data in a client may also require a statistics module, an interface, and client-side support. Copying a statistics toggle alone will not make a complete set of UI data appear. Likewise, buffer settings affect memory use and data handling, but cannot overcome remote bandwidth, congestion, or server limits. A larger buffer does not make every request faster.

For graphical clients, keep the generated baseline policy unless you have a clear, reproducible reason to change it. A client may set defaults for its core and features; overriding the entire section can unintentionally remove existing fields. If you need to change only one value, first check whether the client merges individual fields or replaces the whole section. Compare configs before and after, especially to confirm that levels and system policies you did not intend to change are still present. Otherwise, an apparent timeout tweak may actually remove other settings.

Test policy changes with the same application and steps, and define an observable success criterion, such as whether data can still be sent after an idle period or a one-way response completes. If the issue is not reproducible, keep the original values and gather more evidence instead of changing handshake, idle, and buffer settings at once. On memory-constrained systems, monitor concurrent connections and log volume too, so resource pressure is not mistaken for a network issue. The goal is to match policy limits to actual usage, not to make every value as large as possible.

Complete Example and Step-by-Step Testing

Start by testing the config structure with only a local inbound and a direct outbound, then add a real node and more complex routing. This keeps remote authentication, transport conditions, and subscription generation out of the initial test. The complete basic config below accepts local SOCKS requests, sends a specified test domain to a block outbound, and routes everything else directly. It does not create a remote proxy connection or configure the system proxy for applications. Use it to verify the references between inbounds, rules, and outbounds.

Runnable Local Test Configuration

{
  "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": {
        "domainStrategy": "AsIs"
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:blocked.example"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "inboundTag": ["local-socks"],
        "outboundTag": "direct"
      }
    ]
  },
  "dns": {
    "servers": ["localhost"]
  },
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5
      }
    }
  }
}

Before running it, stop any other instance using the same listening port. If you use a client's custom configuration option, confirm that it accepts a complete config rather than only a section; the structures are different. After saving, check whether the core starts, then verify the listener. Sniffing is disabled in this test so the rule check depends directly on the destination name submitted by the application. The dns section is included as a baseline, but with AsIs on the direct outbound, do not assume requests necessarily use built-in DNS.

Test the Inbound, Match, and Outbound in Stages

First, confirm that the application can connect to the local port. If the connection is refused immediately, check that the process is running, the address families match, and the port has not changed. Next, submit a domain as the destination so the application does not resolve it to an IP first. Then compare a regular destination with the blocked test destination. The commands below apply in environments where the relevant command-line tool is available; they send requests only to a running local inbound. If your terminal defines that tool name as an alias for another command, use the actual executable instead.

curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 --max-time 20 -I https://v2raygpu.com/
curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 --max-time 20 -I https://blocked.example/

socks5h passes the domain to the proxy inbound, making it useful for testing exact-domain rules. Success on the first request also depends on the local network and target site being available. The second request should be blocked, but failure alone does not prove the rule matched: the example domain is not a normal service destination. Check the request target and outbound logs as well, or temporarily substitute a reachable destination in a controlled test and restore the rule afterward. Do not treat every timeout as proof that blocking worked.

Once the basic path is verified, import an existing real node through the client and add it to the outbound selection. Add one capability at a time: verify the remote outbound first, then add domain rules, and finally add DNS conditions. If importing the node causes an immediate failure, compare each addition against the baseline. Do not overwrite the client's generated configuration with the full test config and assume the original nodes and routes are still there. When the configuration source changes, start the verification process again.

Troubleshooting Notes and Rollback

When documenting an issue, note the client, actual core, configuration source, inbound address, test destination, and expected outbound. Remove credentials but preserve tag relationships; otherwise, it is impossible to tell whether routing references are valid. If only one application fails, compare its inbound method, how it passes domain names, and how it reuses connections with a working application. If all applications fail, troubleshoot startup, listening, resolution, and remote connections in that order instead of reinstalling the client immediately.

Roll back changes in reverse order. First remove the last rule or field you added, restart, and test again. If the issue remains, switch back to a known-good configuration source. Separate changes to node data caused by a subscription update from changes to the local template; do not mix an old file with a new UI and expect consistent results. For platform-specific installation, see the downloads page. For basic connection steps, return to the Quick Start Guide; for terminology, see the glossary.

Finally, check that the goal was actually met: the syntax is valid, the listener works, requests reach the core, the outbound matches the rules, the resolution path is clear, and connections end within the expected limits. Keep an observation point for anything that remains uncertain. Snippets in this guide explain individual modules; only configs labeled as complete examples should be used as a whole. When copying a section, merge it at the correct level and retain the other required modules. A stable config comes from dependencies you can verify repeatedly, not from adding more and more fields.