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-core binary.

  • Creates default config and systemd service files.

  • Sets up paths like /usr/local/bin/xray and /etc/xray/.

Step 2: Configure Xray with Reality + VLESS

Edit the main configuration file:

sudo nano /etc/xray/config.json

Paste 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 with uuidgen.

  • REPLACE-WITH-PRIVATE-KEY – Generate using xray 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 x25519

You'll get something like:

Private key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Public key:  yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
  • Use 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 xray

Check if it's running:

sudo systemctl status xray

Or view logs:

journalctl -u xray -e

Step 5: Open firewall ports (if needed)

Below is an example if you have uncomplicated firewall (ufw) enabled

sudo ufw allow 8445/tcp
sudo ufw reload

Step 6: Connect the Clients

You’ll need:

  • Server IP or domain

  • Port (e.g., 8445)

  • UUID

  • Flow: xtls-rprx-vision

  • Public 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

Once 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.sh

Then, you can run it with:

./add_connection.sh 8445 3

This 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.json

  • Restart-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.png

You can then download them and send them to your users as you want.

QR Supported Clients

IOS

Android

Windows

MacOS

Last updated