Deploying Xray Server on a VPS
Table of Contents
Introduction
In an era where internet access is increasingly filtered or restricted, having your own secure tunnel to the open web can be a powerful tool.
This guide will walk you through setting up an Xray server — a modern, flexible proxy framework — to help you regain private, uncensored access to the global internet. Whether you’re trying to overcome national firewalls or simply want full control over your browsing privacy, this solution can serve as a reliable alternative to commercial VPNs.
What Is Xray
Xray is a next-generation proxy platform designed for building secure and flexible internet tunnels. It is based on V2Ray, a widely used open-source project, but includes additional features, better performance, and ongoing development by a more active community. Xray supports many protocols, such like VMess, VLESS, Trojan, etc. You can always find the one you like.
Prerequisites
- A VPS outside mainland China
- Optional: A domain name (with DNS access)
- Basic Linux knowledge
- SSH access to your server
Server Setup
There are several ways to install Xray on your VPS, as listed in the official guide: Install on Linux. I recommend to use official Docker image xray-core to install the Xray on VPS — it makes integration with other services like caddy via Docker Compose much easier. This approach also works well with Ansible (which I will cover in another post).
For now, create a simple Docker Compose file named compose.yml:
services:
xray:
container_name: xray
image: ghcr.io/xtls/xray-core
restart: always
ports:
- "${REALITY_PORT}:${REALITY_PORT}"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./config.json:/etc/xray/config.json
- caddy_data:/dataNext, write a minimal configuration file config.json. You can refer to the official examples at Xray-examples.
Here’s a simplified configuration using VLESS-TCP-XTLS-Vision-REALITY:
{
"log": {
"loglevel": "debug"
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"domain": ["geosite:geolocation-!cn"],
"outboundTag": "direct",
"ruleTag": "Known Site"
},
{
"ip": ["geoip:cn", "geoip:private"],
"outboundTag": "blocked",
"ruleTag": "Private/CN IP"
}
]
},
"inbounds": [
{
"port": ${REALITY_PORT},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "${ CLIENT_ID }", // run `xray uuid` to generate
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"target": "", // A TLS 1.3 + HTTP/2-capable website (e.g. 1.1.1.1:443). This filed was called `dest` in old versions.
"serverNames": [
"" // A server name from the target's certificate; can be left empty if using 1.1.1.1
],
"privateKey": "", // run `xray x25519` to generate. Public and private keys need to be corresponding.
"shortIds": [// Required, list of shortIds available to clients, can be used to distinguish different clients
"", // If this item exists, client shortId can be empty
"${ REALITY_SHORT_ID }" // 0 to f, length is a multiple of 2, maximum length is 16
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls",
"quic"
],
"routeOnly": true
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "blocked"
}
]
}You can see there's some difference with the official one, which block the traffic back to CN, this is a trick to prevent the IP get banned.
You may also need to install the xray binary on your local machine to run the command to generate the public and private keys, you may also use it to generate a uuid for client identification.
Here are some examples for the commends
xray x25519| Private | key: | UM5Gl3GkcDfbaVjAYM1vsaWkw5qns1-pi-PNfojdvEg |
| Public | key: | qWyB3TJzKNpWy9TWFt8COpc5qruIlfiM0sC5zQwFHQs |
xray uuid65cf2486-44dc-4875-9188-da4274f3a9d7
For REALITY_SHORT_ID, you can use this commend to generate:
openssl rand -hex 8b9ce4115b8b311e9
Remember to replace the ${REALITY_PORT} a port number of your choice.
Avoid using common ports like 443 to reduce detection risk.
After editing your files, start the container:
docker compose up -dClient Setup
Popular clients include Shadowrocket and v2rayN.
Most modern clients support the VLESS share link standard, as defined in VMessAEAD / VLESS 分享链接标准提案.
Here’s an example link format for the VLESS-TCP-XTLS-Vision-REALITY:
vless://${ CLIENT_ID }@${ ADDRESS }:${ REALITY_PORT }?type=tcp&security=reality&flow=xtls-rprx-vision&pbk=${ REALITY_PUBLIC_KEY }&sni=${ REALITY_TARGET }&sid=${ REALITY_SHORT_ID }#${ DESCRIPTIVE_TEXT }
ADREESS- Your VPS's public IP.
REALITY_PUBLIC_KEY- Generated via
xray x25519. CLIENT_ID- UUID from
xray uuid. DESCRIPTIVE_TEXT- Any friendly name to identify this server and protocol.
Once you import the link into your client, test the connection by visiting a real website. Avoid using TCP ping or delay tests — these may trigger detection and result in your VPS IP being blocked.