Skip to content

Xray Raw JSON configuration contract

Use Connect → expert mode → Raw JSON → import or editor. Include a root name and all actual proxy outbounds needed by the configuration. Normal-mode server selection, Smart Routing and Custom Routing do not merge into Raw.

A Raw file is complete for OneXray, not necessarily a standalone CLI configuration: the App adds its platform inbound.

If you want custom DNS or routing while keeping server selection in the App, use Advanced Routing JSON instead. Compare all import formats before preparing the file; a successful save does not verify the remote server.

Ownership at runtime

Setting Owner / generation rule
Actual proxy outbounds, order and dependencies User; no empty entry slots
Routing, balancers, explicit DNS servers and user FakeDNS pools User; missing VPN DNS is supplemented below, without Smart business rules
inbounds[tag=tunIn] App supplies a missing inbound; for an existing one, only platform-owned settings are merged
Other inbounds User, subject to platform and resource restrictions; do not add another TUN
log, metrics, traffic counters App; omit generated paths/listeners and statistics policy
DNS query strategy App’s IPv6 choice; do not promise a conflicting user value takes effect
Asset/certificate paths and TUN file descriptor App-managed runtime environment
Xray outbound interface on Windows/Linux App’s selected interface; cannot be bypassed through Raw JSON

Saved source and runtime copy are different. Extra user fields are preserved outside the managed areas. A runtime dump can contain local ports, paths and generated settings: do not distribute it unchanged as a portable example.

Existing tunIn is preserved

An existing tunIn keeps its array position, other inbound fields and user sniffing, including disabled or absent sniffing. For a TUN inbound, OneXray owns only these settings keys: name, mtu, gateway, dns, autoSystemRoutingTable, autoOutboundsInterface. Platform values overwrite them; Apple/Android remove the latter four when inapplicable. Other user settings remain intact. A non-SOCKS-adapter runtime expects protocol: "tun".

Windows MSIX and the iOS simulator explicitly adapt this inbound to SOCKS, replacing protocol/listener/SOCKS settings as required while preserving unrelated fields. This is a platform adaptation, not permission to replace every inbound. Only when the entire tunIn is missing does the App generate one with default sniffing. Raw has no Advanced Custom template field whitelist.

Missing VPN settings

Raw and complete provider configurations use the same portable VPN adaptation on a deep copy. It supplements a missing tunIn and DNS interception without adding Smart/Custom business rules or changing the first outbound, chains, balancers or existing rule order.

An explicit single port: 53 rule with no ingress restriction, an empty inboundTag list or tunIn is respected. This declaration is not a guarantee of complete DNS coverage; the App does not infer equivalence from ranges, other conditions or priority. Otherwise it reuses the first DNS outbound (adding a missing tag) or appends app-vpn-dns, then prepends a tunIn + TCP/UDP 53 rule. A new DNS outbound handles A/AAAA through Core DNS and forwards other query types directly through app-vpn-dns-direct. Existing DNS-outbound settings are not rewritten.

If dns or dns.servers is missing/null, or the server list is empty, runtime compilation supplies the tunnel IPv4 DNS and, when enabled, IPv6 DNS. It preserves other DNS properties and sends only these generated resolvers directly with app-vpn-default-dns. An existing nonempty list—including localhost, DoH or FakeDNS—remains user-owned. Invalid field types still fail Core validation instead of becoming defaults. System DNS, Core DNS and an application’s own DoH/DoT remain distinct.

Save validation, copies and sharing apply the portable adaptation, but never persist device DNS, runtime ports, file paths or a TUN FD. app-vpn-default-dns and app-vpn-dns-direct are reserved for this supplementation; conflicting definitions are rejected when the fallback is required. Raw adds only referenced helper outbounds, not an unconditional direct/block pair. Imported env values xray.tun.fd and XRAY_TUN_FD are removed from validation/runtime copies; Apple/Android inject the current real descriptor at native startup. Other unknown environment fields remain.

Complete VLESS and DNS example

Template only: replace server host, UUID, TLS parameters and port. This example needs no Geodata. It defines the DNS interception and forwarding itself; changing only a root DNS server is not a complete routing design.

JSON: raw-vless-tls.json

{
  "name": "VLESS with explicit DNS",
  "dns": {
    "tag": "user-dns-query",
    "servers": [
      "8.8.8.8"
    ]
  },
  "outbounds": [
    {
      "tag": "user-proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "server.example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000001",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "raw",
        "security": "tls",
        "tlsSettings": {
          "serverName": "server.example.com"
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    },
    {
      "tag": "user-dns",
      "protocol": "dns",
      "settings": {
        "rules": [
          {
            "action": "hijack",
            "qType": "1,28"
          },
          {
            "action": "direct"
          }
        ]
      },
      "streamSettings": {
        "sockopt": {
          "dialerProxy": "user-proxy"
        }
      }
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "ruleTag": "Resolve tunnel DNS",
        "inboundTag": [
          "tunIn"
        ],
        "port": "53",
        "outboundTag": "user-dns"
      },
      {
        "ruleTag": "DNS through VPN",
        "inboundTag": [
          "user-dns-query"
        ],
        "outboundTag": "user-proxy"
      },
      {
        "ruleTag": "Local network",
        "ip": [
          "192.168.0.0/16",
          "10.0.0.0/8",
          "172.16.0.0/12"
        ],
        "outboundTag": "direct"
      }
    ]
  }
}

