{"id":2470,"date":"2026-11-02T13:35:50","date_gmt":"2026-11-02T20:35:50","guid":{"rendered":"https:\/\/traroro.com\/wireguard-vpn-kvm-vps-setup-guide\/"},"modified":"2026-11-02T13:35:50","modified_gmt":"2026-11-02T20:35:50","slug":"wireguard-vpn-kvm-vps-setup-guide","status":"publish","type":"post","link":"https:\/\/traroro.com\/zh\/wireguard-vpn-kvm-vps-setup-guide\/","title":{"rendered":"\u5982\u4f55\u7528 WireGuard \u5728 KVM VPS \u4e0a\u67b6\u8a2d\u81ea\u5df1\u7684 VPN \u4f3a\u670d\u5668"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Commercial VPN apps cost $60\u2013$120 a year, log more than they admit to, and share servers with thousands of strangers. A KVM-based VPS \u2014 the standard virtual-machine instance type sold by DigitalOcean, Linode, and Vultr \u2014 gives you a dedicated Linux box for about $4\u2013$6 a month, and WireGuard turns it into a VPN server in under fifteen minutes. This is the actual command-by-command setup, verified against DigitalOcean&#8217;s and Vultr&#8217;s current documentation.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Why KVM matters, and why WireGuard<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;KVM&#8221; (kernel-based virtual machine) is the virtualization type behind nearly every modern cloud VPS \u2014 it gives your instance its own kernel and full root access, unlike older container-based hosting (OpenVZ) that shares a kernel with other tenants and often blocks the networking modules WireGuard needs. Every droplet on DigitalOcean, every Linode instance, and every Vultr Cloud Compute instance is KVM, so any of the three works for this guide. Pick the cheapest plan with a public IPv4 address \u2014 1 CPU and 1GB RAM is overkill for a personal VPN.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WireGuard was merged into the Linux kernel in version 5.6 (2020) and is now the default choice over OpenVPN because its codebase is roughly 4,000 lines versus OpenVPN&#8217;s 100,000+, it uses modern cryptography (Curve25519, ChaCha20, Poly1305, BLAKE2s) with no negotiated cipher suites to misconfigure, and it consistently benchmarks faster on mobile due to its UDP-only, stateless design.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Spin up the VPS and install WireGuard<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create an Ubuntu 24.04 instance on any provider, note its public IP, and SSH in as root or a sudo user. WireGuard has shipped in Ubuntu&#8217;s default repositories since 18.04, so no PPA is required:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update\nsudo apt install wireguard qrencode -y\nwg --version<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>qrencode<\/code> isn&#8217;t strictly part of WireGuard \u2014 it&#8217;s a small utility you&#8217;ll use later to turn your phone&#8217;s config into a scannable QR code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Generate the server&#8217;s key pair<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">WireGuard authenticates peers with public-key cryptography instead of usernames and passwords. Generate the server&#8217;s private and public keys, and lock down the private key&#8217;s permissions immediately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/etc\/wireguard\numask 077\nwg genkey | tee server_private.key | wg pubkey > server_public.key<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run <code>cat server_private.key<\/code> and <code>cat server_public.key<\/code> to view them \u2014 you&#8217;ll paste the private key into the server config next.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Write the server configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First, identify your server&#8217;s default network interface \u2014 you&#8217;ll need its name for the NAT rule:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip -o -4 route show to default | awk '{print $5}'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This typically prints <code>eth0<\/code> on DigitalOcean\/Linode or <code>enp1s0<\/code> on Vultr. Create <code>\/etc\/wireguard\/wg0.conf<\/code> with that interface name substituted in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[Interface]\nAddress = 10.8.0.1\/24\nListenPort = 51820\nPrivateKey = &lt;paste server_private.key here&gt;\nPostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE\nPreDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE\n\n# Peers are appended below, one [Peer] block per device\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>10.8.0.1\/24<\/code> is the VPN&#8217;s private subnet \u2014 the server is always the first address in it, and each client gets the next one (10.8.0.2, 10.8.0.3, and so on). Port 51820\/UDP is WireGuard&#8217;s conventional default; you can pick a different one, but it must match what clients connect to.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Turn on IP forwarding<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">By default, Linux won&#8217;t route traffic between network interfaces \u2014 you have to explicitly enable it so the server can forward client traffic out to the internet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo 'net.ipv4.ip_forward = 1' | sudo tee -a \/etc\/sysctl.conf\nsudo sysctl -p<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Open the firewall<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re running UFW (Ubuntu&#8217;s default firewall), allow the WireGuard port before starting the service, or you&#8217;ll have a working tunnel that nothing can reach:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw allow 51820\/udp\nsudo ufw allow OpenSSH\nsudo ufw enable<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">DigitalOcean and Linode leave the cloud-level firewall open by default (UFW is what&#8217;s actually blocking you); Vultr&#8217;s Cloud Firewall and DigitalOcean&#8217;s optional Cloud Firewall product are configured separately in their dashboards if you&#8217;ve enabled one \u2014 add a matching UDP 51820 rule there too.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Start WireGuard and enable it at boot<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl start wg-quick@wg0\nsudo systemctl enable wg-quick@wg0\nsudo systemctl status wg-quick@wg0\nsudo wg show wg0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>wg show wg0<\/code> should list your interface, its public key, and listening port with no peers yet. If <code>systemctl status<\/code> shows &#8220;active (exited)&#8221; rather than &#8220;active (running),&#8221; that&#8217;s normal \u2014 <code>wg-quick<\/code> is a one-shot script that configures the interface and then exits.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 7: Create a client and generate its QR code<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each device (phone, laptop) is its own &#8220;peer&#8221; with its own key pair. Generate one:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/etc\/wireguard\nwg genkey | tee client1_private.key | wg pubkey > client1_public.key<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add this peer to the bottom of <code>\/etc\/wireguard\/wg0.conf<\/code> on the server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[Peer]\nPublicKey = &lt;paste client1_public.key here&gt;\nAllowedIPs = 10.8.0.2\/32<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then reload the config without dropping the tunnel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo wg syncconf wg0 &lt;(wg-quick strip wg0)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now write the client&#8217;s own config file \u2014 this is the file the phone or laptop actually imports \u2014 as <code>client1.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[Interface]\nPrivateKey = &lt;paste client1_private.key here&gt;\nAddress = 10.8.0.2\/24\nDNS = 1.1.1.1\n\n[Peer]\nPublicKey = &lt;paste server_public.key here&gt;\nEndpoint = &lt;your server's public IP&gt;:51820\nAllowedIPs = 0.0.0.0\/0\nPersistentKeepalive = 25<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>AllowedIPs = 0.0.0.0\/0<\/code> routes all of the client&#8217;s traffic through the tunnel (a full VPN); set it to just <code>10.8.0.0\/24<\/code> instead if you only want access to the server&#8217;s private network, not full internet routing. <code>PersistentKeepalive = 25<\/code> sends a keepalive packet every 25 seconds \u2014 necessary for phones, which otherwise drop the UDP mapping on cellular networks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Turn that file into a QR code you can scan with the official WireGuard app (iOS\/Android) instead of typing it in by hand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>qrencode -t ansiutf8 < client1.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In the WireGuard mobile app, tap the \"+\" button, choose \"Scan from QR code,\" and point the camera at the terminal. On desktop, copy <code>client1.conf<\/code> to the machine running the official WireGuard client and import it directly \u2014 there's no QR step needed there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Treat that QR code and <code>.conf<\/code> file like a password: anyone who scans it gets full access to the tunnel. Delete <code>client1.conf<\/code> from the server's disk once you've transferred it, since the private key doesn't need to live there.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verify it actually works<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Connect from the client, then from inside the tunnel check that your traffic is actually exiting through the VPS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ping -c 4 10.8.0.1\ncurl ifconfig.me<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The ping should succeed, and <code>curl ifconfig.me<\/code> should return your VPS's public IP, not your home or office IP. Back on the server, <code>sudo wg show wg0<\/code> will now show the peer with a \"latest handshake\" timestamp and transfer counters \u2014 proof data is actually flowing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Repeat Step 7 for every additional device, incrementing the IP each time (10.8.0.3, 10.8.0.4...). One $5\/month KVM instance comfortably handles a household's worth of phones and laptops, and because you control the box end to end, there's no provider logging your browsing to worry about \u2014 only your own.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Commercial VPN apps cost $60\u2013$120 a year, log more than they admit to, and share servers with thousands of strangers. A KVM-based VPS \u2014 the standard virtual-machine instance type sold by DigitalOcean, Linode, and Vultr \u2014 gives you a dedicated Linux box for about $4\u2013$6 a month, and WireGuard turns&#8230;<\/p>","protected":false},"author":0,"featured_media":2473,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[4],"tags":[],"class_list":["post-2470","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-lifestyle"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/traroro.com\/wp-content\/uploads\/2026\/11\/vpn_kvm_featured.jpg","_links":{"self":[{"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/posts\/2470","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/comments?post=2470"}],"version-history":[{"count":0,"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/posts\/2470\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/media\/2473"}],"wp:attachment":[{"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/media?parent=2470"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/categories?post=2470"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/traroro.com\/zh\/wp-json\/wp\/v2\/tags?post=2470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}