Security

Set Up a Self-Hosted VPN on Rocky Linux VPS in Vietnam

The first problem you hit when running a server for users inside China is that the route to most foreign services is congested, or blocked outright. A self-hosted VPN on a Rocky Linux VPS in Vietnam gives your Chinese users a stable exit point that sits geographically close, often with noticeably better latency than a US or European endpoint. This post walks through a full WireGuard deployment on Rocky Linux 9, from kernel module check to client config, with every command you need to copy and run.

越南 VPS 提供靠近中国的网络出口,适合需要稳定访问境外服务的用户。

A Vietnam VPS gives you a network exit close to China, which suits users who need stable access to overseas services.

Environment: Rocky Linux 9.x, root or a sudo user, a Linux VPS with a public IPv4. WireGuard is in the default EPEL repository, so no third-party repo is needed. I assume you already have SSH access and have done the basic hardening covered elsewhere.

Prerequisites

  • A Rocky Linux 9 VPS with a public IPv4 address, reachable over SSH as root or via sudo.
  • EPEL repository enabled (dnf install epel-release).
  • UDP port 51820 open on any external firewall, plus whatever you use for SSH.
  • A Windows, macOS, or Linux client machine for testing. For China users on Windows, the official WireGuard client works fine.

If you are renting the server, pick one with a location that has good routes to China. A Vietnam VPS with a dedicated IPv4 is a common choice because domestic bandwidth within Vietnam is cheap and the international path to China is short compared to trans-Pacific routes. The exact latency depends on the carrier at both ends, so measure before you commit.

Why WireGuard Instead of OpenVPN in 2026

WireGuard shipped in the Linux kernel since 5.6, and Rocky Linux 9 ships kernel 5.14, so the module is already there. You are not installing a userspace daemon and a TUN driver; you are loading a kernel module. That means fewer moving parts, less attack surface, and lower CPU use, which matters on a small VPS.

OpenVPN is still useful when you need complex policy routing or username/password auth. For a static group of users who just need a reliable tunnel, WireGuard is simpler to reason about. Each peer has a single public key, config files are a few lines, and the handshake is a single UDP packet exchange.

One practical gotcha: some hotel and office networks in China block or throttle UDP on common ports. WireGuard on the default 51820 is widely filtered. I run mine on a non-standard high port like 44330 or 51820+offset, which evades shallow DPI that only looks at well-known ports. It is not a guarantee, but it raises the bar.

Step 1 - Installing WireGuard on Rocky Linux 9

Enable EPEL and install the tools. The kernel module is already present, so you only need the userspace utilities:

sudo dnf install -y epel-release
sudo dnf install -y wireguard-tools

Verify the install and confirm the kernel module loads:

sudo modprobe wireguard
lsmod | grep wireguard

You should see wireguard listed with a use count. If the module fails to load, update the kernel first (sudo dnf update -y then reboot). Rocky 9 kernels include WireGuard, so a failure usually means an outdated kernel, not a missing feature.

Step 2 - Generating Server and Client Keys

WireGuard uses Curve25519 keys. Generate a server keypair and a separate keypair for each client. Never reuse keys across peers.

cd /etc/wireguard
sudo umask 077
wg genkey | sudo tee server_private.key | wg pubkey | sudo tee server_public.key
wg genkey | sudo tee client1_private.key | wg pubkey | sudo tee client1_public.key

The umask 077 is important. It makes sure the private key files are only readable by root. WireGuard refuses to start if the private key file has loose permissions. Print the public keys so you can paste them into config files:

cat /etc/wireguard/server_public.key
cat /etc/wireguard/client1_public.key

Keep the private keys on their respective machines. The server needs its own private key and the client public keys. Each client needs its own private key and the server public key.

Step 3 - Writing the Server Configuration

Create /etc/wireguard/wg0.conf. This is the whole server config, no more:

