config.json Structure Explained: What inbounds, outbounds, and routing Do
A minimal config explained: local listeners, remote connections, routing decisions, and how tags link the full traffic path.
Whether v2rayN generates the configuration from a subscription or an administrator prepares it by hand, the structure comes down to three questions: where traffic enters, where it should leave, and which conditions select the exit. Once these are clear, issues such as “the port connects but websites do not load,” “a direct domain still uses the proxy,” and “a blocking rule never matches” become much easier to troubleshoot.
This guide is for users who can already import a subscription and want to understand the underlying configuration. You will learn what inbounds, outbounds, routing, and tags do; how to read a setup with a SOCKS inbound, a VMess proxy, a direct route, and a blocking route; and how to trace missed rules, port conflicts, and invalid outbound references through the logs.
Build the full traffic path: inbound, match, outbound
Applications do not connect to routing directly. A browser or another program first sends a request to a local listening port. The inbound accepts it and creates target information for the core; routing then reads the domain, IP, port, network type, and inbound tag. Once a rule matches, its outboundTag points to an item in outbounds. If no rule matches, the first item in the outbounds array is usually used as the default exit, so array order is part of the configuration logic.
Think of config.json as a directed connection graph rather than a collection of unrelated parameters. Tags in inbounds identify traffic sources, tags in outbounds name exits, and routing.rules uses those names to connect them. A tag does not carry data or create a proxy chain automatically; it is simply a stable, case-sensitive reference key inside the core.
inbounds: define how the local machine accepts traffic
inbounds is an array of inbound entries, with each item representing a listening endpoint. Common desktop entries include SOCKS, HTTP, and client-managed transparent proxy listeners. A basic SOCKS inbound should specify listen, port, protocol, and settings. With 127.0.0.1, only local programs can connect; with 0.0.0.0, every network interface listens, potentially exposing the port to devices on the LAN. These two values are not interchangeable.
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
]
}
Read the inbound configuration field by field
tag: Names this listenersocks-in. Later rules can useinboundTagto process only requests from this entry.listen: Sets the bind address.127.0.0.1is the local loopback address, suitable for a client used only on the current computer.port: Sets the listening port. The example uses 10808, and the browser or application’s SOCKS5 proxy port must also be set to 10808.protocol: Declares how the application communicates with the local core. Here it is SOCKS; this does not mean the remote node also uses SOCKS.settings.udp: Allows the SOCKS inbound to accept UDP requests. It only enables inbound support; actual use still depends on the remote protocol, transport, and target application.sniffing: Recovers the destination domain from an HTTP request or TLS handshake, giving domain-based routing rules a chance to match.
A port conflict occurs when the core creates its listening socket. If another process already uses 10808, the inbound never starts accepting traffic, so routing and outbounds are not reached. In v2rayN, open “Settings” → “Parameter settings” to change the local SOCKS port. After changing it to 10818, update the browser proxy, system proxy, and any other application using that port.
outbounds: define proxy, direct, and blocking exits
outbounds is an array of exits. Each entry needs a unique tag, while protocol determines how the core handles traffic leaving the client. A remote proxy exit contains the server address, port, user ID, and transport settings; a direct exit uses freedom; and an exit that actively terminates connections uses blackhole. All three can coexist, with routing selecting the appropriate one.
proxy: VMess proxy outbound
- Protocol
- VMess
- Server port
- 443
- Transport
- WebSocket
- Transport security
- TLS
Node parameters are usually imported from a subscription. address, id, path, and transport security must match the server.
direct: direct connection
- Protocol
- freedom
- Remote node
- Not required
- Typical use
- LAN and specified domains
- Reference tag
- direct
Direct means the local network accesses the target without going through the proxy outbound.
block: terminate the connection
- Protocol
- blackhole
- Remote connection
- Not established
- Typical use
- Ad-domain rules
- Reference tag
- block
Once matched, the core terminates the request. This is suitable for targets that should clearly be blocked.
Default outbound
- How it is determined
- First item in the array
- Example tag
- proxy
- When it applies
- No rule matches
- Troubleshooting focus
- Order of entries
If uncategorized traffic should use the proxy, place proxy first in outbounds.
{
"outbounds": [
{
"tag": "proxy",
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "node.example.net",
"port": 443,
"users": [
{
"id": "11111111-1111-4111-8111-111111111111",
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/connection"
}
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
The domain, user ID, and path above illustrate the structure; they cannot replace the real node parameters from a subscription. VMess settings describes the user and server, while streamSettings describes the transport layer. Even with the correct address and port, a connection fails if the WebSocket path, TLS settings, or user ID does not match.
Conclusion: confirm the default outbound before checking complex rules
If access still fails with routing temporarily disabled, the issue is more likely in the proxy outbound parameters or network connection. If disabling routing fixes it, return to the rule order, match conditions, and outboundTag references.
routing: send requests to the right outbound by condition
routing does not create listeners or store remote-node credentials. It reads request characteristics and returns an outbound tag. The rules array is evaluated from top to bottom, usually stopping at the first valid match. Put narrow, high-priority rules first and broad fallback rules later.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": [
"socks-in"
],
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
What each of the three rules solves
- The first reads GeoSite categories, matches ad domains, and sends them to
block. This depends on the core being able to read the local GeoSite data. - The second matches private IP ranges and sends them to
direct, preventing LAN addresses from being routed through the remote proxy. - The third limits the source to
socks-inand sends the remaining TCP and UDP requests toproxy. Because it has broad coverage, it comes after the precise rules.
| Field | What to check | Common values | Common issue |
|---|---|---|---|
domain |
Destination domain | domain:、full:、geosite: |
Classification data is outdated, so the domain is not in the expected set |
ip |
Destination IP | CIDR、geoip:private |
The domain did not resolve to an IP, so the rule was not evaluated |
port |
Destination port | 53, 80,443, port ranges |
Mistaking the local listening port for the destination port |
network |
Transport type | tcp、udp、tcp,udp |
Writing only tcp and missing UDP requests |
inboundTag |
Traffic source | socks-in |
The reference name does not match the tag in inbounds |
outboundTag |
Destination outbound | proxy、direct、block |
A tag that does not exist in outbounds was referenced |
domainStrategy controls how domain and IP rules are resolved. AsIs matches the original domain first and does not resolve every domain proactively for IP rules; IPIfNonMatch tries domain rules first, then resolves the IP and checks IP rules when nothing matches; IPOnDemand triggers resolution more aggressively during matching. For typical split routing, start with IPIfNonMatch to retain domain classification while allowing rules for private IPs and similar ranges to work.
Conclusion: rule order matters more than rule count
Place ad blocking and LAN direct rules first, followed by the proxy rule covering all TCP and UDP traffic. If a broad rule comes first, more precise direct or block rules may never get a chance to run.
How tags connect the three configuration sections
The key requirements for tags are that they exist, match exactly, and have a clear purpose. Suppose the inbound tag is socks-in and the three outbound tags are proxy, direct, and block. routing can reference only these four defined names. Changing outboundTag to Proxy or proxy-main does not automatically link it to proxy.
inbounds[0].tag = "socks-in"
routing.rules[2].inboundTag = ["socks-in"]
outbounds[0].tag = "proxy"
routing.rules[2].outboundTag = "proxy"
outbounds[1].tag = "direct"
routing.rules[1].outboundTag = "direct"
outbounds[2].tag = "block"
routing.rules[0].outboundTag = "block"
Check the actual request path in order
- Confirm that the application uses SOCKS5, the address is 127.0.0.1, and the port matches inbounds.port. The example port is 10808.
- Check the core log for “address already in use” or configuration-parse errors. If listening failed, do not continue troubleshooting the remote protocol.
- Confirm that the request comes from the expected inbound. If a rule limits
inboundTag, a request entering through another HTTP inbound will not match it. - Follow the order of routing.rules to find the first matching entry, then record its
outboundTag. - Find a tag with exactly the same name in outbounds, then check its protocol, server address, port, and streamSettings.
- If no rule matches, check whether the first outbounds item is actually the intended default outbound.
To apply different policies by source, configure multiple inbounds. For example, 10808’s socks-in can use proxy by default, while another SOCKS inbound on 10818 uses direct. The two entries must have different ports and tags, with separate rules using inboundTag. Copying an inbound while keeping the same port causes a listening conflict during startup.
Safe configuration changes and common questions
v2rayN generates the runtime configuration from the current node, routing settings, and core options. Direct edits to the generated file may last only for the current process; switching nodes, updating the subscription, or restarting the client can regenerate it. For routing logic that must persist, maintain it in the client’s routing settings. To change the local port, use “Settings” → “Parameter settings” and update every application that calls that port.
- Before editing, save a copy of the currently working configuration and change only one group of fields at a time.
- After editing JSON, check commas, quotation marks, and the closing of arrays and objects before starting the core.
- When changing outbounds order, note the first item’s tag because it determines the default outbound when no rule matches.
- Arrange routing.rules from precise to broad, and record the expected outbound for each rule.
- After updating a subscription, verify that the protocol, transport, TLS, server port, and user ID were imported completely.
- If routing categories behave unexpectedly, check whether the current core can read the GeoIP and GeoSite data.
Port 10808 connects, so why do websites still fail to load?
A successful local connection only proves that inbounds is listening. Temporarily set the log level to info and check whether the request reaches proxy, then verify that the VMess server address, port 443, user ID, WebSocket path, and TLS settings match the subscription.
A direct rule is configured, so why does the destination domain still use the proxy?
First check that the rule appears before the broad proxy rule, then verify domainStrategy and the match type. If the rule uses IP classification, set the strategy to IPIfNonMatch and confirm that the resolved domain IP falls within the target range.
Can outboundTag use any name?
Names are customizable, but they must exactly match a tag in outbounds and should not be duplicated. Clear short names such as proxy, direct, and block are recommended. When renaming one, search and update every routing reference.
Why did my manual routing disappear after a subscription update?
The runtime configuration may have been regenerated by v2rayN. Put persistent rules in the client’s routing settings instead of editing only the temporary config.json; after updating, check that rule order and outbound tags remain consistent.
Are VMess and VLESS configurations structurally identical?
The top level still uses inbounds, outbounds, and routing, but the outbound protocol, user fields in settings, and streamSettings combination differ. Do not merely replace the protocol string; use the complete node parameters imported from the subscription.
Understanding config.json is not about memorizing every field. Keep a consistent troubleshooting path: is the inbound listening, what target information does the request carry, which rule matches first, which outbound does outboundTag reference, and can that outbound complete the connection? Verifying each link in order makes the root cause easier to find than changing the port, protocol, and routing all at once.