Security

Build a Stable WireGuard VPN Server on AlmaLinux for China Teams

When your team sits in China and the rest of the company runs on infrastructure in Vietnam, you need a tunnel that survives the Great Firewall's mood swings. OpenVPN and IPSec often crawl in these conditions. WireGuard, with its minimalist UDP-based design, behaves far better. This guide walks through building a stable WireGuard VPN server on an AlmaLinux 9 VPS located in Vietnam, tuned specifically for teams accessing it from mainland China. You will end with a persistent, systemd-managed tunnel where each team member has their own key pair, and you will know exactly which settings decide between a tunnel that works and one that times out every few minutes.

Prerequisites

  • An AlmaLinux 9 VPS with a public IPv4 address. The server should sit in a Vietnam datacenter if your goal is low-latency access to Vietnam-hosted resources. A Linux VPS with a dedicated IPv4 works here.
  • Root or sudo access on the server.
  • Basic firewall knowledge. You will use firewalld, the default on AlmaLinux.
  • WireGuard client software on each team member's machine. Windows, macOS, Linux, and Android clients all exist. iOS clients are available but require more care with App Store rules.

越南 VPS 提供低延迟隧道,能显著改善中国团队访问越南内部系统的体验。

A Vietnam-based VPS offers a low-latency tunnel, which noticeably improves how a China-based team reaches Vietnam-hosted systems.

Why WireGuard beats OpenVPN for China connections

WireGuard lives in the kernel. It does not need userspace processes shuffling packets, so it uses less CPU and responds faster. Its UDP-only transport is both a weakness and a strength. Deep packet inspection can fingerprint WireGuard, but in practice UDP traffic is far less throttled than TCP-based VPNs, which suffer when the firewall resets connections.

The protocol is also quiet. OpenVPN handshakes are chatty and recognizable. WireGuard sends nothing until a client wants to reach the network, and its cryptographic noise protocol is indistinguishable from random UDP to most middleboxes. For a team in China, this means fewer resets and less interference on the wire.

Do not expect magic. The Great Firewall adapts. If a tunnel dies, the usual causes are UDP packet loss or an idle timeout from the carrier. WireGuard sends periodic keepalives to solve the second problem. You will configure those in this guide.

Step 1 - Install WireGuard on AlmaLinux 9

AlmaLinux 9 ships with WireGuard in the EPEL repository. Install EPEL first, then WireGuard tools:

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

WireGuard runs as a kernel module. On AlmaLinux 9 the module ships with the kernel itself, so you do not need to install a separate DKMS package.

Verify the install worked before moving on:

sudo modprobe wireguard
lsmod | grep wireguard

You should see a line listing the wireguard module. If nothing shows, reboot and try again, the kernel module loads at boot time.

Step 2 - Generate server and client keys

WireGuard uses Curve25519 keys. Generate a key pair for the server and one for each client. Store the private keys only on their respective machines. The public keys travel freely.

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

The umask 077 keeps the private key files readable only by root. WireGuard refuses to start if the private key file has loose permissions.

Print the public keys, you need them for the server configuration:

cat /etc/wireguard/client1_public.key
cat /etc/wireguard/client2_public.key

Step 3 - Write the server configuration

Create /etc/wireguard/wg0.conf. The server uses IP 10.0.0.1, with clients starting at 10.0.0.2.

sudo nano /etc/wireguard/wg0.conf

Paste the following, replacing the placeholders with your actual keys:

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>
SaveConfig = false

# Client 1, a laptop in Shanghai
[Peer]
PublicKey = <client1_public_key>
AllowedIPs = 10.0.0.2/32

# Client 2, a desktop in Shenzhen
[Peer]
PublicKey = <client2_public_key>
AllowedIPs = 10.0.0.3/32

Keep SaveConfig = false. When true, WireGuard rewrites the config file when the interface shuts down, which strips comments and can reorder sections. That causes confusing diffs later.

Note that AllowedIPs on the server side contains only the client's tunnel IP. You do not route the whole internet through this server, unless you want to build a full-tunnel VPN. For teams that only need to reach Vietnam-hosted internal systems, split tunneling is far more stable. Full tunnels force all Chinese team traffic through the Vietnam uplink, which adds latency and can trigger more aggressive throttling.