[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

[Peer]
# Client 1
PublicKey = <client1_public_key>
AllowedIPs = 10.0.0.2/32

Replace the interface name eth0 if yours is different. Check with ip route show default and use the interface that holds the default route. On many KVM VPS images this is eth0, but some providers use ens3 or enp1s0.

The PostUp line enables IP forwarding for the tunnel and sets up NAT so client traffic exits through the server's public IP. Without the MASQUERADE rule, clients can reach the server but no further.

Enable IP forwarding at the kernel level. Edit /etc/sysctl.conf or drop a file in /etc/sysctl.d/:

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

Verify forwarding is on:

sysctl net.ipv4.ip_forward

Expected output: net.ipv4.ip_forward = 1.

Step 4 - Configuring firewalld for WireGuard

Rocky Linux ships with firewalld enabled. You need to allow the WireGuard UDP port and masquerading:

sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload

Verify the rules took effect:

sudo firewall-cmd --list-all

Look for ports: 51820/udp and masquerade: yes in the output. If you changed the ListenPort in the server config, adjust the port number here to match.

One thing people miss: firewalld's masquerade only applies to the zone it is enabled in. If your public interface is in a different zone (some providers configure public vs external), run the masquerade command with --zone=public explicitly. The default zone is usually fine, but confirm with firewall-cmd --get-default-zone.

Step 5 - Starting WireGuard and Enabling Autostart

Start the interface and set it to come up on boot:

sudo systemctl start wg-quick@wg0
sudo systemctl enable wg-quick@wg0

Check status:

sudo systemctl status wg-quick@wg0
sudo wg show

The wg show output lists the interface, listening port, and any peers that have connected. Before any client connects, the peer list shows the public key and allowed IPs with no transfer counters. That is normal.

Step 6 - Creating the Client Configuration

On your local machine, create a config file. For the official WireGuard client on Windows or macOS, you import this as a tunnel. For Linux, place it in /etc/wireguard/ or use the wg-quick tool.

[Interface]
Address = 10.0.0.2/24
PrivateKey = <client1_private_key>
DNS = 1.1.1.1

[Peer]
PublicKey = <server_public_key>
Endpoint = your.server.ip:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

The critical line is AllowedIPs = 0.0.0.0/0. This routes all client traffic through the tunnel. If you only want certain subnets to go through the VPN, list them here, but for the China access use case, full tunnel is what you want. DNS is also routed, which prevents leaks where DNS queries go out the local interface.

PersistentKeepalive = 25 matters for clients behind NAT. It sends a keepalive packet every 25 seconds so the server knows where to send return traffic. Without it, a client behind a home router can go idle and the tunnel stalls until the next handshake.

Step 7 - Testing the Tunnel End to End

Bring up the client interface. On Linux:

sudo wg-quick up wg0

On Windows, click Activate in the WireGuard client. Then check the handshake:

sudo wg show

Back on the server, run the same command. You want to see a recent latest handshake timestamp, not never. That confirms the UDP path is open in both directions.

From the client, verify traffic exits through the server:

curl ifconfig.me

The IP returned should be your VPS public IP, not your local one. If it shows your home IP, the tunnel is up but routing is wrong, usually a missing MASQUERADE rule or the client AllowedIPs does not cover the traffic.

Troubleshooting Common WireGuard Issues

Three failures cover most of what goes wrong.

Handshake never completes. The client shows latest handshake: never. Almost always a firewall issue. On the server, check sudo firewall-cmd --list-all and confirm the UDP port is open. From your client, test reachability: nc -u your.server.ip 51820 or just watch sudo tcpdump -i eth0 udp port 51820 on the server while you attempt a connection. If packets arrive, the port is reachable. If not, the provider firewall or the OS firewall is dropping them. Also confirm the VPS provider's control panel does not have a separate firewall enabled, some do by default.

Tunnel is up but no internet. The handshake succeeds, curl fails. Run curl ifconfig.me on the client and sudo wg show on the server. If the server sees transfer counters incrementing but the client gets no response, the MASQUERADE rule is missing or pointing at the wrong interface. Check ip route show default on the server and fix the interface name in the PostUp line.

Connection drops after a few minutes. Usually NAT keepalive. Confirm PersistentKeepalive = 25 is set on the client. Some mobile carriers in China use very aggressive NAT timeouts, so you may need to lower it to 15.

For anything else, the server logs help:

sudo journalctl -u wg-quick@wg0 -f

FAQ

Which VPN protocol works best from China in 2026?

WireGuard is the most reliable for a self-hosted setup because it is lightweight and fast. Shadowsocks and VLESS are designed for evading DPI and work well, but they are proxies, not VPNs. For full tunnel routing, WireGuard on a non-standard UDP port is a solid choice. Nothing is guaranteed, carrier-grade filtering changes, so test from a real Chinese network before rolling out to users.

Does a Vietnam VPS give better latency to China than Singapore?

Often yes, but it depends on the carrier. Land routes from Vietnam to southern China are short, while Singapore traffic often goes through submarine cables that land in Hong Kong or Shanghai. Measure with ping and mtr from a machine in China to the VPS IP. There is no substitute for a live test. A VPS with monthly billing lets you test for a month without a long-term commitment.

What is the minimum VPS spec for a WireGuard server?

One vCPU and 512 MB of RAM is enough for dozens of peers. WireGuard does almost no per-packet CPU work, it runs in kernel space. The bottleneck is bandwidth, not compute. A 2 GB RAM VPS gives you headroom if you later add other services like a small web server or monitoring agent.

Is it legal to run a VPN on a VPS in Vietnam?

Operating a VPN service for personal or business use is not illegal in Vietnam. Providing public VPN services to Vietnamese users without a license is a separate question under local telecom rules. If you are running this for your own team or personal access, it falls in a normal gray area. Check current regulations if you plan to offer it publicly.

Can I use the same server for other workloads?

Yes, WireGuard is lightweight. Many people run it alongside n8n, GitLab, or a web server. Just make sure your bandwidth plan handles the extra traffic. If the VPS is mainly a VPN exit for China users, the international bandwidth pool matters more than CPU. Check the provider's fair use policy on international traffic before committing.

Related articles

越南机房自建 VPN 访问配置要点

本文介绍了在 Rocky Linux 9 VPS 上部署 WireGuard 的完整步骤,包括安装、密钥生成、路由配置和防火墙规则。对位于中国的用户而言,越南机房提供较近的网络出口,延迟通常优于欧美节点。建议使用非标准 UDP 端口以规避浅层流量检测,并通过实测验证链路质量。选择支持月付的越南 VPS 服务商,便于测试后长期使用。

Note: This guide is for general reference. Every system and infrastructure has its own specifics, so test each step in a safe environment and consult a qualified engineer before applying it in production.