Independent hosting operator since 2014 · New York · Dallas · Amsterdam Status Looking Glass Contact
D4 NetworksD4 Networks
Client area ↗ Deploy a server
Home / Resources / Set up VLESS and REALITY with Xray on a VPS

Tutorials

Set up VLESS and REALITY with Xray on a VPS

Oct 4, 2026 · 9 min read

REALITY is a modified form of TLS in Xray that borrows the handshake of a real website, so your connection looks like ordinary browsing to that site, with no domain or certificate of your own. This guide builds it from the Xray project's own documentation, on a VPS set up as in the first ten minutes on a new VPS.

1. Install Xray

The official install script works on any systemd distro. It puts the config at /usr/local/etc/xray/config.json and starts the service right away with an empty config, which is why step 4 restarts it.

ssh session
$ sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
$ xray version

This guide was tested with Xray v26.3.27. Run the same command later to update.

2. Generate the three secrets

ssh session
$ xray uuid
$ xray x25519
$ openssl rand -hex 8

xray x25519 prints a PrivateKey for the server config and a Password (PublicKey) for the client; you can ignore Hash32. The random hex is your short ID. A short ID needs an even number of characters, up to 16.

3. Pick a site to imitate

REALITY borrows the handshake of a real site, called the target. Pick one that:

  • answers with TLS 1.3 and HTTP/2
  • does not redirect, which often means using the www hostname
  • is not behind a CDN such as Cloudflare, and ideally sits in the same ASN as your VPS
  • is not an Apple or iCloud hostname, which Xray warns tend to get server IPs blocked

Check a candidate:

ssh session
$ xray tls ping www.example.com
$ curl -sSI --http2 https://www.example.com/

You want TLS 1.3 from the first command, and an HTTP/2 status with no Location: header from the second. A via:, x-cache: or cf-ray: header, or server: cloudflare, means a CDN is in front of the site.

4. Write the server config

/usr/local/etc/xray/config.json
{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          { "id": "YOUR-UUID", "flow": "xtls-rprx-vision" }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "raw",
        "security": "reality",
        "realitySettings": {
          "target": "www.example.com:443",
          "serverNames": ["www.example.com"],
          "privateKey": "YOUR-PRIVATE-KEY",
          "shortIds": ["YOUR-SHORT-ID"]
        }
      }
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      { "ip": ["geoip:private"], "outboundTag": "block" }
    ]
  },
  "outbounds": [
    { "protocol": "freedom", "tag": "direct" },
    { "protocol": "blackhole", "tag": "block" }
  ]
}

Put your target's hostname in target (with :443) and in serverNames, without wildcards. Older guides call target by its old name, dest; both work. The routing block stops anyone using your link from reaching services on the server itself or its private network, such as a database bound to 127.0.0.1.

Test the file, then restart the service:

ssh session
$ sudo xray run -test -config /usr/local/etc/xray/config.json
$ sudo systemctl restart xray
$ sudo systemctl status xray

The test should print Configuration OK. Use restart here. The service is already running the empty config from step 1, and only a restart loads yours. Restart again after every config change.

5. Open port 443 in your firewall

Xray does not need a firewall, but if you run one, open 443 in it. If you run neither ufw nor firewalld, skip this step.

Ubuntu and Debian
$ sudo ufw allow 443/tcp
RHEL-based
$ sudo firewall-cmd --get-active-zones
$ zone=public
$ sudo firewall-cmd --permanent --zone="$zone" --add-port=443/tcp
$ sudo firewall-cmd --reload

For firewalld, replace public with the zone the first command shows.

Build the link from your values and import it into a client such as v2rayN on Windows or v2rayNG on Android:

share link
vless://[email protected]:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.example.com&fp=chrome&pbk=YOUR-PUBLIC-KEY&sid=YOUR-SHORT-ID&type=tcp#MyVPS

pbk is the public key from step 2 and sid is the short ID.

Treat the link like a password

It holds everything needed to use your server. Do not post it publicly.

7. When it does not connect

ssh session
$ sudo systemctl status xray
$ sudo ss -lntp | grep ':443'
  • If nothing is listening on 443, restart Xray and read the log with sudo journalctl -u xray -n 50.
  • If the client log says received real certificate, the pbk, sid or SNI in the link does not match the server config.
  • If the target no longer passes the step 3 checks, pick another one.
  • For more detail, set loglevel to info, restart Xray, retry once, and read the server log. Set it back afterwards.

Quick reference

TaskCommand
Install or updatesudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
New UUIDxray uuid
New key pairxray x25519
Client password (public key) from a private keyxray x25519 -i "PRIVATE-KEY"
New short IDopenssl rand -hex 8
Check a target sitexray tls ping HOST and curl -sSI --http2 https://HOST/
Test the configsudo xray run -test -config /usr/local/etc/xray/config.json
Apply a config changesudo systemctl restart xray
Logssudo journalctl -u xray -n 50

Where to go next

REALITY hides what the traffic is. Observers still see that you connect to your server, and no proxy is guaranteed to stay reachable on every network. It also cannot fix distance or poor routing, so before you order, use the Looking Glass to ping each location's test address from your own network. Keep SSH key-only and fail2ban running as usual.

xray vless reality proxy ubuntu debian almalinux

Related articles