Step 4 - Enable IP forwarding and firewall rules

The server must forward packets between the tunnel and the physical network. Enable forwarding in the kernel:

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

Confirm the setting is active:

sysctl net.ipv4.ip_forward

The output should show net.ipv4.ip_forward = 1.

Now open the WireGuard UDP port in firewalld and add the tunnel interface to the trusted zone. Do not put the whole server in the trusted zone, only the VPN interface:

sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --permanent --new-zone=wireguard
sudo firewall-cmd --permanent --zone=wireguard --add-interface=wg0
sudo firewall-cmd --reload

The new wireguard zone trusts everything arriving on the tunnel interface, which is internal traffic. The public zone still protects the rest of the server. If you later need to allow the VPN clients to reach specific services, you manage the policy between the zones rather than loosening the public zone.

Step 5 - Start the tunnel and test locally

Enable and start the WireGuard interface with systemd:

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

Check the status before testing any client:

sudo wg show

You should see the interface, the listening port, and the two peers you defined, each with no handshake yet. Also confirm the interface is up:

ip addr show wg0

The output must show inet 10.0.0.1/24 scope global wg0. If the interface is down, check the kernel log:

journalctl -u wg-quick@wg0 -n 30

Most startup failures come from a wrong private key or a malformed config line. Validate the config with:

sudo wg-quick strip wg0

This prints the parsed config so you can spot typos.

Step 6 - Configure client machines

Each client needs its own config file. On Windows and macOS clients you import this file into the official WireGuard app. On Linux you place it in /etc/wireguard/.

