Set up a secure WireGuard VPN on Rocky Linux VPS

The first thing you realize when you need a reliable VPN for users inside China is that OpenVPN's TLS handshake gets detected and throttled fast. WireGuard, with its single UDP port and minimal handshake, behaves differently on congested international links. This tutorial builds a WireGuard VPN server on Rocky Linux 9, tunes it for the China route, and shows you how to verify it actually works before handing out client configs.
- OS: Rocky Linux 9 (minimal install, up to date)
- Privileges: root or an account with full sudo
- Server: any KVM VPS with a public IPv4. A core VPN like this runs fine on a 1 vCPU / 2 GB plan
- Client: a laptop or phone with the WireGuard app installed
Why WireGuard beats OpenVPN for the China route
WireGuard lives in the kernel, not in userspace. Every packet crosses the stack once, which means lower CPU use and less latency on the same hardware. For China users the bigger advantage is the protocol shape. WireGuard sends a single UDP stream and does not do the TLS handshake dance OpenVPN does, so it is far less likely to be flagged by DPI.
The trade-off is that WireGuard has no built-in obfuscation. If the destination port or the handshake pattern gets blocked, the tunnel simply stops. That is why this guide keeps the port configurable and shows you how to test before you ask anyone to rely on it. A Linux VPS with a clean IPv4 is all you need to start.
WireGuard 使用单个 UDP 端口,握手过程极简,比 OpenVPN 更难被检测。
WireGuard uses a single UDP port with a minimal handshake, which makes it harder to detect than OpenVPN.
Step 1 - Install WireGuard and generate keys on Rocky Linux 9
Rocky Linux 9 ships WireGuard in the base repositories, no ELRepo needed. Update the system first, then install the tools and the kernel module.
sudo dnf update -y
sudo dnf install -y wireguard-tools
sudo modprobe wireguard
lsmod | grep wireguard
The lsmod output should show the module loaded. If it does not, reboot once and check again. On a KVM VPS the module is almost always available in the stock kernel.
Generate the server key pair with wg. The private key never leaves the server. The public key goes in every client config.
cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key
cat server_private.key
cat server_public.key
Write both keys down. You need the public key later, and the private key goes into the server config file. The umask 077 matters here, it makes sure the key files are only readable by root.
Step 2 - Write the WireGuard server configuration
Create the interface config. This example uses the private subnet 10.8.0.0/24 and listens on UDP port 51820.
sudo vi /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = REPLACE_WITH_SERVER_PRIVATE_KEY
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = nft add table ip wireguard
PostUp = nft add chain ip wireguard postrouting { type nat hook postrouting priority 100 \; }
PostUp = nft add rule ip wireguard postrouting ip saddr 10.8.0.0/24 masquerade
PostDown = nft delete table ip wireguard
PostDown = sysctl -w net.ipv4.ip_forward=0
This config enables IP forwarding on start and sets up NAT with nftables, which is the default firewall backend on Rocky Linux 9. The PostUp and PostDown lines run automatically when the interface comes up or down.
Now add a client peer. Each device gets its own key pair and its own IP in the 10.8.0.0/24 range.
[Peer]
PublicKey = REPLACE_WITH_CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32
The AllowedIPs line tells the server which IP the client is allowed to use. Add one [Peer] block per client. When you add a client, it is a reasonable moment to think about whether you want VPS with OpenClaw or other automation later, but for now keep the peer list tight and review it regularly.
Start the interface and enable it at boot.
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg show
wg show prints the interface state, the listening port, and the list of peers. If you see your interface with a public key and no peers yet, the server side is up.
Verify: check that the interface got its address.
ip addr show wg0
ping -c 3 10.8.0.1
Step 3 - Open the firewall for WireGuard
Rocky Linux 9 uses firewalld by default, but the nftables rules in the WireGuard config handle NAT directly. You still need to open the UDP port in firewalld so the server accepts inbound handshakes.
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
The output must show 51820/udp. If you changed the ListenPort in the config, use that port here instead.
Double check that firewalld is running, a fresh Rocky install has it enabled by default.
sudo systemctl status firewalld
If you prefer nftables over firewalld for everything, disable firewalld and add the port rule directly. That is a cleaner setup if you manage the whole server with nftables anyway. For most people firewalld plus the PostUp NAT rules is enough and one less moving part.
Step 4 - Create a client configuration for China users
On the client machine, generate its own key pair. Linux and macOS clients use the same wg tool, Windows uses the WireGuard GUI which generates keys for you.
wg genkey | tee client_private.key | wg pubkey > client_public.key
Copy the client public key to the server and add it as a new peer. Give each peer a unique IP.
sudo wg set wg0 peer CLIENT_PUBLIC_KEY allowed-ips 10.8.0.2/32
sudo wg addconf wg0 /dev/stdin
Now write the client config file. This is the file you import into the WireGuard app.
[Interface]
Address = 10.8.0.2/24
PrivateKey = REPLACE_WITH_CLIENT_PRIVATE_KEY
DNS = 1.1.1.1
[Peer]
PublicKey = REPLACE_WITH_SERVER_PUBLIC_KEY
Endpoint = YOUR_SERVER_IP:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Three settings matter specifically for China users. AllowedIPs = 0.0.0.0/0 routes all traffic through the tunnel. PersistentKeepalive = 25 keeps the NAT binding alive on mobile networks, without it the tunnel can go silent after a few minutes. A public DNS like 1.1.1.1 avoids the local resolver, which is part of the censorship chain.
Import this file into the WireGuard client on the laptop or phone. On Linux, put it in /etc/wireguard/ and run sudo wg-quick up client.
Verify from the client: check that the tunnel hands over and the server sees the peer.
sudo wg show
curl -s ifconfig.me
The curl result must show your VPS public IP, not the client's home IP. That proves the tunnel encrypts and routes traffic.
Step 5 - Harden the server and test the tunnel
A WireGuard server is only as good as the OS under it. This guide assumes you already locked down SSH, but here is the reminder: disable root login, use key auth only, and change the SSH port if you want to cut down brute force noise. The UDP port WireGuard uses is already open, keep everything else closed.
Test the tunnel from both sides before you distribute the config.
# On the server
ping -c 3 10.8.0.2
# On the client behind the tunnel
ping -c 3 10.8.0.1
Both directions must reply. If only one direction works, the problem is the firewall on the server or the AllowedIPs on one side. When traffic reaches the client but the client cannot reach the internet, the NAT rule in PostUp is missing or the server has no route to the internet on its main interface.
For a production setup, consider running dedicated server resources if the VPN will carry a lot of users. A VPS with 2 GB RAM handles dozens of WireGuard peers easily, but CPU matters under heavy transfer, the kernel crypto does the work and it adds up.
WireGuard troubles on the China route
Two failures show up constantly with this exact setup.
The handshake completes but no traffic flows. Check the firewall on the server and confirm net.ipv4.ip_forward is actually 1 while the interface is up.
sysctl net.ipv4.ip_forward
sudo nft list table ip wireguard
If the nat table is empty, the PostUp rules did not run. Restart the interface.
sudo systemctl restart wg-quick@wg0
The tunnel is up but websites time out. That is usually MTU. WireGuard's default MTU of 1420 is fine for most links, but some China ISPs fragment smaller. Set a lower MTU on the client interface, 1280 is the safe floor.
[Interface]
Address = 10.8.0.2/24
MTU = 1280
Test with ping -M do -s 1300 10.8.0.1 from the client to find the largest packet that passes without fragmentation, then set the MTU to that value plus 28 bytes of header.
FAQ
Does WireGuard work reliably for users in China?
It works better than OpenVPN because the protocol is a single UDP stream with no TLS handshake. But no VPN protocol is guaranteed. If the server IP or port gets blocked, change the port or switch to a fresh VPS with a new IP. Test from a real China connection before deploying to many users.
Can I run WireGuard on a cheap VPS plan?
Yes. WireGuard runs entirely in the kernel, so a 1 vCPU / 2 GB RAM plan handles more than a dozen active peers. The bottleneck is bandwidth and CPU during heavy transfers, not RAM. A VPS for n8n and automation or any other workload can host WireGuard in parallel without issue.
Should I use a VPS in Vietnam for China users?
A VPS in Vietnam offers a good balance. The route from China to Vietnam is shorter than to the US or Europe, so latency stays lower. VPS pricing with monthly billing means you can test the route for one month and switch if the latency is not acceptable.
How do I add a new client later?
Generate a key pair on the new device, add the public key to the server with wg set wg0 peer, assign a free IP, and write a client config. Then reload the config with wg addconf. No server restart needed, WireGuard applies peer changes live.
Is WireGuard faster than OpenVPN in practice?
In kernel-to-kernel comparison, WireGuard uses less CPU and adds less latency because it avoids the userspace hop. On a 100 Mbps link you may not see a throughput difference, but on a faster connection or a weak CPU the gap shows. The bigger win for China users is the detection resistance, not speed.
Related articles
- Set up a WireGuard VPN on your VPS in 10 minutes
- Set up a VPS VPN for China users on Ubuntu 24.04
- Set up nftables firewall on a VPS to replace iptables
- Hardening SSH on a new VPS, keys, ports, fail2ban and CrowdSec
Rocky Linux 搭建 WireGuard 服务器要点
本文在 Rocky Linux 9 上搭建 WireGuard VPN 服务器,面向中国用户优化。关键步骤包括:安装 wireguard-tools 并生成密钥对,配置 wg0 接口并启用 nftables NAT 转发,在 firewalld 中开放 UDP 端口,以及为客户端生成配置并设置 PersistentKeepalive 和 MTU。测试时用 ping 和 curl 验证隧道双向连通。遇到连接超时优先检查 MTU 和防火墙规则。选择越南 VPS 可缩短中国用户延迟,月付方案便于试错调整。


