Why use XRay instead of V2Ray
In my previous blog, I have mentioned using V2Ray to bypass government restrictions of social media and other sites.
What's wrong with V2Ray?
V2Ray is great option but the problem stems from it's scalability. It creates an extra process whenever a new user is added to the V2Ray network. This creates huge problems when there are hundreds of users in the system. Connections can be lost and processes can be dropped to accomodate other system processes or just save memory.
Enter Xray: Built for Scale
Xray-core, a powerful fork of V2Ray, was created to address these exact issues. It maintains protocol compatibility with V2Ray but introduces fundamental architectural improvements, including:
Centralized process model: No more spawning new child processes per user. Instead, multiple users can be handled within a single runtime instance efficiently.
Reality protocol support: A newer, TLS-free obfuscation method that’s more stealthy, lighter, and CDN-free.
Improved routing and load handling: Thanks to an optimized networking stack and modern event-loop architecture.
Better TLS (XTLS) implementation: Especially with
xtls-rprx-vision, providing faster handshakes and lower latency.
This makes Xray the obvious choice for operators who:
Manage many individual users, each with their own UUID and shortId.
Want granular control over client access.
Need to minimize overhead and maximize connection reliability.
Installation
Getting Xray set up is simple thanks to the community-maintained installation script. Here's how you can get started with a clean and secure setup using Reality and VLESS.
Step 1: Install Xray-core
Run the official installation script:
bash <(curl -Ls https://raw.githubusercontent.com/XTLS/Xray-install/main/install-release.sh)This:
Installs the latest stable
xray-corebinary.Creates default config and systemd service files.
Sets up paths like
/usr/local/bin/xrayand/etc/xray/.
Step 2: Configure Xray with Reality + VLESS
Edit the main configuration file:
sudo nano /etc/xray/config.jsonPaste in your configuration (updated with real values):
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 8445,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "REPLACE-WITH-UUID",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "www.microsoft.com:443",
"xver": 0,
"serverNames": [
"www.microsoft.com"
],
"privateKey": "REPLACE-WITH-PRIVATE-KEY",
"shortIds": [
"b1"
]
}
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
]
}
REPLACE-WITH-UUID– You can generate one withuuidgen.REPLACE-WITH-PRIVATE-KEY– Generate usingxray x25519.Your server Names should be replace with a domain name that you own that points to the VPS
Step 3: Generate Reality Keys
Use the built-in xray generator:
xray x25519You'll get something like:
Private key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Public key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyUse the private key in your server config.
Share the public key with clients.
Step 4: Enable and Start Xray
sudo systemctl enable xray
sudo systemctl start xrayCheck if it's running:
sudo systemctl status xrayOr view logs:
journalctl -u xray -eStep 5: Open firewall ports (if needed)
Below is an example if you have uncomplicated firewall (ufw) enabled
sudo ufw allow 8445/tcp
sudo ufw reloadStep 6: Connect the Clients
You’ll need:
Server IP or domain
Port (e.g., 8445)
UUID
Flow:
xtls-rprx-visionPublic key
ShortId (e.g.,
b1)Server name (e.g.,
www.microsoft.com) (where ever you have pointed the vps domain to)
My Helper Script: add_connection.sh
add_connection.shOnce your Xray server is up and running, you’ll eventually face the challenge of adding new users efficiently, especially if you’re offering access to friends, clients, or community members.
Typing in a complex vless:// URL manually with:
A UUID
A public key
A shortId
A Reality server name
Is truly painful when you're on mobile. That’s why I wrote a simple helper script: add_connection.sh.
#!/bin/bash
# Check if the required arguments are provided
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <port_number> <number_of_connections>"
exit 1
fi
PORT=$1
NUM_CONNECTIONS=$2
CONFIG_FILE="config.json"
# Check if config.json exists
if [ ! -f "$CONFIG_FILE" ]; then
echo "Error: $CONFIG_FILE not found!"
exit 1
fi
# Generate UUID function
generate_uuid() {
xray uuid
}
# Generate public and private key
generate_keys() {
xray x25519
}
# Get keys
KEYS=$(generate_keys)
PRIVATE_KEY=$(echo "$KEYS" | grep 'Private key' | awk -F ': ' '{print $2}')
PUBLIC_KEY=$(echo "$KEYS" | grep 'Public key' | awk -F ': ' '{print $2}')
# Create connections and update config.json
for ((i=0; i<NUM_CONNECTIONS; i++)); do
UUID=$(generate_uuid)
PORT_INCREMENT=$((PORT + i))
CONNECTION_ENTRY=$(cat <<EOF
{
"port": $PORT_INCREMENT,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "$UUID",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "www.microsoft.com:443",
"xver": 0,
"serverNames": ["ADD YOUR DOMAIN HERE"],
"privateKey": "$PRIVATE_KEY",
"publicKey": "$PUBLIC_KEY",
"shortIds": ["b1"]
}
}
}
EOF
)
# Insert the connection into the config.json
jq ".inbounds += [$CONNECTION_ENTRY]" "$CONFIG_FILE" > tmp.json && mv tmp.json "$CONFIG_FILE"
# Generate QR code for each connection
CONNECTION_STRING="vless://[email protected]:$PORT_INCREMENT?security=reality&encryption=none&flow=xtls-rprx-vision&type=tcp&sni=network.farmernet.org&fp=chrome&pbk=$PUBLIC_KEY&sid=b1#Connection_$PORT_INCREMENT"
echo "$CONNECTION_STRING" | qrencode -o "qrcode_$PORT_INCREMENT.png"
done
echo "Successfully added $NUM_CONNECTIONS connections starting from port $PORT and generated QR codes."Before you can run the script, make sure it’s executable:
chmod +x add_connection.shThen, you can run it with:
./add_connection.sh 8445 3This example starts from port 8445 and creates three unique inbound connections on ports 8445, 8446, and 8447.
Each of these connections will:
Use a random UUID
Share a common Reality keypair (generated automatically)
Be appended to your existing
config.jsonRestart-ready (just reload Xray to apply changes)
For each connection created, the script generates a scannable QR code image using qrencode. These images are saved in your current directory:
qrcode_8445.png
qrcode_8446.png
qrcode_8447.pngYou can then download them and send them to your users as you want.
QR Supported Clients
IOS
ShadowRocket - paid app, highly configurable
Potatso - free
Android
Windows
v2rayN - free, open source
MacOS
Last updated