wg-easy sa Docker: WireGuard VPN na may web UI
I-set up ang wg-easy gamit ang Docker Compose, tamang ports, NET_ADMIN, sysctls, at QR code onboarding. Alamin ang Version 15 environment settings.
Mga binubuo mo
Ang wg-easy ay WireGuard na may web interface at tumatakbo bilang isang Docker container. Pinamamahalaan nito ang WireGuard interface para sa iyo at nagdaragdag ng browser UI para gumawa ng mga client. Bawat client na gagawin mo ay magkakaroon ng config file at QR code, kaya makakakonekta ang phone sa VPN kapag itinapat ang camera nito sa screen.
Karaniwang WireGuard ang mismong tunnel. Ang kernel module ang naglilipat ng mga packet, kaya pareho ang throughput nito sa setup na manu-manong isinulat. Ang napapala mo ay client lifecycle: pagdaragdag, pag-disable, at pagtanggal ng mga peer nang hindi nag-e-edit ng config file sa SSH. Ang isinusuko mo naman ay direktang kontrol sa config na iyon. Ito ang paksa ng manu-manong WireGuard setup sa isang VPS.
Kailangan mo ng KVM VPS na may public IPv4 address, Docker Engine na may Compose plugin, at root access. Ang container virtualization na nakikibahagi sa kernel ng host, gaya ng OpenVZ o LXC, ay karaniwang hindi makakapag-load ng WireGuard module. Dahil dito, mabibigo ang container na i-enable ang interface.
Inilipat ng Version 15 ang mga setting mula sa environment
Karamihan sa mga guide na makikita mo ay isinulat para sa wg-easy 14, kung saan itinatakda mo ang WG_HOST sa address ng server at ang PASSWORD_HASH sa bcrypt hash ng admin password, na parehong environment variable. Rewrite ang Version 15. Malinaw na sinasabi ng official migration notes na hindi ginagamit ng v15 ang parehong environment variable ng v14 at inilipat ang karamihan sa mga ito sa admin panel ng web UI.
Kaya wala nang ginagawa ang WG_HOST at PASSWORD_HASH. Kapag kinopya mo ang lumang compose file, magsisimula ang container, hindi papansinin ang mga linyang iyon, at hihilingin sa iyong gumawa ng admin account sa browser. Hindi ito bug. Ito ang bagong setup flow.
Noong July 2026, ang dapat i-pin na major tag ay 15. I-pin ang major version sa halip na gamitin ang latest, dahil binabago ng major upgrade ang on-disk config format at hindi ito maayos na makakapag-rollback.
Ang compose file
Gumawa ng directory para sa stack at ilagay dito ang opisyal na compose file. Ito ang upstream file at hindi ito binago.
sudo mkdir -p /etc/docker/containers/wg-easy
sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml \
https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.ymlGanito ang hitsura ng mga nilalaman:
volumes:
etc_wireguard:
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
networks:
wg:
ipv4_address: 10.42.42.42
ipv6_address: fdcc:ad94:bacf:61a3::2a
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
- net.ipv6.conf.default.forwarding=1
networks:
wg:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 10.42.42.0/24
- subnet: fdcc:ad94:bacf:61a3::/64Ang etc_wireguard ay isang named volume na naglalaman ng server key at ng bawat client na gagawin mo. I-back up ang volume na ito. Kung hindi, mabubura ang lahat ng peer kapag nag-rebuild. Kung mas gusto mong makita ang mga file na ito sa filesystem ng host, palitan ito ng bind mount. Basahin muna ang pagkakaiba ng bind mount at named volume bago ito gawin, dahil magkaiba ang pag-uugali ng permissions.
Bakit kailangan ang NET_ADMIN, SYS_MODULE at mga sysctl
Hindi pinapayagan ang container na baguhin ang network stack bilang default, at inaalis ng bawat linyang ito ang isang partikular na hadlang.
Pinapayagan ng NET_ADMIN ang container na likhain ang interface na wg0, lagyan ito ng address, at magsulat ng mga route. Kung wala ito, nagsisimula ang container ngunit tumitigil habang inaakyat ang interface, dahil nagbabalik ang ip link add wg0 type wireguard ng Operation not permitted.
Pinahihintulutan ng SYS_MODULE, kasama ang read-only na mount na /lib/modules, ang container na i-load ang WireGuard kernel module kung hindi pa ito na-load ng host. Nasa host kernel ang module, hindi sa loob ng image. Kaya kailangang makita ng container ang directory ng host. Sa modern kernel, karaniwang built in na ang module. Makukumpirma mo ito gamit ang sudo modprobe wireguard && echo ok sa host.
Pinapagana ng net.ipv4.ip_forward=1 ang kernel na mag-forward ng mga packet na hindi nakalaan mismo sa machine. Kung wala ito, kumokonekta ang client at nagtatagumpay ang handshake, ngunit dini-drop ang bawat packet papunta sa internet. Dahil dito, nagti-timeout ang ping 1.1.1.1 kahit mukhang connected ang VPN.
Ang net.ipv4.conf.all.src_valid_mark=1 ang madalas nakakagulat sa mga user. Mina-mark ng WireGuard ang sarili nitong outgoing packet para hindi ito ma-route pabalik sa tunnel. Nakikita ng strict reverse path filtering ang packet na may source address na hindi tumutugma sa inaasahang route, kaya dini-drop ito. Sinasabi ng sysctl na ito sa kernel na tanggapin ang mga marked packet. Ito ang pumipigil sa full tunnel na masira ang sarili nitong routing.
Simulan ito at gawin ang admin account
cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -fGamitin ang docker compose up at docker compose down, hindi ang start at stop. Nagbabala ang upstream na ang start sa container na ginawa gamit ang ibang settings ay nag-iiwan sa network sa hindi pare-parehong estado. Kung gusto mong awtomatikong bumalik ang stack pagkatapos ng reboot, sapat na ang restart: unless-stopped. Ipinaliliwanag ng boot behavior ng mga Compose service kung ano ang ipinapangako at hindi ipinapangako ng policy na iyon.
Nakikinig ang web UI sa TCP 51821. Sa unang pag-access, may setup page kung saan gagawin mo ang admin account at kukumpirmahin ang host address na gagamitin ng mga client para maabot ang server. Mapupunta ang host address na iyon sa linyang Endpoint ng bawat client config, kaya dapat itong public IP o DNS name ng VPS. Kung mali ito, ang QR code na ibibigay mo sa telepono ay tuturo sa address na hindi maaabot, at hindi makukumpleto ang handshake.
May isa pang mahalagang punto tungkol sa port na iyon: tumatanggi ang wg-easy 15 sa plain HTTP maliban kung itakda mo ang INSECURE=true. Ayos lang ang pag-access dito gamit ang HTTPS na may untrusted certificate, o ang pag-terminate ng TLS sa reverse proxy na nasa harap nito. Hindi ligtas ang pag-access gamit ang http:// kapag default settings ang ginagamit.
Huwag i-publish sa internet ang UI port
Ini-publish ng compose file ang 51821 sa lahat ng interface. Login page ito para sa isang box na maaaring mag-route ng iyong traffic, kaya hindi ito dapat bukas sa lahat. Kapag nag-publish ng port sa Docker, nagsusulat ito ng mga rule sa DOCKER chain, na sinusuri bago ang ufw. Dahil dito, hindi ito isinasara ng ufw deny rule. Mahalagang maunawaan ang trap na ito nang hiwalay, at ipinaliliwanag ito nang buo ng kung bakit binabalewala ng Docker published ports ang ufw.
Ang simpleng solusyon ay i-bind ang UI sa loopback at i-access ito sa pamamagitan ng SSH tunnel:
ports:
- "51820:51820/udp"
- "127.0.0.1:51821:51821/tcp"
environment:
- INSECURE=truePagkatapos, mula sa iyong laptop:
ssh -L 51821:127.0.0.1:51821 youruser@your.server.addressBuksan ang http://127.0.0.1:51821 sa browser ng iyong laptop. Ine-encrypt ng SSH ang traffic, walang ibang makaka-access sa port, at ligtas gamitin dito ang INSECURE=true dahil hindi lumalabas sa loopback interface ang plain HTTP hop.
Buksan ang UDP 51820 at suriin ang parehong firewall
Kailangang reachable mula sa internet ang UDP 51820 para sa WireGuard mismo. Ipinapublish ito ng Docker, pero maraming provider ang naglalagay ng hiwalay na network firewall sa harap ng VPS na walang nalalaman tungkol sa Docker. Buksan ang port sa parehong lugar. Kung pinamamahalaan mo ang host firewall gamit ang ufw, ang pangunahing ufw rules para sa VPS ang mas maikling paraan kaysa manu-manong pagsulat ng nftables.
Suriin kung aktuwal na nakikinig ang container:
sudo ss -ulnp | grep 51820Dapat kang makakita ng nakikinig na UDP socket. Kung walang lumabas sa linyang iyon, hindi naitaas ng container ang interface, at ilalahad ng sudo docker compose logs wg-easy ang dahilan.
Gumawa ng client at i-scan ito gamit ang phone
Sa UI, gumawa ng client at bigyan ito ng pangalang makikilala mo sa susunod, gaya ng device na pag-aari nito. Awtomatikong inilalaan ng wg-easy ang susunod na bakanteng tunnel address at ginagawa ang key pair para sa iyo. May QR code at nada-download na .conf file ang bawat client row.
I-install ang opisyal na WireGuard app sa phone, piliing magdagdag ng tunnel mula sa QR code, at itutok ang camera sa code sa screen. Lalabas ang tunnel gamit ang pangalang inilagay mo. I-on ito, at magsisimulang magpakita ang client row sa UI ng transfer counters at oras ng pinakahuling handshake. Kapag nakakonekta na ang phone sa tunnel, maa-access nito ang mga serbisyong hindi mo kailanman inilabas sa internet. Sa ganitong paraan, patuloy na makakapag-upload ang phone sa self-hosted photo server mula saanman nang hindi kailangang magbukas ang server na iyon ng kahit isang port sa publiko. Magagamit din ang parehong paraan para sa media, at kaaya-ayang i-browse mula sa hotel room ang Jellyfin library na ginawang parang 90s video store, habang nananatili itong pribado gaya noong nasa LAN mo. Gumagana rin sa kabilang direksiyon ang mga alert sa parehong tunnel, dahil maaaring magpadala ang self-hosted ntfy server ng mensahe sa phone sa mismong sandaling mabigo ang backup job, nang hindi kailanman sumasagot sa request mula sa public internet.
Ang client na walang handshake matapos itong i-enable ay hindi talaga nakakarating sa server. Ipinapahiwatig nito ang problema sa UDP 51820, maaaring nasa provider firewall o nasa endpoint address na naka-embed sa config. Kung may handshake ang client pero walang gumaganang internet, malamang forwarding o DNS ang problema.
Sa desktop, i-download ang .conf file at i-import ito sa WireGuard client sa halip na i-type muli ang laman. Isang beses lang ginagawa at ipinapakita ang private key sa file na iyon. Ituring ang file na parang SSH private key.
Kailan hindi na sapat ang UI
Ang wg-easy ang tamang tool habang ang mga peer mo ay mga tao at phone. Mas mabilis ang UI kaysa sa pag-edit ng mga configuration file, at isang click lang ang kailangan para i-revoke ang nawalang phone.
Maaabot mo ang mga limitasyon nito kapag kailangan mo ng bagay na hindi nito kayang i-model sa UI. Karaniwang unang hadlang ang site-to-site routing, kung saan sumasaklaw ang AllowedIPs ng isang peer sa buong remote subnet sa halip na sa iisang address. Kasunod nito ang split tunnel na may per-peer routing rules, o configuration na binuo ng provisioning tool mo. Sa puntong iyon, hindi mas mahirap ang manual na setup; iba lang ang paraan nito, at ipinapakita ng plain WireGuard guide ang parehong tunnel na binuo mula sa wg0.conf. Kung mas gusto mong tuluyang ihinto ang pagpapatakbo ng control plane, ipinapaliwanag ng WireGuard kumpara sa Tailscale ang managed na opsyon. Nakadepende ang pagiging sulit nito sa aktuwal na maaabot ng coordination server, at mahalagang basahin ang trust model ng Tailscale bago mo ipagamit dito ang network mo. Karaniwang kasunod na tanong ang gastos, at sapat na ang aktuwal na saklaw ng libreng plan ng Tailscale para walang bayaran ang isang household o maliit na team. Paglampas doon, users ang binibilang sa billing sa halip na devices. Iba ang ganitong billing sa VPS na binabayaran mo na, kaya ang gastos ng Tailscale kapag lumampas ka sa libreng plan ang dapat mong tingnan bago mag-migrate ng team. May direktang katumbas doon ang buong tunnel na kakabuo mo lang, dahil ang pag-aanunsyo sa VPS bilang Tailscale exit node ay nagbibigay ng parehong route palabas sa pamamagitan ng server. Inaprubahan ito sa admin console sa halip na isulat sa configuration ng bawat client. May katumbas din ang subnet na hadlang, dahil ang pag-aanunsyo ng buong private network mula sa VPS ay nagbibigay ng access sa network na iyon sa bawat device sa tailnet nang hindi kailangang i-edit ang per-peer AllowedIPs na nagtulak sa iyo palayo sa UI. Kung gusto mo ang dashboard at automatic mesh routing pero ayaw mong gumamit ng coordination server ng ibang tao, pinananatili ng pagpapatakbo ng sarili mong NetBird server sa VPS ang control plane sa hardware na pagmamay-ari mo. Kapalit nito ang DNS at TLS setup na hindi kailanman hiningi ng wg-easy.
Kung ang compose syntax sa itaas ang hindi pamilyar na bahagi, sa halip na ang WireGuard, ipinapaliwanag ng mga batayan ng Docker Compose sa VPS ang file format at mga karaniwang command.
FAQ
Bakit binabalewala ng wg-easy ang aking WG_HOST at PASSWORD_HASH?
Ang mga variable na iyon ay para sa wg-easy 14. Rewrite ang Version 15, at inilipat ng upstream ang halos lahat ng configuration sa admin panel ng web UI. Hindi binabasa ng container ang alinman sa dalawang variable, kaya normal itong nagsisimula at pagkatapos ay hinihiling sa iyo na gumawa ng admin account sa unang pagbisita. Itakda na lang ang host address na ginagamit ng client sa setup page na iyon.
Kailangan ko ba ang SYS_MODULE kung mayroon nang WireGuard ang kernel ko?
Hindi. Umiiral ang SYS_MODULE at ang /lib/modules mount para ma-load ng container ang module kapag wala ito sa host. Sa host na matagumpay nang gumagana ang sudo modprobe wireguard, hindi nagagamit ang capability. Makatuwirang hakbang sa hardening ang pag-alis nito, at kailangan pa rin ang NET_ADMIN.
Kumokonekta ang client pero walang internet. Ano ang mali?
Ang handshake na walang traffic ay halos palaging nangangahulugan ng problema sa forwarding. Tiyaking nasa compose file pa rin ang net.ipv4.ip_forward=1 at net.ipv4.conf.all.src_valid_mark=1, dahil madalas nawawala ang mga ito sa kinopyang mano-manong in-edit. Kung naka-enable ang forwarding, suriin ang DNS server na natanggap ng client. Ang tunnel na nagpapadaan ng lahat ng traffic sa VPN pero gumagamit ng DNS server na hindi na nito maabot ay mukhang patay na connection sa browser.
Paano ko iba-back up ang mga client ko?
Nasa etc_wireguard named volume ang lahat, sa isang wg0.json file. May backup button din ang UI na nag-e-export ng parehong data. Kopyahin ang file na iyon sa labas ng server bago ang anumang upgrade. Ang pag-restore ay ginagawa sa pamamagitan ng pag-upload sa setup step ng bagong container.
Maaari ko bang patakbuhin ang wg-easy sa likod ng reverse proxy?
Oo. Ilagay ang proxy sa harap ng TCP 51821, tapusin doon ang TLS, at itakda ang INSECURE=true sa container upang tanggapin nito ang plain HTTP hop mula sa proxy. Direktang i-publish ang UDP 51820, dahil UDP ang VPN traffic at hindi ito dumaraan sa HTTP proxy.