V2Ray Configuration JSON Explained: Inbounds, Outbounds, and Routing

Learn the V2Ray config structure with a minimal working example. Explore log, inbounds, outbounds, routing, and DNS, including how fields reference one another.

At a glance

For users familiar with subscriptions who are ready to explore core configuration. Start with a SOCKS inbound that listens only on localhost, then verify the config with direct and blocked outbounds before looking at tag references, rule order, and DNS boundaries. This example has no remote proxy node. After testing, add the server-provided protocol settings to an outbound so JSON syntax, node connectivity, and routing issues stay easy to distinguish.

1. Save a Standalone Test config.json

This example uses the traditional V2Ray JSON config format supported by V2Fly cores. Run v2ray version to identify the program in use, then check v2ray help. V2Fly and Xray share some fields, but their extensions and command-line options are not identical. Don't assume a config for one core is universally compatible.

Save the following as UTF-8-encoded config.json. It exposes a SOCKS endpoint at 127.0.0.1:10808, blocks connections to example.org and its subdomains, and sends all other requests direct by default. “Working” here means the inbound, routing, and outbound paths run—not that a remote proxy is available.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks-local",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": false
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "blocked",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "inboundTag": ["socks-local"],
        "domain": ["domain:example.org"],
        "outboundTag": "blocked"
      }
    ]
  },
  "dns": {
    "servers": [
      "1.1.1.1",
      "8.8.8.8"
    ]
  }
}

2. log: Know Where to Find Errors

log controls logging; it doesn't handle proxying or routing. This example sets only loglevel, with no log file path. When launching from a terminal, watch the process output. When running as a service, check that service's logs instead of looking for a default log file in an arbitrary directory.

Field or valuePurposeWhen to use it
loglevel: warningKeep higher-severity messages, such as warnings and errorsFor everyday use; routine connections may not generate log entries
loglevel: infoShow more runtime detailsFor a quick look at connection and config behavior
loglevel: debugProvide more detailed diagnostic informationReproduce the issue, then restore the previous log level
access / errorSet separate paths for access and error logsIf using file paths, make sure the directory exists and the process can write to it

If startup fails, start with the first clear error. Parse errors usually point to a field or location; address already in use means the listening port is occupied; permission errors call for checking the config file and log directory. Changing the node address won't fix a local port conflict.

3. inbounds: Apps Connect to a Local Endpoint

inbounds defines how the core accepts requests. In this example, protocol: socks is the SOCKS protocol between an app and the local core—not the protocol used by a remote server. Configure your browser or terminal to use this endpoint explicitly. Starting the process alone won't change system proxy settings or capture traffic from every app.

Local SOCKS inbound

Tag
socks-local
Listen address
127.0.0.1
Listen port
10808
Protocol
socks

Accepts local connections only. Make sure the test app uses a SOCKS proxy, not an HTTP proxy.

Default direct outbound

Tag
direct
Protocol
freedom
Protocol settings
{}
Array position
First entry

Used when no rule matches. Connects directly to the destination without a remote proxy server.

listen controls who can access the endpoint; port sets its port number. The loopback address used here prevents other devices on your LAN from connecting via your computer's local network address. Don't switch to 0.0.0.0 to troubleshoot and leave authentication disabled—that would expose the listener more widely.

With settings.auth set to noauth, the local SOCKS proxy requires no username or password. udp: false means this example doesn't enable SOCKS UDP forwarding. This guide tests TCP requests only, so it doesn't establish whether games, voice calls, or other UDP apps will work.

4. outbounds: Separate Protocol Settings from Outbound Tags

outbounds defines how requests leave the core. In this example, freedom connects directly, while blackhole rejects or discards requests. Neither is a remote node, so neither needs a server address, user ID, or transport security settings.

  1. Start with protocol. It determines which protocol fields are valid in settings. Don't copy authentication fields from a SOCKS inbound into a VMess outbound.
  2. Next, check settings. This section holds connection parameters for the selected protocol. When adding a real proxy node, use the server's details and documentation for your core version rather than guessing default values.
  3. Then check tag. This is the name used to reference an outbound in the config. In this example, direct and blocked are custom tags, not reserved protocol names.
  4. Check streamSettings. When needed, configure transport and transport security settings in the outbound object. These are sibling fields of the protocol's settings, not fields to nest arbitrarily.

VMess and VLESS are different proxy protocols; changing only protocol won't convert one into the other. For VLESS, check your core version and its supported features. In particular, don't copy Xray-specific transport extensions into a V2Fly config just because the fields look similar.

Takeaway: Keep a Direct Baseline, Then Add a Real Node

First, verify that the 10808 endpoint works with a direct connection. Then add a proxy outbound and route a rule to it. If the new setup fails, you can focus on the protocol and server settings without questioning the inbound config at the same time.

5. routing: Route Requests to Outbounds in Rule Order

Once a request enters the core, the routing module selects an outbound based on conditions such as the destination domain, destination IP, port, and inbound tag. The flow in this example is shown below. It doesn't use a virtual network adapter or automatically configure the system proxy.

App request Local inbound Rule match Outbound selected Connect to destination

The rule in this example requires both the socks-local inbound tag and a destination matching domain:example.org, then sends the request to blocked. Both conditions in this rule must match. If the domain array lists multiple domains, matching any one of them satisfies the domain condition.