The first outbound is the default proxy path. Tunnel DNS traffic on port 53 is sent to user-dns; A/AAAA is handled by Xray DNS; DNS-originated traffic uses the proxy. Non-A/AAAA forwarding uses the DNS outbound’s dialerProxy. Private destination IPs use direct. App-managed tunIn is referenced by routing but intentionally not defined in the input.

Do not add port-53 interception without a DNS outbound, or forward the DNS resolver back into its own interception rule. Browser/application DoH on 443 is not automatically equivalent to ordinary port-53 DNS.

Multiple paths and a final exit

Each final-exit copy must dial through its corresponding entry. The direction is: device → entry → final exit → destination. The exit outbound’s streamSettings.sockopt.dialerProxy points to the entry tag, not the reverse.

Template only: supply two actual entries and one final-exit server. The two exit copies share the final server’s credentials but have different tags and dependencies.

JSON: raw-chain.json

{
  "name": "Two paths to one final exit",
  "dns": {
    "tag": "user-dns-query",
    "servers": [
      "8.8.8.8"
    ]
  },
  "outbounds": [
    {
      "tag": "exit-a",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "exit.example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000003",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "raw",
        "security": "tls",
        "tlsSettings": {
          "serverName": "exit.example.com"
        },
        "sockopt": {
          "dialerProxy": "entry-a"
        }
      }
    },
    {
      "tag": "exit-b",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "exit.example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000003",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "raw",
        "security": "tls",
        "tlsSettings": {
          "serverName": "exit.example.com"
        },
        "sockopt": {
          "dialerProxy": "entry-b"
        }
      }
    },
    {
      "tag": "entry-a",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "entry-a.example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000001",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "raw",
        "security": "tls",
        "tlsSettings": {
          "serverName": "entry-a.example.com"
        }
      }
    },
    {
      "tag": "entry-b",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "entry-b.example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000002",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "raw",
        "security": "tls",
        "tlsSettings": {
          "serverName": "entry-b.example.com"
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    },
    {
      "tag": "user-dns",
      "protocol": "dns",
      "settings": {
        "rules": [
          {
            "action": "hijack",
            "qType": "1,28"
          },
          {
            "action": "direct"
          }
        ]
      },
      "streamSettings": {
        "sockopt": {
          "dialerProxy": "exit-a"
        }
      }
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "ruleTag": "Resolve tunnel DNS",
        "inboundTag": [
          "tunIn"
        ],
        "port": "53",
        "outboundTag": "user-dns"
      },
      {
        "ruleTag": "DNS through VPN",
        "inboundTag": [
          "user-dns-query"
        ],
        "balancerTag": "user-pool"
      },
      {
        "ruleTag": "Local network",
        "ip": [
          "192.168.0.0/16",
          "10.0.0.0/8",
          "172.16.0.0/12"
        ],
        "outboundTag": "direct"
      },
      {
        "ruleTag": "Explicit balanced destinations",
        "domain": [
          "domain:example.net"
        ],
        "balancerTag": "user-pool"
      }
    ],
    "balancers": [
      {
        "tag": "user-pool",
        "selector": [
          "exit-a",
          "exit-b"
        ],
        "strategy": {
          "type": "roundRobin"
        },
        "fallbackTag": "direct"
      }
    ]
  },
  "observatory": {
    "subjectSelector": []
  }
}

The balancer selects full exit tags. Here explicitly matched example.net and DNS queries use balancing; unmatched traffic uses the first complete chain. Non-A/AAAA DNS forwarding uses the first exit. fallbackTag: direct permits direct fallback and is not a fail-closed design. This is a demonstration of composition, not a promise that every unmatched connection is balanced.

Validation and dependencies

Saving checks a separate projection through libXray instance construction and close, without starting the instance. It excludes/reduces App-managed resources and retains user protocol/routing/DNS dependencies. Do not interpret this as permission, port-binding or connectivity validation.

For custom DAT dependencies, install the files first through Routing data or use the App’s Raw sharing flow with separate Geodata App Links. Root geodata.assets is a Custom import contract, not a downloader for plain Raw files. See dependencies. Do not embed local filesystem paths or rely on automatic downloads at VPN startup. Account explicitly for other required files/modules.

For advanced Core fields not covered here, consult the version-matched Xray reference. It does not override the App ownership table. A newly generated tunIn gets FakeDNS recovery when Raw declares a FakeDNS server or pool. An existing inbound keeps the user’s sniffing unchanged: configure recovery yourself when required. See FakeDNS behavior and limitations.

LAN proxy sharing

The device-level LAN sharing switch does not modify complete Raw JSON. Configure listeners and chains directly in Raw.

Core-scheduled root geodata downloads are removed from validation/runtime copies: OneXray manages routing-data downloads and indexes. Custom exchange geodata.assets remains dependency metadata, not a Core cron/outbound scheduler. The Core scheduler’s file/count/interval limits do not become new App downloader limits.

Last updated on