Set up a VPS VPN for China Users on Ubuntu 24.04

The first time I tried connecting to a standard VPN from a network behind the Great Firewall, the handshake just timed out. That's the reality in 2026: plain OpenVPN and IPsec get blocked fast. The reliable approach is running your own tunnel on a Linux VPS with a dedicated IPv4 not tied to any public VPN provider. This guide walks through setting up both WireGuard (for speed and low overhead) and xray-core (as a fallback with traffic obfuscation) on an Ubuntu 24.04 VPS to serve a user in China.
Prerequisites
- An Ubuntu 24.04 VPS with full root access. A rent a VPS in Vietnam provider like thueVPS gives you a clean IPv4 located in Vietnam, which keeps latency reasonable and avoids shared IP blacklists.
- A non-root sudo user configured.
- A domain name pointing to the VPS IP (for xray-core TLS setup).
- UDP port 51820 (WireGuard) and TCP port 443 (xray-core) allowed in your hosting firewall.
Why a VPS VPN Beats Consumer VPNs for China Users
Consumer VPN services use IP ranges that are known and blocked by the GFW. When every user on the same server gets flagged, the whole node drops. A VPS you control gives you a unique, clean IP that has never been associated with a VPN service. You choose the protocol and port. You can rotate the handshake port or switch to obfuscated traffic the moment a block hits. The trade-off is you manage everything: installation, updates, and firewall rules. For a single user, the overhead is small, about 30 minutes of setup per year.
Vietnam is a strong choice for the server location. The Vietnam datacenter's international routes have decent peering into China, and traffic that originates from a Vietnamese ISP is less aggressively throttled than traffic from US or EU IPs.
Step 1, Install and Configure WireGuard
WireGuard is the simplest, fastest tunnel protocol. It uses UDP on a single port and authenticates with public/private key pairs. Start by installing it:
sudo apt update
sudo apt install wireguard -y
Generate server keys:
wg genkey | sudo tee /etc/wireguard/server.key
sudo chmod 600 /etc/wireguard/server.key
sudo cat /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub
Create the config file /etc/wireguard/wg0.conf:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server-private-key>
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Replace <server-private-key> with the key from /etc/wireguard/server.key. Replace eth0 with your server's public interface, check with ip route.
Enable IP forwarding:
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Start WireGuard:
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
Verify:
sudo wg show
# Expected: interface: wg0, listening port 51820, no peers yet
Step 2, Generate the Client Config
On the client machine (or any machine where you generate keys), create a client key pair:
wg genkey | tee /etc/wireguard/client.key
cat /etc/wireguard/client.key | wg pubkey | tee /etc/wireguard/client.pub
Add a peer block to the server's /etc/wireguard/wg0.conf:
[Peer]
PublicKey = <client-public-key>
AllowedIPs = 10.0.0.2/32
Apply the change without restarting:
sudo wg set wg0 peer <client-public-key> allowed-ips 10.0.0.2/32
Create the client config (/path/to/client.conf):
[Interface]
PrivateKey = <client-private-key>
Address = 10.0.0.2/24
DNS = 1.1.1.1, 8.8.8.8
[Peer]
PublicKey = <server-public-key>
Endpoint = <vps-public-ip>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Send this client.conf to the user in China. They import it into their WireGuard client.
Verify connectivity once they connect:
sudo wg show
# The peer should show "latest handshake" a few seconds ago and "transfer" with some bytes
Step 3, When WireGuard Is Blocked: xray-core with TLS
Some networks in China now employ deep packet inspection (DPI) that detects WireGuard by its handshake patterns. When that happens, switch to xray-core, a proxy that can disguise traffic as regular HTTPS on port 443.
Install xray-core using the official script:
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
Generate a TLS certificate using Certbot:
sudo apt install certbot -y
sudo certbot certonly --standalone -d your-domain.com
Create /usr/local/etc/xray/config.json:
{
"log": { "loglevel": "warning" },
"inbounds": [
{
"port": 443,
"protocol": "vless",
"settings": { "clients": [{"id": "your-uuid","flow": "xtls-rprx-vision"}], "decryption": "none" },
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": { "certificates": [{"certificateFile": "/etc/letsencrypt/live/your-domain.com/fullchain.pem","keyFile": "/etc/letsencrypt/live/your-domain.com/privkey.pem"}]}
}
}
],
"outbounds": [{"protocol": "freedom","tag": "direct"}]
}
Generate a UUID:
cat /proc/sys/kernel/random/uuid
Start xray:
sudo systemctl enable xray
sudo systemctl start xray
Verify:
sudo systemctl status xray
# Expected: active (running)
curl -o /dev/null -s -w "%{http_code}\n" https://your-domain.com
# Expected: 000 (because xray responds with no HTTP, but the TLS handshake succeeds)
The client in China configures the xray client (Qv2ray or a mobile app) with the same UUID and domain. The traffic looks like HTTPS to any DPI.
Step 4, Firewall and Port Tuning
Use ufw to restrict access to only the ports you need, and maybe limit WireGuard to a known source IP if the user has a static address:
sudo ufw allow 51820/udp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp
sudo ufw enable
For WireGuard, consider setting a PersistentKeepalive of 25 seconds in the client config, this keeps the NAT mapping alive over mobile networks that drop UDP states quickly.
Troubleshooting Common Failures
WireGuard handshake succeeds but no traffic passes
Symptom: sudo wg show shows an established handshake, but ping 1.1.1.1 from the client fails. Fix: Verify IP forwarding is enabled (sysctl net.ipv4.ip_forward must return 1). Check that iptables MASQUERADE rule is in place (sudo iptables -t nat -L POSTROUTING -v). Also confirm the client's AllowedIPs is set to 0.0.0.0/0.
xray-core fails to start with "listen tcp :443: bind: address already in use"
Symptom: Port 443 is occupied by Nginx or Apache. Fix: Either stop the existing web server (sudo systemctl stop nginx) or change the xray inbound port to 8443 and use port forwarding.
VPN works for a few minutes then drops entirely
Symptom: Connects, works 2-3 minutes, then no data moves until a reconnect. Fix: The GFW may interfere with the UDP flow. Lower PersistentKeepalive to 15 seconds. If it keeps happening, fall back to xray-core (TCP + TLS is harder to interfere with mid-stream).
FAQ
What is the best VPN protocol for China in 2026?
WireGuard for its speed and simplicity, with xray-core (VLESS + TLS) as a fallback when DPI blocks UDP. Avoid PPTP and L2TP/IPsec, both are trivially detected.
Do I need a VPS with a dedicated IPv4?
Yes. A dedicated IPv4 that hasn't been used for VPN traffic reduces the chance of getting blocked. Shared IPv4 ranges from consumer VPNs are often on block lists already.
Is a Vietnam VPS good for a China user?
Vietnam provides low-latency routes into China (around 40-80ms from southern provinces) and the IP geolocation is less suspicious than a US or EU IP for traffic flowing into China.
How much RAM and CPU does a single-user VPN need?
A 2GB RAM VPS is more than enough for one user running both WireGuard and xray-core concurrently. CPU usage stays under 5% on a modern vCPU.
Can I use a free VPN instead?
Running your own VPN on a low-cost VPS with monthly billing is reliable, avoid any service that does not charge money. The IP is clean, you control the logs, and no third party sees the traffic.
Related articles
- Set up a WireGuard VPN on your VPS in 10 minutes
- Hardening SSH on a new VPS: keys, ports, fail2ban and CrowdSec
- Security audit and hardening with Lynis on a VPS