[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 = 10.0.0.0/24
PersistentKeepalive = 25

Change the Address to match the peer entry on the server. Client 2 uses 10.0.0.3/24.

PersistentKeepalive matters more than any other setting for China links. Chinese carriers drop UDP mappings after a short idle period, often under two minutes. A keepalive every 25 seconds keeps the NAT mapping alive and triggers a fresh handshake before the carrier forgets the flow. Without it, the tunnel appears dead until the client sends a packet, which then sits in a stale NAT state until the next keepalive succeeds.

If your team needs to reach multiple internal networks rather than just the VPN subnet, adjust AllowedIPs on the client to list those ranges, for instance 10.0.0.0/24, 172.16.5.0/24. The server then needs a route toward those networks and the peer entry must list the same ranges.

Step 7 - Test the tunnel from a client in China

Start the tunnel on the client and ping the server's tunnel IP:

ping 10.0.0.1

With a working tunnel you should see replies in a few hundred milliseconds. The exact latency depends on the route from the client to the Vietnam server, which is why you cannot promise a fixed number. What you can check is that the handshake completes:

sudo wg show

On the server this now shows a recent handshake time for the connected peer. A missing handshake means a firewall problem, wrong Endpoint, or the UDP port is blocked somewhere along the path.

For the team in China, latency to Vietnam typically ranges from 30 to 80 ms depending on whether traffic crosses via Hong Kong or goes direct. Do not trust a single ping. Run a longer test with ping -c 100 and watch for packet loss. Loss over 2% causes noticeable stutter in interactive work.

Troubleshooting common China link failures

Three failures dominate. The first: the client handshakes once, then the tunnel goes silent after a few minutes. This is the NAT timeout problem. Raise PersistentKeepalive to 20 seconds on the client and verify it applies by restarting the tunnel. Do not set it below 15 seconds, the extra noise can itself draw attention.

The second: the handshake never completes. Run sudo wg show on the server while the client attempts to connect. If the client's endpoint shows the wrong public IP, the client is behind carrier-grade NAT and the keepalive has not yet punched the mapping. Wait a few seconds and check again, the endpoint should correct itself after the first keepalive.

The third: the tunnel works but traffic to Vietnam-hosted services stalls. This is usually an MTU problem. WireGuard's default MTU is 1420 bytes, which fits most paths, but some Chinese carriers fragment aggressively. Test with a lower MTU on the client:

sudo ip link set dev wg0 mtu 1280

If performance improves, make the MTU permanent in the client config file with an MTU = 1280 line under [Interface]. This setting costs a little throughput on good links but massively improves reliability on broken ones.

Why run this on a Vietnam VPS at all

Put the VPN server where the resources live. If your company systems, databases, and internal tools are in Vietnam, a VPN server next to them keeps the tunnel short. Traffic from a Shanghai employee goes China to Vietnam once, then to the internal network. If the VPN sat in Singapore, traffic would hop China to Singapore to Vietnam, adding latency and another border crossing.

The dedicated IPv4 also matters. A shared IP used by many VPN customers may already be on blocklists, which some Chinese networks treat with suspicion. A clean, dedicated IPv4 from a Vietnamese provider carries less baggage. Infrastructure quality matters too, choose a provider with a direct route to Hong Kong or China. A Linux VPS with a dedicated IPv4 in a Vietnam datacenter with good international peering behaves noticeably better than one on a congested upstream. Ask the provider about their route toward China before you commit. thueVPS offers KVM-based plans on NVMe storage with a dedicated IPv4 and monthly billing, which suits a team that wants to test the tunnel without a long-term contract.

Keeping the tunnel stable over months

WireGuard itself rarely crashes. The problems come from the environment. Kernel updates on AlmaLinux require a reboot to load the new kernel module, which drops the tunnel. Plan reboots during low-usage windows, or check whether the running kernel already supports WireGuard before rebooting after an update.

Monitor the tunnel with a cron job that pings the gateway and restarts the interface if it fails. A simple script beats manual checks when a team depends on the link:

#!/bin/bash
if ! ping -c 3 -W 2 10.0.0.2 > /dev/null 2>&1; then
    systemctl restart wg-quick@wg0
    logger "WireGuard tunnel restarted after ping failure"
fi

Put this in /etc/cron.d/wg-watchdog and make it executable. The false-positive rate is low because the ping target is a client that should always respond once the tunnel is established.

Also record which client key belongs to which person. When someone leaves the company, remove their peer entry from the server config and restart the interface. Without a key inventory, you cannot revoke access cleanly, and a former employee keeps a working tunnel into your network.

FAQ

Why does my WireGuard tunnel drop every few minutes from China?

Carrier-grade NAT and UDP timeouts. Chinese carriers drop idle UDP flows quickly. Add PersistentKeepalive = 25 on the client and restart the tunnel. If it still drops, lower the MTU to 1280 on the client, some paths fragment the default 1420-byte packets.

Should I route all traffic through the VPN or only internal ranges?

Split tunnel for stability. Route only the internal networks the team needs, for instance 10.0.0.0/24. A full tunnel forces all Chinese internet traffic through the Vietnam uplink, adding latency and increasing the chance of throttling.

Which port should I use for WireGuard?

The default 51820/udp works, but changing to a high random port such as 44330 reduces casual scans. Port 443 over UDP is more likely to be noticed and blocked because it mimics QUIC traffic, keep the default high range instead.

Can I run WireGuard server and other services on the same VPS?

Yes. WireGuard uses little CPU at typical team scale. A 2 GB RAM VPS easily handles a dozen concurrent tunnels alongside nginx or GitLab. Policy routing is the only complexity, make sure the firewall zones separate the VPN interface from the public one.

Does a Vietnamese VPS give better latency to China than Hong Kong or Singapore?

Not automatically. Latency depends on the route. Vietnam to China is often 30 to 80 ms, similar to Hong Kong for northern cities. The advantage of Vietnam is proximity to Vietnam-hosted resources, not to China. Measure with ping and traceroute before committing.

Related articles

中国团队越南 WireGuard 部署要点

在越南 AlmaLinux VPS 上部署 WireGuard,为中国团队提供稳定的远程通道。关键设置包括启用 IP 转发、通过 firewalld 放行 UDP 端口,并为每个客户端单独生成密钥。客户端必须配置 PersistentKeepalive 25 秒以避免运营商 NAT 超时断线,遇到丢包时降低 MTU 至 1280 可提升稳定性。建议采用分流模式,只路由内部网段,不把全部流量引入越南线路。

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.