Xray on a Linux VPS (VLESS + REALITY)
Minimal setup guide for running Xray on a VPS and connecting from a client app.
What this guide includes
- Server-side
config.jsonpattern for VLESS + REALITY - IPv6 setup on a cloud VPS
- User management (add/remove users)
- Monitoring and troubleshooting commands
Prerequisites
- A Linux VPS from a cloud provider
- Root or sudo access
- Xray installed
- A client app that supports VLESS + REALITY
1) Provision a cloud VPS
- Choose a cloud provider and region close to your users.
- Create a virtual machine with Ubuntu LTS.
- For up to four users, a practical starting point is 1 shared vCPU and 1 GB RAM; 2 GB RAM gives more headroom.
- Add SSH key authentication and disable password login.
- Note the instance's public IPv4 address and, if available, its IPv6 address and gateway.
Initial server hardening:
sudo apt update && sudo apt upgrade -y
2) Enable IPv6 on your VPS
Some users' internet service providers (ISPs) may block IPv4 while allowing IPv6. Enable IPv6 in your cloud provider's control panel and verify the assigned IPv6 address and gateway.
ip -6 addr show
ip -6 route
Only use the Netplan example if your cloud provider does not configure IPv6 automatically. Do not apply it unchanged; use the exact values from the provider's network settings or the VPS:
YOUR_INTERFACE_NAME: interface name fromip -br link(for example,ens3).YOUR_MAC_ADDRESS: MAC address fromcat /sys/class/net/YOUR_INTERFACE_NAME/addressafter replacingYOUR_INTERFACE_NAMEwith the actual interface name.YOUR_IPV4/CIDRandYOUR_IPV6/CIDR: assigned addresses and prefix lengths from the provider panel orip addr show. A public IPv4 address may not appear inip addrif the provider uses NAT.YOUR_IPV4_GATEWAYandYOUR_IPV6_GATEWAY: default gateways fromip -4 route show defaultandip -6 route show default, or from the provider panel.YOUR_DNS_IPV4_1andYOUR_DNS_IPV4_2: DNS server addresses from the provider panel orresolvectl status.
The prefix length (the number after /) is provider-specific; use the assigned value rather than assuming /24 or /64.
Example Netplan config:
network:
version: 2
ethernets:
YOUR_INTERFACE_NAME:
match:
macaddress: "YOUR_MAC_ADDRESS"
addresses:
- "YOUR_IPV4/CIDR"
- "YOUR_IPV6/CIDR"
nameservers:
addresses:
- "YOUR_DNS_IPV4_1"
- "YOUR_DNS_IPV4_2"
search: []
routes:
- to: "0.0.0.0/0"
via: "YOUR_IPV4_GATEWAY"
- to: "::/0"
via: "YOUR_IPV6_GATEWAY"
Apply the config:
sudo netplan apply
3) Generate Xray connection values
Two separate pieces are involved: config.json is Xray's server-side configuration file, while a VLESS URL contains the connection details imported into a client app. The VLESS URL uses the REALITY public key, never the private key.
- Generate one UUID per user. Add it to that user's
clientsentry inconfig.jsonand their VLESS URL; it identifies the user. On Linux, runcat /proc/sys/kernel/random/uuid; on macOS, runuuidgen. - Generate a REALITY key pair. Put the private key in the server config's
privateKeyfield and the matching public key in the VLESS URL aspbk. - Generate a short ID. Add it to the server config's
shortIdslist and use the same value assidin the VLESS URL.
The xray x25519 command prints the matching private and public keys. Some Xray versions label the client public-key value Password; use that value as pbk, not the private key. openssl rand -hex 8 generates one 16-character short ID each time it runs.
# Generate a UUID per user
cat /proc/sys/kernel/random/uuid
# Generate REALITY key pair
/usr/local/bin/xray x25519
# Generate a short ID
openssl rand -hex 8
4) Configure and start Xray
The command below writes the entire example to Xray's server config file, /usr/local/etc/xray/config.json, replacing that file if it already exists. Back up an existing config before replacing it; if you already have a working config, edit that file instead.
realitySettings is not a separate file. It is a section inside config.json that configures REALITY for this inbound connection. In this example:
destis the destination hostname and port used by REALITY; here it isYOUR_SNI_HOST:443.serverNameslists the hostname accepted in the TLS SNI field. The VLESS URL'ssnivalue must match it exactly.privateKeyis the server's secret REALITY key. Keep it in this file; use the matching public key in the VLESS URL aspbk.shortIdslists the short IDs the server accepts. The VLESS URL'ssidmust match one of them.- Leave
showasfalseandxveras0for this example.
The 0.0.0.0 listen value means “listen on all IPv4 network interfaces”; it is not your VPS's public IP. The 0.0.0.0/0 and ::/0 values in the Netplan example mean “the default route.” Leave these special values unchanged.
This sample listens on IPv4 only. To also accept IPv6 connections, change the inbound listen value from 0.0.0.0 to ::.
Replace the YOUR_... placeholders with your values, but keep the JSON quotes, commas, and braces in place.
sudo tee /usr/local/etc/xray/config.json << 'EOF'
{
"log": { "loglevel": "warning" },
"inbounds": [
{
"listen": "0.0.0.0",
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{ "id": "YOUR_UUID_USER1", "flow": "xtls-rprx-vision" },
{ "id": "YOUR_UUID_USER2", "flow": "xtls-rprx-vision" }
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "YOUR_SNI_HOST:443",
"xver": 0,
"serverNames": ["YOUR_SNI_HOST"],
"privateKey": "YOUR_PRIVATE_KEY",
"shortIds": ["YOUR_SHORT_ID"]
}
}
}
],
"outbounds": [{ "protocol": "freedom" }]
}
EOF
Validate and restart:
sudo /usr/local/bin/xray -test -config /usr/local/etc/xray/config.json
sudo systemctl restart xray
sudo systemctl status xray --no-pager
Before using an IPv6 address in a VLESS URL, confirm that Xray is listening for IPv6 connections. Run sudo ss -lntp 'sport = :443'. Look for LISTEN and [::]:443 to confirm an IPv6 listener; 0.0.0.0:443 indicates an IPv4-only listener. This checks the VPS itself, not the cloud firewall. The cloud firewall must separately allow inbound TCP 443 over IPv6.
5) Firewall setup
Allow inbound TCP 443 for Xray in the cloud provider firewall and server firewall, if both are enabled.
sudo ufw allow 443/tcp
sudo ufw reload
6) Client VLESS URL format
vless://YOUR_UUID@YOUR_SERVER_ADDRESS:443?encryption=none&security=reality&sni=YOUR_SNI_HOST&fp=chrome&pbk=YOUR_PUBLIC_KEY&sid=YOUR_SHORT_ID&flow=xtls-rprx-vision#YOUR_LABEL
Replace the placeholders as follows:
YOUR_UUID: the user's UUID; it must match that user'sidinconfig.json.YOUR_SERVER_ADDRESS: the VPS public IP or a hostname. For IPv6, put the address in square brackets and omit its CIDR suffix:@[YOUR_IPV6]:443. Do not use brackets for IPv4 (@YOUR_IPV4:443) or a hostname (@YOUR_HOSTNAME:443). Replace each placeholder with your actual server address.YOUR_SNI_HOST: the exact hostname inrealitySettings.serverNamesand the hostname part ofdest. Use the full hostname, such aswww.example.com, withouthttps://or a path.YOUR_PUBLIC_KEY: the public key corresponding to the server'sprivateKey(sometimes labeledPasswordbyxray x25519).YOUR_SHORT_ID: one value fromrealitySettings.shortIds.YOUR_LABEL: an optional, non-sensitive label for the client entry; it is not an authentication field and does not go inconfig.json.
Each user's VLESS URL contains their UUID, which acts as a credential. Share each URL privately with its intended user. The server's REALITY private key belongs only in config.json, never in a VLESS URL.
7) Adding new users
Generate a new UUID and add it to the clients list in config.json. Test the config and restart Xray, then create a VLESS URL with that UUID and share it privately with the user.
8) Monitoring and troubleshooting
Use these commands one at a time to check whether Xray is running and inspect its logs if a client cannot connect:
- Check whether the Xray service is running:
sudo systemctl status xray --no-pager
- Show the most recent 100 log entries:
sudo journalctl -u xray -n 100 --no-pager -l
- Follow new log entries live while troubleshooting. Stop with
Ctrl+C:
sudo journalctl -u xray -f
Notes
- Do not commit real IP addresses, UUIDs, private keys, public keys, short IDs, or VLESS URLs.
- Rotate credentials if they were ever shared publicly.
- Use a separate UUID per user.
- The UUID in each VLESS URL is that user's credential. Share the URL privately; anyone who obtains it can use that UUID to connect.
- Depending on the cloud provider, you may be able to change the VPS's public IP. If you do, update the server address in each user's VLESS URL.
- User IPs in logs are sensitive.