inbounds: entry points for local apps
Inbounds define the address and port where the core accepts requests, along with the proxy protocol to use. Local apps typically connect through a loopback address to keep the proxy port off your local network. This example listens on separate ports for SOCKS and HTTP, so apps must use the matching protocol. Port numbers aren't fixed standards; use the values configured in your client. Unlike the system proxy setting, an inbound doesn't tell your browser to use a proxy. Both sides must be configured correctly for requests to reach the core.
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": { "auth": "noauth", "udp": true }
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
outbounds: protocols and tags
Outbounds send accepted requests to their destinations. Use freedom for direct connections, blackhole to block traffic, and the appropriate protocol and remote parameters for a proxy outbound. A tag is an internal reference name, not a protocol; the outboundTag in routing must match it exactly. This example shows only direct and blocked traffic, not a remote proxy. In common configurations, the first outbound handles traffic that doesn't match a routing rule, so changing array order can change the default route. Don't look at the rule list alone.
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
routing: match first, then choose an outbound
Routing rules are evaluated from top to bottom, and the matching rule selects an outbound. Put specific domain or address rules before broader ones so an earlier condition doesn't capture the request first. This example sends private IPs to direct, assuming that outbound exists. IPIfNonMatch means the core attempts to resolve an address when no domain rule matches, then checks the IP; it doesn't mean every app uses the built-in DNS resolver. The geoip and geosite data sets must also be available; the rule text doesn't include them.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
dns: resolver settings don't mean full DNS interception
The dns module configures the servers used by the core's built-in resolver. When it handles a lookup also depends on routing policies, outbound settings, and whether the app submits a domain name or an IP address. This example shows the basic structure for static hosts mappings and DNS servers; adding these fields doesn't route all DNS traffic through the core. Encrypted DNS in browsers, the system resolver, and the core resolver may all be active. To troubleshoot DNS, check where the request originates and which outbound sends it, then confirm whether that path is expected.
{
"dns": {
"hosts": {
"domain:example.com": "192.0.2.1"
},
"servers": [
"https://1.1.1.1/dns-query"
],
"queryStrategy": "UseIPv4"
}
}
policy: connection behavior by user level
policy describes connection settings for each user level, such as handshake wait times and idle connection timeouts. Numeric keys under levels represent user levels, not server IDs; the relevant user configuration must use the matching level to apply the policy. Times in this example are in seconds, and the values illustrate the structure rather than recommend settings for every network. Short timeouts can interrupt slow handshakes or long-lived connections. If connections drop, check the logs and network first, then change one setting at a time and compare results.
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5
}
}
}
}
subscription: a client management concept
Subscriptions are managed by clients, not by a top-level key in V2Ray core JSON. A client reads subscription content and converts connection parameters into a runnable configuration; updating a subscription doesn't upgrade the core. Subscription URLs may contain access credentials, so don't expose them in screenshots or public logs. The example below is still a valid core configuration fragment: it shows where outbounds appear in the final configuration, not a subscription format. It contains only a direct outbound, so it can't replace importing a subscription. Generate actual proxy parameters from a valid connection configuration you already have.
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
}
]
}