Setup Xray VPN with VLESS + Reality
In this document, we will set up an Xray-core VPN server using the VLESS protocol with Reality as the transport security layer, then connect to it from a client.
For background on how each piece works, see vless.md and xtls_reality.md.
Requirements
- A VPS with a public IPv4 address.
- 1 vCPU and 512 MB – 2 GB of RAM is enough for personal use (Xray idles around 20–50 MB).
- 50 GB disk is more than enough; the binary itself is < 30 MB.
- A modern Linux distro with a recent kernel (Debian 12, Ubuntu 22.04/24.04, …). A kernel ≥ 4.9 is required for BBR.
- Root or sudo access.
Architecture
sequenceDiagram
participant Client
participant VPS as VPS (Xray on :443)
participant Decoy as Reality decoy<br/>(e.g. www.cloudflare.com)
participant Internet
Client->>VPS: TLS-looking handshake (SNI = decoy host)
alt Authorized client
VPS->>VPS: Validate Reality auth<br/>(UUID + shortId + ECDH)
VPS-->>Client: Reality handshake reply
Client->>VPS: Encrypted VLESS payload
VPS->>Internet: Forward as plain outbound
Internet-->>VPS: Response
VPS-->>Client: Encrypted reply
else Anyone else (probe / censor)
VPS->>Decoy: Transparently proxy ClientHello
Decoy-->>Client: Real TLS response from decoy
Note over Client,Decoy: Looks identical to a normal HTTPS site
end
Server setup
1. Open the listening port
Most VPS providers ship a default firewall that drops everything except SSH. Open the port Xray will listen on (we use tcp/443 because it blends in with normal HTTPS traffic):
1 2 3 4 5 6 7 | |
[!NOTE]
Cloud providers usually have a second firewall layer at the network level (Security Groups / Security Lists / Cloud Firewall). Allowing the port inside the VM is not enough — make sure ingress forTCP/443is also opened in the provider's console.
2. Enable BBR
BBR is a Google-developed TCP congestion control algorithm that drastically improves throughput on lossy or long-distance links. Xray rides on the kernel TCP stack, so this benefits every connection automatically.
1 2 3 4 5 6 7 | |
3. Install Xray-core
Use the official install script. It places the binary in /usr/local/bin/xray, ships a hardened systemd unit, and sets up log directories.
1 2 | |
Files installed:
| Path | Purpose |
|---|---|
/usr/local/bin/xray |
Binary |
/usr/local/etc/xray/config.json |
Server config |
/usr/local/share/xray/{geoip,geosite}.dat |
Routing data files |
/etc/systemd/system/xray.service |
Systemd unit |
/var/log/xray/{access,error}.log |
Logs |
To upgrade later, rerun the same script. To remove: sudo bash /tmp/xray-install.sh remove --purge.
4. Generate Reality credentials
Three values are needed: an X25519 keypair for Reality, a UUID for the VLESS user, and a short ID.
1 2 3 4 5 6 7 8 9 | |
Save the output. The values used below are placeholders:
| Field | Placeholder | Notes |
|---|---|---|
privateKey |
<server_private_key> |
Keep secret, server only |
publicKey / pbk |
<server_public_key> |
Goes into client config |
UUID |
<uuid> |
Per-user identifier |
shortId / sid |
<short_id> |
1–16 hex chars |
5. Choose a Reality decoy host (dest / serverName)
Reality forwards probes from anyone who isn't an authorized client to a real public site, so the connection looks like a perfectly normal HTTPS request to that site. The decoy must:
- Speak TLS 1.3 + HTTP/2.
- Be a popular site you do not own (so the traffic blends in).
- Be geographically close to the VPS so the upstream latency stays low.
Good defaults: www.cloudflare.com, www.apple.com, www.bing.com, www.icloud.com. Verify:
1 2 | |
6. Write the server config
Replace the placeholders, then write to /usr/local/etc/xray/config.json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
A few things to know about the structure:
flow: xtls-rprx-visionenables XTLS Vision flow control on top of Reality — splices large data streams without per-packet TLS overhead, big throughput win.destandserverNames[0]should match — this is the decoy that probes get forwarded to.- The blackhole outbound combined with the routing rules drops BitTorrent and traffic to private/internal ranges (avoids your VPS being abused as a P2P relay or LAN scanner).
Validate before restarting:
1 2 | |
7. Start the service
1 2 3 4 | |
Client setup
Any Xray/V2Ray client supporting VLESS + Reality + Vision will do: v2rayN (Windows), NekoBox / NekoRay, Hiddify, Streisand (iOS), FairVPN, Shadowrocket, V2RayNG (Android), Furious (macOS).
Share link format
1 | |
Manual fields
| Field | Value |
|---|---|
| Address / Host | <server_ip> |
| Port | 443 |
| Protocol | VLESS |
| UUID | <uuid> |
| Encryption | none |
| Flow | xtls-rprx-vision |
| Network / Transport | tcp |
| Security | reality |
| SNI / ServerName | www.cloudflare.com |
| Fingerprint (uTLS) | chrome |
Public Key (pbk) |
<server_public_key> |
Short ID (sid) |
<short_id> |
| SpiderX | (empty) |
[!NOTE]
The client does not need any TLS certificate. That is the whole point of Reality — the certificate the client appears to validate during the fake handshake is the decoy site's real certificate.
Testing
Server-side checks
1 2 3 4 5 6 7 8 9 | |
Reachability from the public internet
From any other machine:
1 2 | |
If this fails, the cloud-level firewall is almost always the cause — the host firewall (iptables/ufw) being open is not enough.
Client-side verification
- Import the share link, activate the connection.
- Confirm the egress IP changed:
1 2
curl https://ifconfig.me # → <server_ip> - DNS leak / WebRTC: https://browserleaks.com/ip, https://www.dnsleaktest.com.
- Speed: https://fast.com or https://www.speedtest.net. Expect roughly your line speed minus 10–20 % overhead.
Common failures
| Symptom | Likely cause | Fix |
|---|---|---|
nc -zv from outside fails |
Cloud firewall blocking 443 | Open ingress in provider console |
| Connects but client times out | Wrong UUID / pbk / sid |
Re-import the share link |
Configuration OK. but service fails to start |
Port 443 already used (web server, etc.) | sudo ss -tlnp \| grep :443 and stop the conflict |
| Throughput poor on lossy network | BBR not active | sysctl net.ipv4.tcp_congestion_control should print bbr |
| Client connects but no internet | Egress blocked / routing rule | journalctl -u xray -f while reproducing |
Day-2 operations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |