For readers who have imported a subscription but find the config file hard to follow. A complete example with a local inbound, direct connection, and blocking rule traces the request flow, then explains ports, outbound tags, rule order, and DNS behavior. It contains no remote proxy server and cannot replace a subscription. By the end, you’ll know whether to change a client setting or inspect the core config.
1. The five main sections of config.json
config.json is the config file read by the core. It is not a subscription link or the client’s complete settings. v2rayN manages nodes, subscription groups, and interface options, then converts your selections into a config the core can run. The same filename does not mean every field is interchangeable between V2Ray and Xray.
You don’t have to read the file from top to bottom. Start with inbounds to see where requests enter, then check how routing selects a path, and finally use outbounds to identify the actual exit. log helps with troubleshooting, while dns defines name resolution settings the core may use.
| Top-level field | What it does | What to check first |
|---|---|---|
log | Sets the log level and output location | Whether the current log level records the error |
inbounds | Accepts connections from browsers and other apps | Listen address, port, and inbound protocol |
outbounds | Defines direct, blocked, or remote proxy connections | Protocol, tag, and server settings |
routing | Routes connections to an outbound based on rules | Rule order and destination tag |
dns | Configures the built-in DNS resolver | Resolver address and when it is actually used |
2. Complete example: local inbounds, direct connections, and domain blocking
This example uses traditional JSON fields found in V2Ray and Xray, making it easier to follow. It provides two inbounds accessible only from the local machine: SOCKS on port 10808 and HTTP on port 10809. Connections to example.org and its subdomains are blocked; all other connections through these inbounds go direct.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": false
}
},
{
"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": [
"domain:example.org"
],
"outboundTag": "block"
},
{
"type": "field",
"inboundTag": [
"local-socks",
"local-http"
],
"outboundTag": "direct"
}
]
},
"dns": {
"servers": [
"1.1.1.1"
]
}
}
These ports are explicitly set in the example; they are not defaults for every client version. JSON objects use braces and arrays use square brackets. Keys and strings must use double quotes, and the last item must not have a trailing comma. Comments are omitted for compatibility across cores.
3. inbounds: listen address, port, and protocol
inbounds is an array, so a core can provide multiple local entry points. listen selects the network interface, port tells apps which port to connect to, and protocol specifies the proxy protocol an app should use. Check all three together; don’t rely on the port number alone.
Local SOCKS inbound
- tag
local-socks- listen
127.0.0.1- port
10808- protocol
socks- udp
false
Configure the app to use a SOCKS proxy. This example does not accept SOCKS UDP relay traffic.
Local HTTP inbound
- tag
local-http- listen
127.0.0.1- port
10809- protocol
http- settings
{}, using default settings
Configure the app to use an HTTP proxy. HTTPS destinations typically use CONNECT to establish a tunnel.
127.0.0.1 allows connections only from the local machine. Changing it to 0.0.0.0 listens on all IPv4 interfaces, potentially exposing the inbound to other devices on your local network. The example SOCKS inbound uses noauth, so don’t widen its listen scope just to troubleshoot.
- Apps can’t connect to an inbound: Check that the app’s proxy type and port match. Don’t enter an HTTP proxy on the SOCKS port.
- The core reports that the port is in use: Check whether two client instances are running or an old core process is still active.
- Browser traffic isn’t going through the proxy: Check whether the system proxy or the browser’s own proxy setting is enabled. Starting the core does not automatically change every app’s network route.
In v2rayN, look under Settings → Parameter Settings for local listening port options. Then, from the main window’s System Proxy menu, select Auto-configure system proxy. Menu locations may vary by version; use the labels and ports shown in your current interface.
4. outbounds: protocols and their tag mappings
outbounds is also an array, but it describes how requests leave the core. In this example, freedom connects directly to the destination, while blackhole blocks the connection. direct and block are custom tags, not protocol names.
| Field or protocol | Role in this example | What to watch when editing |
|---|---|---|
tag | Lets routing rules refer to an outbound | If you rename it, update every reference and match the capitalization |
freedom | Connects directly to the destination | It is not a remote proxy and does not provide a proxy server |
blackhole | Terminates matching connections | It is not a fallback outbound and won’t switch nodes automatically |
settings | Carries the settings for the selected protocol | Protocol settings are not interchangeable |
A working proxy outbound also needs a server address, port, user ID, and transport and security settings that match the server. VMess and VLESS use different user parameters, while TLS, REALITY, and WebSocket each have their own fields and requirements. Changing "protocol": "freedom" to "vless" does not complete the configuration.
Bottom line: check the tag, then verify the server settings
If a route points to proxy, first confirm that the outbound array contains "tag": "proxy". Once the tag reference is correct, check the node settings. Otherwise, even complete server details won’t send requests to the intended outbound.
Connections that match no rule usually use the first outbound in the array. This is not automatic failover. The example puts direct first and uses a second rule to explicitly handle both local inbounds, making the match behavior easier to follow.
5. routing: rule order and domain matching
Common field rules are checked in array order. Once a rule matches, its outbound is selected; the core won’t keep looking for a “more specific” rule. This example blocks the specified domain first, then sends other traffic from the two inbounds direct. Swap the rules and the inbound rule will match first, preventing the domain block from taking effect.
- Receive the destination: The app passes a domain or IP address through the inbound, and the core uses it to make a routing decision.
- Check the first rule:
domain:example.orgmatches the domain and its subdomains. Matching connections useblock. - Check the second rule: If the first rule doesn’t match, connections from
local-socksorlocal-httpusedirect. - Handle unmatched connections: If other inbounds exist and no rule matches, the default outbound behavior applies.
Different conditions in the same rule usually all need to match. Multiple values in an array for one condition usually mean any one of them can match. For example, putting both inboundTag and domain in one rule means “from one of these inbounds and the destination domain matches,” not that either condition alone is enough.
| Syntax | What it matches | How to read it |
|---|---|---|
domain:example.org | The domain itself and its subdomains | Useful for matching an entire domain namespace |
full:example.org | An exact match for the full domain | Does not include all subdomains |
inboundTag | Matches the inbound tag on the connection | Not a server name or subscription group name |
outboundTag | Sets the outbound tag to use on a match | Must match an existing outbound |
The example does not configure traffic sniffing. If an app resolves a domain to an IP address and passes only the IP to the core, a domain rule may not match. Check first whether the app is passing the domain through the proxy; don’t immediately broaden the blocking rule. Domain routing also can’t match URL paths: for example, /account is not a domain condition.
6. dns: resolver settings don’t take over system DNS
dns.servers defines the servers used by the built-in DNS resolver. In this example, 1.1.1.1 is an explicitly configured resolver address. Adding it does not change system DNS or guarantee that a browser’s encrypted DNS will use it. Whether the built-in resolver is queried depends on routing policy, outbound settings, and the type of request.
AsIs: Use the destination information already available during routing; don’t resolve domains just to evaluate IP rules. The example uses this value to make domain rule behavior easier to see.IPIfNonMatch: If no domain rule matches, the destination domain can be resolved before checking IP rules.IPOnDemand: Resolve a domain when the matching process reaches a rule that requires an IP address.
These values belong to routing.domainStrategy; don’t confuse them with an outbound’s own domainStrategy. Routing decides which outbound to use, while an outbound’s resolution strategy affects how it connects to the destination. The names are similar, but they apply at different stages.
If a config uses geoip: or geosite: conditions, check that the required data files are available in the runtime environment. This example omits those conditions so missing data files don’t get confused with JSON structure problems.
7. log and safe changes: change one variable at a time
The example sets loglevel to warning, which captures warnings and errors but may not show routing details for every connection. Temporarily switch to info to investigate a problem; more verbose debug logging is best for brief diagnostic sessions. Logs may contain destination domains and addresses, so remove sensitive details before sharing them.
- Identify the config source: First determine whether this is a runtime file generated by the client or a separately managed custom config. Don’t edit similarly named files in the installation directory without checking.
- Save the current state: Note the selected node, system proxy status, and listening ports. Keep a copy of the original config so you can roll back.
- Use the UI first: Change local ports under Settings → Parameter Settings in v2rayN. Check routing rules under Settings → Routing Settings. Names may vary by version.
- Change one setting at a time: Change the port and verify the inbound first, then adjust rule order. Don’t change DNS, transport settings, and outbound tags all at once.
- Check the first error after reloading: Fix JSON parsing, port binding, or tag reference errors before checking network reachability.
- Restore the usual log level: When troubleshooting is complete, switch back to a suitable level to avoid keeping excessive connection details in the logs.
Bottom line: change client-generated configs at their source
If a manually edited config.json reverts after you switch nodes, change the relevant client setting instead. Edit the full JSON directly only if you’ve confirmed that you’re using a custom config and understand how it is loaded.
v2rayN is a desktop client. On Android, v2rayNG uses the Xray core, while v2flyNG uses the V2Fly core. Their import workflows and supported fields differ. Successfully importing a subscription only means the client parsed it; it does not guarantee that every protocol combination is supported by the core currently in use.
8. Troubleshooting config errors and pages that won’t load
Break the problem into four layers: “Did the config load?”, “Is the app using the proxy?”, “Did routing choose the right outbound?”, and “Can the outbound connect?” This is usually more useful than repeatedly importing the subscription. The symptoms below can help narrow things down, but a single log line is not enough to conclude that the server is at fault.
| Symptom | Check first | Next step |
|---|---|---|
| Core exits immediately on startup | Brackets, commas, field types, and the first startup error | Undo the most recent change and compare the config before and after |
| Address already in use | Whether another process is listening on 10808 or 10809 | Close duplicate instances, or change the port in the client and update the app settings to match |
| Outbound not found | Whether outboundTag exactly matches the outbound tag | Fix the reference, checking for capitalization or spelling differences |
| Domain block isn’t working | Rule order, inbound tag, and whether the destination is still a domain | Verify the match conditions first; don’t switch straight to blocking everything |
| Config loads, but pages time out | App proxy type, current outbound, destination resolution, and connection errors | Test the inbound and outbound separately to distinguish a direct connection failure from a remote proxy failure |
Keep three distinctions in mind: the inbound port is not the remote server port; an outbound tag is not a protocol name; and DNS settings do not take over system traffic. Once these are clear, it’s easier to read real node configs and understand where complex transport settings belong.