Paano mag-self-host ng WireGuard VPN sa VPS
I-set up ang WireGuard sa Linux VPS gamit ang key pair, wg0.conf, IP forwarding, NAT, AllowedIPs at DNS, kasama ang mga sanhi ng handshake failures.
Mga binubuo mo
Ang WireGuard VPN sa server na pagmamay-ari mo ay nangangailangan ng humigit-kumulang 40 linya ng configuration: isang key pair, isang interface file, isang sysctl, isang NAT rule, at isang firewall hole. Simple ang installation, kaya nakatuon ang karamihan ng gabay na ito sa mga karaniwang nasisira, sa mga permission ng key, AllowedIPs, forwarding, at DNS.
Ang WireGuard ay isang Layer 3 tunnel sa kernel at bahagi na ng mainline mula Linux 5.6. Kaya kasama na ito sa Ubuntu 24.04 at Debian 13 at hindi nangangailangan ng external module. Walang cipher negotiation, certificate authority, o hakbang para sa username/password. Ang peer ay isang public key kasama ang mga IP address na maaaring gamitin ng key na iyon. Kapag nabigo ang isang packet sa MAC check, itinatapon ito nang walang reply. Dahil dito, hindi sumasagot ang port sa mga scan. Ang kapalit nito: walang auth server, kaya para alisin ang access, kailangan mong mag-delete ng peer sa server.
Suriin muna ang virtualisation
Kailangan ng WireGuard ng kernel kung saan maaaring mag-load ng module. Sa isang KVM VPS, gumagana ito out of the box. Sa container virtualisation na nakikibahagi sa kernel ng host, gaya ng OpenVZ at LXC, mabibigo ang unang command at magpapakita ng RTNETLINK answers: Operation not supported. Ang fallback ay ang userspace implementation na wireguard-go. Suriin muna gamit ang sudo modprobe wireguard && echo ok.
Mga key na i-generate nang hindi inilalantad ang mga ito
Ang /etc/wireguard/server.key na nababasa ng lahat ay katumbas ng walang VPN. Hindi maaasahan ang karaniwang linya na umask 077 && wg genkey | sudo tee ... dahil naglalapat ang sudo ng sarili nitong umask sa file na ginagawa ng tee. Itakda nang tahasan ang mode.
sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.keyI-generate ang client pair sa parehong paraan. Nagdaragdag ang wg genpsk ng optional na pre-shared key, na may tig-isang linya sa bawat config.
Ang interface ng server: /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32chmod 600 ito; ipinapakita ng startup warning na accessible sa lahat ang file kapag nalaktawan mo iyon. Ang Address ay address ng server sa loob ng tunnel at may mask ng buong VPN subnet. Pumili ng range na hindi mo makikita sa karaniwang network, dahil ang 192.168.1.0/24 ay nagkakaroon ng conflict sa maraming home router na ginagamit ng mga client, kaya tahimik na natatalo ang tunnel sa local route.
Ang AllowedIPs ng isang peer sa panig ng server ay isang /32, ang nag-iisang tunnel address na pagmamay-ari ng client na iyon. Kapag ibinigay mo ang parehong allowed IP sa dalawang peer, mapupunta ito sa peer na huling na-configure, at hihinto ang unang peer sa pagtanggap ng traffic nang walang error na lumalabas kahit saan. Iwanang hindi naka-set ang SaveConfig, o muling isusulat ng wg-quick down ang file na ito mula sa live state.
Gawing router ang box
Ibinabagsak ng Linux server ang mga packet na hindi nakalaan dito. Hindi naka-enable bilang default ang forwarding at source NAT.
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
| sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forwardGumagana ang isang bare sysctl -w hanggang sa susunod na reboot, at pagkatapos ay tahimik itong tumitigil. Kailangan ng NAT ang egress interface, ang NIC na kumokonekta sa internet, hindi ang wg0. Huwag ipagpalagay ang eth0; kunin ang iyo mula sa ip route show default, dahil gumagamit ang mga kasalukuyang image ng mga pangalang gaya ng enp1s0 o ens3.
Firewall: the port, and the forward path
One nftables file covers filter and NAT. Write /etc/nftables.conf, it flushes the existing ruleset, so skip this on a box already managed by ufw or Docker.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 accept
udp dport 51820 accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "wg0" oifname "enp1s0" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
}
}Apply it with sudo systemctl enable --now nftables, keeping a second SSH session open: policy drop plus a typo in the SSH rule locks you out of your own server. Note what the forward chain does not allow, wg0 to wg0. Peers reach the internet, not each other; add iifname "wg0" oifname "wg0" accept for a peer-to-peer VPN. The same chain governs what a peer may touch on the server itself, which matters when the box doubles as a remote development box running Claude Code in tmux and you would rather not expose that side of it publicly.
On a ufw box: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw, and a *nat POSTROUTING MASQUERADE rule at the top of /etc/ufw/before.rules.
Ipatakbo ito gamit ang systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showAng wg-quick ang gumagawa ng interface, nagdaragdag ng mga address, at nag-i-install ng mga route na hinango mula sa AllowedIPs. Ang enable --now ang mahalagang bahagi: mawawala ang manu-manong pagpapatakbo ng wg-quick up wg0 pagkatapos ng susunod na reboot, at nangangahulugan ang kernel upgrades ng mga reboot. Mananatiling tahimik ang isang unit na hindi muling gumagana pagkatapos ng ganitong reboot hanggang sa may sumubok kumonekta. Kaya ang OnFailure= drop-in sa wg-quick@wg0 na nakaturo sa sarili mong ntfy server ang pinakamurang paraan para makatanggap ka ng abiso sa phone mo, sa halip na manggaling ito sa user na hindi makapag-login.
Ang client config at ang setting na madalas napagkakamalian
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25AllowedIPs ay may dalawang magkaibang gamit, at ang pagsasama sa mga ito ang pinagmumulan ng karamihan ng kalituhan sa WireGuard.
Sa outbound traffic, routing table ito. Ine-encrypt at ipinapadala sa peer ang packet na ang destination ay tumutugma sa AllowedIPs ng isang peer. Ipinapadaan ng 0.0.0.0/0, ::/0 ang lahat ng traffic sa tunnel: full tunnel, kung saan ang server ang default route. Mas maikli ang listahan sa split tunnel: pinapadaan ng AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 ang VPN traffic at isang private network sa likod ng server, habang ginagamit ng iba pang traffic ang local route nito. Dahil sa maikling listahang ito, maaaring manatiling ganap na hindi exposed sa public internet ang mga serbisyo. Halimbawa, mananatiling reachable ng mga peer at invisible sa iba ang isang private Nextcloud instance sa VPS na naka-bind sa tunnel address, pati ang nested-virtualisation lab VMs na tumatakbo sa parehong box.
Sa inbound traffic, access-control list ito. Ibinabagsak ang decrypted packet mula sa isang peer kapag wala sa AllowedIPs ng peer na iyon ang source address nito. Kaya itinatakda ng server ang 10.8.0.2/32 para sa laptop: kung 0.0.0.0/0 ang ilalagay doon, maaaring mag-spoof ang client ng anumang address sa tunnel.
Ang PersistentKeepalive ay para sa mga client na nasa likod ng NAT, kung saan nananatiling bukas ang UDP mapping ng router habang may dumadaloy na packet. Kapag nag-expire ito, hindi na maabot ng server ang client. Pinananatiling bukas ng PersistentKeepalive = 25 ang mapping. Itakda ito sa client, hindi sa server na may public IP.
DNS at ang leak na hindi napapansin
Kapag mayroon kang AllowedIPs = 0.0.0.0/0 at walang linyang DNS =, ginagamit pa rin ng client ang resolver na natutuhan nito mula sa local network, ang café router sa 192.168.1.1. Mas specific ang route na iyon kaysa sa default route, kaya lumalabas ang mga DNS query sa local link nang cleartext habang naka-tunnel ang lahat ng iba pang traffic. Private ang traffic; hindi private ang listahan ng mga pangalan.
May dalawang wastong option. Ituro ang DNS sa public resolver (DNS = 9.9.9.9), at dadaan ang mga query sa tunnel bago lumabas sa server mo, pero makikita pa rin ng resolver na iyon ang mga query. O magpatakbo ng unbound o dnsmasq na naka-bind sa 10.8.0.1, itakda ang DNS = 10.8.0.1, at idagdag ang udp dport 53 iifname "wg0" accept sa input chain. Itakda ang linyang iyon at huwag nang baguhin ang resolver; kung hindi, walang mare-resolve.
Sa mga Linux client, inilalapat ng wg-quick ang DNS sa resolvconf; kapag wala ito, makukuha mo ang resolvconf: command not found. I-install ang openresolv, o itakda ang PostUp = resolvectl dns %i 10.8.0.1 sa client na gumagamit ng systemd-resolved.
Pagdaragdag at pag-aalis ng mga peer nang hindi pinapababa ang tunnel
Ang pag-restart ng interface para magdagdag ng user ay magdidiskonekta sa lahat ng kasalukuyang koneksyon. Idagdag ang block na [Peer] sa wg0.conf, pagkatapos ay i-reload ang peer set habang tumatakbo ang tunnel.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'Inilalabas ng wg-quick strip ang configuration nang hindi kasama ang mga key na para lamang sa wg-quick (Address, DNS, PostUp), at inilalapat ng syncconf ang mga pagbabago habang nananatiling aktibo ang mga session. Mga peer lamang ang ina-update nito: kailangan pa rin ng kumpletong down/up kapag nagbago ang Address. I-revoke gamit ang sudo wg set wg0 peer <public key> remove, pagkatapos ay tanggalin ang block sa file; kung hindi, babalik ito sa susunod na reload.
Mga failure mode at ang mga string na makikita mo
Hindi nakukumpleto ang handshake. Inililista ng wg show ang peer nang walang latest handshake, at ganito ang nasa client logs:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)Walang dumarating, o walang tinatanggap. Suriin sa ganitong pagkakasunod-sunod: bukas ba ang UDP 51820 sa VPS firewall at sa network firewall ng provider mo, na hiwalay na kontrol sa karamihan ng panel; tama ba ang Endpoint address at port; tama ba ang pagkakapareha ng mga key. Ang key sa [Peer] block ng client ay dapat public key ng server. Ang pag-paste ng private key o sariling public key ng client ay nagdudulot ng eksaktong sintomas na ito. Ipinapakita ng sudo tcpdump -ni any udp port 51820 sa server kung may dumarating na packet. Bilang default, walang inilalabas na log ang kernel module. Lumilitaw ang mga mensahe ng WireGuard sa dmesg pagkatapos mong i-enable ang dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). Kapag naka-enable ito, makikita ang key mismatch bilang invalid-MAC drop.
Gumagana ang handshake pero walang internet. Matagumpay ang ping 10.8.0.1 pero nagti-time out ang ping 1.1.1.1: nawawala ang forwarding o NAT. Tiyaking ang sysctl net.ipv4.ip_forward ay 1, pagkatapos ay bantayan ang mga counter habang nagpi-ping ang client gamit ang sudo nft list ruleset o sudo iptables -t nat -L POSTROUTING -n -v. Kapag zero ang packets sa masquerade rule, mali ang pangalan ng egress interface. Kapag tumataas ang counter pero walang reply, malamang nasa forward chain policy ang problema.
Gumagana ang internet pero hindi ang mga pangalan. Matagumpay ang ping 1.1.1.1 at nagbabalik ang curl https://example.com ng Could not resolve host. Nawawala ang DNS line, o tumutukoy ito sa resolver na hindi maabot mula sa loob ng tunnel.
May ilang HTTPS site na hindi naglo-load. Maayos ang SSH at ping, pero natitigil ang malalaking page. Path MTU ang sanhi: nagdaragdag ng overhead ang tunnel, at maaaring i-drop ng link sa gitna ang mga packet na masyadong malaki nang walang ICMP message na makabalik. Bawasan ang MTU sa client [Interface], subukan ang 1420, pagkatapos ang 1380, at saka ang 1280. Kung nalulutas ng pagbaba ng MTU ang pagtigil pero mababa pa rin ang throughput, huwag nang manghula gamit ang mga bilog na numero. Gamitin ang paghahanap sa aktuwal na path MTU sa pamamagitan ng bisection at pag-clamp ng TCP MSS, na tumutulong ding alisin ang mga dahilang walang kinalaman sa tunnel.
Tumangging magsimula ang interface. Ibig sabihin ng Address already in use ay may ibang process na gumagamit ng UDP 51820. Ang Cannot find device wg0 pagkatapos ng nabigong up ay karaniwang nangangahulugang tinanggihan ang configuration. Basahin ang journalctl -u wg-quick@wg0 -n 50.
Paglipat mula sa Streisand o OpenVPN
Hindi na pinapanatili ang Streisand at naka-archive na ang repository nito. Ang pagpapatakbo ng VPN gamit ang inabandunang automation ay unti-unting nagiging security problem. Walang in-place upgrade. Hindi rin mako-convert ang PKI ng OpenVPN. Walang certificates, CA, o expiry ang WireGuard. Kaya bawat client ay kailangang bigyan ng bagong key pair.
Isagawa ang migration nang sabay sa kasalukuyang serbisyo. Maaaring tumakbo ang WireGuard sa UDP 51820 kasabay ng OpenVPN sa 1194 sa parehong server. I-set up ang wg0, ilipat ang mga client nang paisa-isa, at saka ihinto ang lumang serbisyo. Hindi naililipat sa WireGuard ang username/password at revocation model ng OpenVPN. Kung kailangan mo ng mga account o audit trail, magdagdag ng layer para rito sa ibabaw ng WireGuard.
Mga backup, upgrade, at mga nagiging bottleneck kapag lumalaki ang scale
/etc/wireguard ang server. Gumawa ng backup nito (sudo tar czf wg-backup.tgz -C /etc wireguard, mode 600, at itago sa labas ng server) para ma-rebuild mo ito sa bagong VPS sa loob ng ilang minuto. Kapag nawala ang private key ng server, kailangang mag-issue muli ng config para sa bawat client dahil naka-pin ang public key ng server sa mga client. Karaniwang apt upgrade ang mga upgrade, kasabay ng reboot para sa kernel updates, at awtomatikong babalik ang wg-quick@wg0 kung na-enable mo ito.
Maliit ang per-peer state at tumatakbo sa kernel ang cryptography, kaya ang limitasyon ay ang CPU at bandwidth allowance ng iyong VPS, hindi ang anumang nasa config na ito. Sukatin ito gamit ang iperf3 sa tunnel sa halip na umasa sa inilathalang figure. Ang nagiging mahirap kapag lumalaki ang scale ay ang operations. Kailangan ng bawat peer ng natatanging tunnel IP. Kapag mano-mano mong ine-edit ang animnapung [Peer] block, madaling makalusot ang duplicate AllowedIPs. Sa halip, i-generate ang mga config gamit ang script. Ang isang server ay isang UDP endpoint at isang point of failure. Walang clustering ang WireGuard. Para magkaroon ng redundancy, kailangan mo ng pangalawang server na may sarili nitong mga key. Mano-mano pa rin ang key rotation, kaya itala kung sino ang may hawak ng bawat key at kung paano mo ito ire-revoke. Kapag hindi na sapat ang text file para sa bookkeeping na ito, karaniwang ginagamit ang control plane sa ibabaw ng parehong kernel data plane. Hinahawakan ng isang self-hosted na NetBird server ang address allocation, peer distribution, at setup keys na kung hindi ay mano-mano mong ginagawa. Kung isang server na masyado nang dagdag ang pagpatakbo ng control plane, ang Tailscale ang magho-host nito para sa iyo. Saklaw ng libreng plan nito ang anim na user na may unlimited na device, kaya hindi ito kailangang bayaran ng karamihan sa mga personal fleet. Paglampas doon, mas nakabatay ang bayarin sa mga tao kaysa sa mga machine. Kaya nakadepende ang aktuwal na binabayaran ng isang household o maliit na team sa dami ng taong may login, hindi sa dami ng peer na mano-mano mong idaragdag sa wg0.conf. Sa ganitong setup, ang bookkeeping ng split-tunnel AllowedIPs ay nagiging pag-aanunsyo ng iyong private range mula sa isang subnet router. Isang VPS lamang ang nag-aanunsyo nito, at centrally itong ina-approve sa halip na i-paste sa bawat client file. Nakadepende sa kung ano talaga ang maaabot ng hosted control plane kung sulit ang ganitong trade-off. At hindi nito kailanman hinahawakan ang mga key na nag-e-encrypt ng iyong traffic, pero ito ang nagpapasya kung aling mga peer ang makakakilala sa isa’t isa.
Kailangan nito ng Linux box na kontrolado mo, public IP, kernel na puwede mong pag-load-an ng module, at firewall na pagmamay-ari at kontrolado mo mula dulo hanggang dulo.
FAQ
Bakit hindi kailanman nakukumpleto ang WireGuard handshake?
Ang wg show na naglilista ng peer na walang latest handshake ay nangangahulugang hindi dumarating o hindi tinatanggap ang mga packet. Suriin ang UDP 51820 sa firewall ng VPS at sa hiwalay na network firewall ng provider mo. Tiyakin ang Endpoint host at port. Pagkatapos, tiyaking hindi napagpalit ang mga key. Dapat nasa [Peer] block ng client ang public key ng server. Ipinapakita ng sudo tcpdump -ni any udp port 51820 sa server kung talagang dumarating ang mga packet. Iniuulat lamang ng dmesg ang mga handshake failure ng WireGuard kapag pinagana mo ang dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). Sa ganitong sitwasyon, lumilitaw ang maling key bilang invalid-MAC drop.
Nakakonekta ang tunnel pero wala akong internet. Ano ang kulang?
Ang sabay na paggana ng ping 10.8.0.1 at pag-timeout ng ping 1.1.1.1 ay tumutukoy sa problema sa forwarding o NAT. Tiyaking ang sysctl net.ipv4.ip_forward ay may halagang 1 at nakatakda ito sa /etc/sysctl.d/, hindi lamang sa pamamagitan ng sysctl -w na nawawala kapag nag-reboot. Pagkatapos, suriin ang masquerade rule. Dapat nitong tukuyin ang aktuwal na egress interface mula sa ip route show default, enp1s0, o, mas bihira, ens3. Tanging sa mga bihirang kaso ito nagmumula sa eth0.
Kailangan ba ang DNS = line sa configuration ng client?
Kapag full tunnel at walang DNS = line, ginagamit ng client ang resolver na natutuhan nito mula sa local network. Lumalabas ang mga query na ito nang hindi naka-encrypt sa local link, habang ang lahat ng iba pa ay ipinapadaan sa tunnel. Ituro ang DNS sa isang public resolver. Maaari ka ring magpatakbo ng unbound/dnsmasq na naka-bind sa 10.8.0.1 at buksan ang udp dport 53 iifname "wg0" sa input chain.
Ano ba talaga ang kinokontrol ng AllowedIPs?
Dalawa ang tungkulin nito. Para sa outbound traffic, routing table ito: ang traffic na tumutugma sa AllowedIPs ng isang peer ay ine-encrypt at ipinapadala sa peer na iyon. Para sa inbound traffic, access-control list ito: dini-drop ang decrypted packet kapag ang source nito ay wala sa AllowedIPs ng peer na iyon. Kaya naglilista ang server side ng isang /32 para sa bawat client, samantalang maaaring maglista ang client side ng 0.0.0.0/0.
Tatakbo ba ang WireGuard sa anumang VPS?
Sa KVM VPS, gumagana ito gamit ang in-kernel module at walang karagdagang setup. Sa container virtualisation na gumagamit ng shared host kernel, gaya ng OpenVZ o LXC, nabibigo ang modprobe wireguard dahil sa Operation not supported. Ang fallback ay ang wireguard-go userspace implementation. Patakbuhin muna ang sudo modprobe wireguard && echo ok bago ang iba pa.