Skip to content

VLESS — Xray-core

VLESS is Xray's stateless flagship protocol. There is no symmetric cipher inside the protocol itself; confidentiality and integrity come from the outer TLS / REALITY layer. The handshake carries a UUID for the user and a small "addons" block (notably the flow algorithm).

Inbound

settings for an inbound of "protocol": "vless":

FieldTypeDefaultAllowed valuesDescription
users[]json.RawMessage<user object array> | …Inbound account list; same object shape as `clients`. `users` is the newer name introduced in recent Xray and is accepted alongside `clients`.
clients[]json.RawMessage(required)<user object array>List of accepted users. Each entry carries at minimum `id` (UUID) and optionally `email`, `level`, `flow`, `testseed`, `reverse`. Setting `encryption` on an inbound client is rejected.
decryptionstring(required)none | mlkem768x25519plus.<mode>.<seconds>.<keys>Must be explicitly set. `none` is the classic VLESS no-crypto mode. The mlkem-based string enables the new post-quantum encryption — see Notes below for the format.
fallbacks[]*VLessInboundFallback[]<VLessInboundFallback array>Fallback destinations consulted when the incoming TLS-with-VLESS handshake does not look like VLESS (e.g. a stray HTTPS request). Mutually exclusive with non-`none` `decryption`.
flowstring(empty) | xtls-rprx-visionDefault flow applied to clients that do not set their own. `xtls-rprx-vision` enables XTLS Vision; empty disables flow.
testseed[]uint32[]<uint32 array>Seed values used by the deterministic-test mode of the encryption suite. Almost always omitted.

Source: infra/conf/vless.go:33-40 · pinned at v26.6.1 (94ffd50)

clients[] — user object

The user object is JSON-decoded into the protobuf-backed vless.Account struct. The fields you care about:

FieldTypeDefaultAllowed valuesDescription
idstring (UUID)required<UUID>Client UUID. Canonical 8-4-4-4-12 or dash-less hex.
emailstring""<string>Tag used in stats/log output.
leveluint320<level>Looked up in policy.levels.
flowstringinherits flow"", xtls-rprx-visionPer-user flow override.
testseed[]uint32inheritsPer-user test-seed override.
encryptionstringforbiddenNot allowed on inbound clients.
reverse{tag,sniffing?}(unset)VLessReverseConfigBind this user to a reverse-proxy tag. sniffing is not allowed inside an inbound's reverse block.

fallbacks[]

FieldTypeDefaultAllowed valuesDescription
namestring(unset)<TLS server name>Match incoming TLS SNI; empty matches any.
alpnstring(unset)h2 | http/1.1 | Match the negotiated ALPN; empty matches any.
pathstring(unset)/<path>HTTP path prefix used when proxying. Must start with `/` if non-empty.
typestring(auto)tcp | unix | serveBackend protocol. Inferred from `dest` when omitted: numeric port or host:port → tcp; absolute / @-prefixed path → unix; `serve-ws-none` → serve.
destjson.RawMessage(required)<host:port> | <port> | <unix path>Where to forward the handshake. A bare integer is treated as a port on localhost.
xveruint6400 | 1 | 2PROXY-protocol version to prepend to the forwarded connection. 0 disables.

Source: infra/conf/vless.go:24-31 · pinned at v26.6.1 (94ffd50)

The fallback chain runs in declaration order on the first byte of the incoming connection when it does not look like VLESS. Field ordering matters: matches are tested by (name, alpn, path) tuples, and the first match wins. See infra/conf/vless.go:149-200 for the parsing of dest.

Outbound

settings for an outbound of "protocol": "vless":

FieldTypeDefaultAllowed valuesDescription
address*Address(unset)<host>Simplified shape — server hostname or IP. When set, `vnext` is constructed internally and the top-level `id`, `flow`, etc. are read directly.
portuint16(required with address)<port>Server port (simplified shape).
leveluint320<uint32>User level (simplified shape) — keyed into the policy table.
emailstring(unset)<string>User identifier reported in stats.
idstring(required with address)<UUID>User UUID (simplified shape). Accepts both canonical and dash-less hex.
flowstring(empty) | xtls-rprx-vision | xtls-rprx-vision-udp443Flow algorithm.
seedstring(unset)<string>Encryption seed (reserved). Currently parsed but not applied — kept for forward compat.
encryptionstring(required)none | mlkem768x25519plus.<mode>.<rtt>.<keys>Must be set to `none` or the mlkem encryption string. Anything else is rejected.
reverse*VLessReverseConfig(unset)VLessReverseConfigOptional reverse-proxy tag binding for outbound-side reverse tunnels.
testpreuint320<uint32>Pre-test counter for the deterministic test mode.
testseed[]uint32[]<uint32 array>Seed values for the deterministic test mode.
vnext[]*VLessOutboundVnext(use simplified shape)[{address,port,users:[user]}]Verbose shape — must hold exactly one server with exactly one user. Use one outbound per server and a balancer if you want multiple endpoints.

Source: infra/conf/vless.go:245-258 · pinned at v26.6.1 (94ffd50)

Simplified vs. vnext

The outbound accepts either the simplified top-level shape (address, port, id, flow, ...) or the verbose vnext shape — never both. The simplified form is rewritten into a single-entry vnext array internally (infra/conf/vless.go:251-259). The verbose form must contain exactly one server with exactly one user; for multi-server setups, use multiple VLESS outbounds and a routing balancer.

Examples

Minimal inbound with a single user and XTLS Vision:

json
{
  "inbounds": [
    {
      "tag": "vless-in",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          { "id": "a3482e88-686a-4a58-8126-99c9df64b7bf", "flow": "xtls-rprx-vision" }
        ],
        "decryption": "none"
      },
      "streamSettings": { "network": "tcp", "security": "tls" }
    }
  ]
}

Simplified outbound:

json
{
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "address": "example.com",
        "port": 443,
        "id": "a3482e88-686a-4a58-8126-99c9df64b7bf",
        "flow": "xtls-rprx-vision",
        "encryption": "none"
      },
      "streamSettings": { "network": "tcp", "security": "reality", "realitySettings": { /* ... */ } }
    }
  ]
}

Notes

  • decryption and encryption are required. Omitting them produces the error please add/set "decryption":"none" to every settings (infra/conf/vless.go:140) or the matching outbound error.
  • The mlkem-based encryption string is parsed by the inline closure in Build (infra/conf/vless.go:95-137 inbound, 321-357 outbound). Format: mlkem768x25519plus.<mode>.<seconds-or-rtt>.<keys-and-padding> where mode is native, xorpub, or random; seconds is <from> or <from>-<to> (inbound) or 1rtt/0rtt (outbound); keys are base64-url-safe 32-byte or 1184-byte values, with short strings used as padding.
  • The only flow value accepted in current source is xtls-rprx-vision, with the outbound additionally accepting the -udp443 suffix.
  • fallbacks cannot be combined with a non-none decryption (infra/conf/vless.go:145-147).
  • fallbacks[].xver > 2 is rejected. Only PROXY protocol v0/v1/v2 are accepted.

Cross-core notes

  • sing-box uses users[].uuid instead of Xray's clients[].id, and users[].name instead of email. See VLESS — sing-box.
  • mihomo has a single outbound shape (no inbound/outbound asymmetry in the YAML); user UUIDs are stored on the proxy object itself rather than in a clients array. See VLESS — mihomo.

Source: infra/conf/vless.go:24-258 · v26.6.1 (94ffd50)

Core Tutorial by Argsment