This guide is for users who have imported a subscription and want to understand the core config. Start with a JSON example containing a local SOCKS inbound, direct and block outbounds, then review logs, DNS, ports, tags, and rule order. By the end, you’ll know where to look when the core fails to start, a local port won’t connect, or a routing rule doesn’t match.
Client settings vs. core config
v2rayN is a desktop client for managing subscriptions, server entries, routing presets, and core processes. V2Ray or Xray is the program that reads the core config and handles connections. Client preferences and subscription group data aren’t the same as the runtime JSON passed to the core. When troubleshooting, find the file actually loaded at startup—don’t edit a file just because it ends in .json.
On Android, v2rayNG uses the Xray core, while v2flyNG uses the V2Fly core. This guide covers common V2Ray config structures; it doesn’t mean every field works across both cores. For example, REALITY is an Xray-related feature, so don’t assume its config can be used with V2Fly. Even when both support streamSettings, check the current core’s supported transport names and options.
This is the main path to follow when reading the config. DNS comes into play when a domain needs resolving, and logs record the process. The system proxy only affects apps configured to use it; having a SOCKS inbound doesn’t mean all app traffic reaches the core. If traffic isn’t reaching the inbound, changing routing rules usually won’t help.
- Identify the core: Check the startup log to see whether V2Ray or Xray is running—don’t rely on the client name alone.
- Find the config file: Check the startup log or process arguments for its path. The path and filename may vary by client version.
- Keep a copy: Copy the runtime config before inspecting it. Subscription updates, switching servers, or restarting the core may regenerate the file.
Complete JSON example: One inbound, two outbounds, two rules
This teaching example has no remote proxy server. It provides SOCKS on 127.0.0.1:10808, blocks requests to the exact domain example.org and TCP traffic on port 25, and sends all other connections direct by default. Don’t use it as a replacement for an existing subscription config, or you’ll lose its proxy outbounds.
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": [
"1.1.1.1"
]
},
"inbounds": [
{
"tag": "socks-local",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"inboundTag": [
"socks-local"
],
"domain": [
"full:example.org"
],
"outboundTag": "block"
},
{
"type": "field",
"network": "tcp",
"port": "25",
"outboundTag": "block"
}
]
}
}
The order of top-level object keys usually doesn’t affect processing; they’re arranged here for readability. Array order, however, can affect behavior—especially the first item in outbounds and the order of routing.rules. Don’t reorder these arrays just to tidy up the file.
log: Use logs to tell startup errors from connection errors
log controls log output; it doesn’t select a server. This example sets only loglevel and doesn’t specify a log file. Where logs appear depends on how the core was started; the client may collect them in its own log panel. When troubleshooting, check the first error after startup, then look for new entries when you reproduce the issue.
| Field or symptom | What it means | What to check next |
|---|---|---|
loglevel: warning | Warnings and errors only, keeping routine output to a minimum. | Temporarily switch to info for more connection details. |
loglevel: debug | Provides more debug detail, but logs grow faster. | Reproduce the issue once, then restore the previous level. Remove sensitive information before sharing logs. |
address already in use | The listening address or port is already in use by another process. | Check whether another core instance is already listening on port 10808. |
| Config parsing or loading error | The core hasn’t reached the stage where it can handle connections. | Check field names, value types, and whether the current core supports the option. |
If the core exits immediately after launch, fix the config or port conflict first—not DNS. If it runs normally but a specific request fails, check the destination, routing result, and connection error. Logs may contain visited domains, server addresses, and local paths; don’t publish the full runtime config or logs publicly.
dns: DNS servers and routing strategy are separate settings
dns.servers specifies the DNS servers used by the core’s built-in resolver. The 1.1.1.1 in this example is a standard DNS server address, typically queried over UDP port 53. No encrypted DNS is configured here, and queries don’t automatically go through a proxy. Verify that the resolver is reachable on your network.
Two fields with the same name are easy to confuse: routing.domainStrategy controls how domains are handled during route matching, while settings.domainStrategy in a freedom outbound controls how the destination is resolved when making a direct connection. They belong to different objects and have different values and effects.
During route matching
- Location
routing.domainStrategy- Example value
AsIs- What it does
- Doesn’t proactively resolve the destination domain for route matching.
- Other common values
IPIfNonMatch、IPOnDemand
AsIs doesn’t mean DNS queries never happen during the connection.
During direct outbound connection
- Location
freedom.settings.domainStrategy- Example value
UseIP- What it does
- Resolves the destination domain to an IP using built-in DNS, then connects.
- Resolver source
- This example’s
dns.servers
Don’t use the routing value IPIfNonMatch here.
IPIfNonMatch attempts to resolve a domain if no rule matches, then checks IP rules. IPOnDemand triggers a lookup when route matching needs an IP. Neither setting forces all traffic through a proxy. If your config has no IP rules, keep the strategy simple rather than adding extra DNS behavior just to explain a domain rule.
- A browser’s own DNS lookups aren’t automatically sent to the core just because the config includes a
dnsobject. - If an app sends only an IP address to the SOCKS inbound, the core may not receive the original domain, so domain rules may not match.
- Built-in DNS settings don’t change the operating system’s DNS settings. Check them separately.
inbounds: Check which inbound the app connects to
inbounds is an array; each item describes an inbound that accepts connections. In this example, listen is set to the loopback address, port is the number 10808, and protocol is socks. Together, these fields specify where an app should connect and which protocol to use.
- Check the listening address:
127.0.0.1is accessible only on the local machine. Changing it to0.0.0.0exposes the listener on all interfaces; don’t expose an unauthenticated inbound just to troubleshoot. - Check the protocol: A SOCKS port isn’t an HTTP proxy port. Pointing an HTTP-only app to this example’s port 10808 won’t tell you whether the server is working.
- Check the actual port: 10808 is the value used in this guide, not a universal client default. In v2rayN, check the local listener options under Settings → Parameter Settings. The runtime config and logs are the final authority.
- Check UDP:
udp: truelets this SOCKS inbound handle UDP, but the app must also use SOCKS UDP, and the outbound and network must support that traffic.
tag: socks-local is an internal inbound name, not a domain or subscription server name. It doesn’t change the listening protocol; it lets other config sections refer to this inbound. The first rule uses an array for inboundTag because a rule can match multiple inbound tags.
"inboundTag": [
"socks-local"
]
If you change an inbound tag, update every rule that references it. Changing socks-local to local-socks on the inbound but leaving the original rule unchanged means that rule won’t match its traffic.
outbounds: Tags, protocols, and the default outbound
outbounds defines how a connection is sent onward. In this example, freedom means direct and blackhole means blocked; neither is a remote proxy protocol. A real subscription-generated config may include VMess or VLESS outbounds with server addresses, ports, user IDs, and transport settings.
| Item | In this example | What to know |
|---|---|---|
tag | direct、block | A name referenced by rules; spelling and capitalization must match exactly. |
protocol: freedom | Direct outbound | Connects to the destination directly from the device running the core. |
protocol: blackhole | Block outbound | Rejects matching traffic; it doesn’t automatically try another outbound. |
| VMess、VLESS | Not configured in this example | Requires valid server and authentication settings; changing only the protocol name won’t work. |
streamSettings | Not needed in this example | Describes the transport and security layers; settings must match the server. |
In this example, with no other default dispatch mechanism configured, connections that don’t match a routing rule use the first item in outbounds, so they go direct by default. Moving block to the first position changes the fallback behavior; renaming a tag doesn’t change the array order.
Takeaway: Check the default outbound before analyzing exceptions
Open the runtime config and note the tag on the first item in outbounds. Then check each routing.rules entry’s outboundTag. This is more reliable than searching for “proxy,” since the config generator determines tag names.
If the real config uses proxy as the proxy outbound tag, each routing outboundTag: proxy must point to a matching outbound. Adding a rule that targets proxy to this example won’t create a proxy connection. Keep and verify the valid proxy outbound generated by the client.
routing: Rules run top to bottom; understand AND vs. OR
routing.rules are generally checked from top to bottom. When a rule matches, its specified outbound is used, and later standard rules aren’t checked. Put specific rules before broader ones. A rule being present in the config doesn’t mean it will get a chance to run.
Different condition fields in a rule generally work as AND conditions, while multiple values in an array for the same field generally work as OR conditions. The first rule in this example requires both the inbound tag socks-local and the destination domain example.org to match before sending the request to block.
full:example.org: Matches this exact domain, notwww.example.org.domain:example.org: Matches this domain and its subdomains; use it when you need to cover the whole domain tree.network: tcpandport: "25": The second rule in this example matches TCP traffic to destination port 25. This isn’t the local listening port.outboundTag: block: Routes the request to the block outbound; it doesn’t continue to the next rule.
| Requests sent to the local SOCKS inbound | How it matches | Result |
|---|---|---|
| Domain example.org, TCP 443 | Both the inbound and exact-domain conditions in the first rule match. | block |
| Domain www.example.org, TCP 443 | Doesn’t match the exact domain or destination port 25. | Default direct |
| Other domain, TCP 25 | The first rule doesn’t match; the second does. | block |
| Other domain, TCP 443 | Neither rule matches. | Default direct |
This walkthrough assumes the app sends the domain name to the SOCKS inbound. If the app resolves it first and sends only the IP, the first domain rule may not work. Sniffing can identify domain information in some protocols, but it can’t always recover the original domain. Sniffing isn’t enabled here; test the rule with a request that explicitly uses the domain name.
Config checks and common questions
Change one variable at a time and note the logs before and after each change. First confirm the core starts, then check that the inbound is reachable, and finally verify rules and outbounds. Changing ports, DNS, tags, and transport settings all at once makes it hard to tell what caused a success or failure.
- Check syntax and types: Port
10808is a number,udpis the booleantrue, andinboundsandoutboundsare arrays. - Check every reference: Find each
inboundTagandoutboundTag, and confirm that a matchingtagexists in the corresponding inbound or outbound. - Confirm the app uses the inbound: When testing this example, specify the SOCKS address explicitly so browser system proxy settings don’t affect the result.
- Trace the routing: Start with the first rule and check the destination domain, destination port, network type, and inbound tag.
- Return to client-managed settings: Once you understand the behavior, make persistent changes using routing or config options supported by the client, then regenerate and test the runtime config.
If curl with SOCKS support is installed, run the command below while the core is using this example and the port is available. socks5h sends the destination domain to the SOCKS server for resolution. This example blocks that request with its exact-domain rule, so a failed request is the expected test result—not evidence of a remote proxy failure.
curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 https://example.org/
Why does my JSON change revert after restarting v2rayN?
The client may regenerate the runtime file. Keep a record of your changes, then check the corresponding rule under Settings → Routing Settings, or use a custom config option explicitly supported by your current version. Don’t treat a temporary runtime file as a permanent source of settings.
Why does startup fail when outboundTag is set to proxy?
Find the item with the tag proxy in the outbounds array and check for spelling or capitalization differences. If it’s missing, restore a valid proxy outbound config rather than simply renaming freedom.
Why don’t my domain rules match after changing DNS to 1.1.1.1?
First check whether the app sends the domain name, then verify rule order and the full or domain prefix. Changing the DNS server won’t restore domain information the app has already discarded or change rule priority.
Can I replace the VMess protocol field with VLESS for an outbound?
No. The two protocols use different authentication fields and parameters, and their transport and security settings must also match the server. Reimport the correct subscription or connection details, then check which fields the running core supports.
For more on routing, see the routing reference. To check DNS settings in the client, follow the DNS setup steps in the guide. Once you understand the core config, compare it with the client interface to avoid mistaking subscriptions, inbounds, and routing for the same setting.