Config itemReference or meaningCommon mistake
inboundTagMatches the inbound object's tagUsing the protocol name socks as the inbound tag
outboundTagReferences the outbound object's tagUsing a tag that doesn't exist or has different capitalization
domain:example.orgMatches the domain and its subdomainsAssuming it matches only the exact hostname
full:example.orgMatches only the exact domainExpecting it to match all subdomains too
rules arrayChecked from top to bottom; the first matching rule selects an outboundPutting broad rules before more specific ones

If no rule matches, the first entry in outbounds is used by default. The first entry here is direct, so ordinary requests connect directly. Moving a proxy outbound to the first position also changes how unmatched requests are handled. If every site seems to use the same route, check the first array entry as well as the rules.

domainStrategy: AsIs means routing matches against the target information already available, without resolving domain names just to match IP rules. If an app sends an IP address to SOCKS, this domain rule alone can't recover the original hostname. This example doesn't configure sniffing, so make sure the app passes the domain name to the SOCKS endpoint when testing.

6. dns: Configuring DNS Servers Doesn't Take Over System DNS

The top-level dns section configures the core's built-in DNS module. This example lists 1.1.1.1 and 8.8.8.8, but adding this section doesn't change the operating system's DNS settings or mean every app's queries will automatically go through the core.

So the DNS section here mainly illustrates the structure; it doesn't guarantee that DNS will be used in every test. Routing's domainStrategy and the direct outbound's settings.domainStrategy operate at different layers: the first affects rule matching, while the second affects how direct connections handle destination addresses. Don't assume changing either field with the same name will alter every resolution path.

Takeaway: First Find Out Who Resolves the Domain

If a domain rule doesn't match, retry with socks5h://127.0.0.1:10808 so curl passes the destination domain to SOCKS. Once you've confirmed how the request is sent, decide whether to adjust DNS or routing.

7. Test the Config with Two Requests

For V2Fly versions with test and run subcommands, run the commands below from the directory containing the config file. On Windows, use .\v2ray.exe when running from the current directory. For other versions, check the local help output for supported options. Before starting, make sure no desktop client is already using 10808.

v2ray version
v2ray help
v2ray test -c config.json
v2ray run -c config.json

A successful test means only that the current core accepts the config; it doesn't prove network connectivity. Keep the core running and execute the commands below in another terminal. On Windows, use curl.exe to avoid conflicts with terminal aliases.

curl --connect-timeout 10 --max-time 20 --proxy socks5h://127.0.0.1:10808 -I https://example.com/
curl --connect-timeout 10 --max-time 20 --proxy socks5h://127.0.0.1:10808 -I https://example.org/
  1. First, test a direct connection. If the destination is reachable on your current network, the first command should return HTTP response headers. The status code depends on the destination site and doesn't have to be 200.
  2. Next, test blocking. The second request should fail to complete a normal HTTPS response. The exact error varies by curl and core version, so don't judge the result by one specific error message.
  3. Keep the timeouts in mind. The command sets a 10-second connection timeout and a 20-second overall timeout to prevent long waits. These are not latency measurements or performance results.
  4. Change one thing at a time. Change the block rule's outboundTag to direct, then test again after restarting the core. If the second request now gets a response, that confirms the tag reference and rule are participating in request handling.

After editing the JSON on disk, don't assume the running process has reloaded it. For the manual setup in this guide, stop the old process, test the config, and start it again. When using subscription nodes in v2rayN, the client may regenerate its runtime config. Distinguish client-generated files from custom configs you maintain yourself, and keep a copy before editing.

8. Common Config Problems and Fixes

The config test passes, so why isn't my browser using the proxy?

A config test doesn't set the system proxy. First run the curl commands in this guide to verify the endpoint, then check the browser's proxy type and port. In this config, 10808 is a SOCKS port; it won't work in a setting that accepts HTTP proxies only.

Why can I still open a site when it's routed to blocked?

First, confirm the request really goes through 127.0.0.1:10808, then use socks5h to pass the domain name. Next, check that inboundTag is set to socks-local and that no earlier rule matches first. Restart the process before testing your changes.

Startup says port 10808 is in use. What should I change?

Stop the old core process, or change the inbound port to an unused 10818 and update the port in the test command to 10818 as well. Don’t change only the application’s port while leaving the core listening on the original one; the ports must match.

Can I put a subscription URL directly in outbounds?

No. A subscription is how a client retrieves node information; outbounds requires structured objects the core can recognize. Import the subscription into v2rayN first, then inspect the config it generates for the selected node. Before sharing a troubleshooting snippet, remove the subscription URL, server credentials, and other sensitive settings.

Follow the same checks every time: validate the JSON, start the core, connect to the inbound, match a rule, and reach the destination through the outbound. Change one layer at a time and keep a working baseline config. That way, even after adding VMess, VLESS, or more complex DNS routing, you can narrow failures down to specific fields instead of repeatedly replacing the whole config.

Choose a Client and Platform

Choose an installer for your operating system. Use v2rayN on desktop; on Android, choose v2rayNG or v2flyNG based on your core requirements. Follow the setup guide to establish a basic connection, then return here to review the config structure and rule references.

Download v2rayNChoose a client for your operating system