wg-easy: WireGuard dengan UI Web di Docker
Panduan menjalankan WireGuard dengan UI wg-easy di Docker Compose, termasuk port, NET_ADMIN, sysctl penting, serta onboarding ponsel melalui kode QR.
Yang Anda bangun
wg-easy adalah WireGuard dengan antarmuka web yang berjalan sebagai satu container Docker. Aplikasi ini mengelola antarmuka WireGuard dan menyediakan UI browser untuk membuat client. Setiap client yang Anda buat memperoleh file konfigurasi dan kode QR. Dengan demikian, ponsel dapat bergabung ke VPN dengan mengarahkan kameranya ke layar.
Tunnel tersebut tetap menggunakan WireGuard biasa. Modul kernel memindahkan paket, sehingga throughput-nya sama seperti konfigurasi yang ditulis secara manual. Keuntungannya adalah pengelolaan siklus hidup client: Anda dapat menambahkan, menonaktifkan, dan menghapus peer tanpa mengedit file konfigurasi melalui SSH. Kekurangannya adalah Anda tidak mengendalikan konfigurasi tersebut secara langsung. Hal ini dibahas dalam penyiapan WireGuard manual pada VPS.
Anda memerlukan host Linux yang dapat dikelola, seperti KVM VPS, dengan alamat IP publik atau nama domain yang mengarah ke host tersebut, CPU x86_64 atau arm64, Docker Engine dengan plugin Compose, dan akses root. Virtualisasi container yang menggunakan kernel host secara bersama, seperti OpenVZ atau LXC, biasanya tidak dapat memuat modul WireGuard. Akibatnya, container gagal mengaktifkan antarmuka tersebut.
Version 15 memindahkan pengaturan dari environment
Sebagian besar panduan yang Anda temukan ditulis untuk wg-easy 14. Pada versi tersebut, Anda menetapkan WG_HOST ke alamat server dan PASSWORD_HASH ke hash bcrypt kata sandi admin, keduanya sebagai variabel environment. Version 15 merupakan penulisan ulang. Catatan migrasi resmi menyatakan dengan jelas bahwa v15 tidak menggunakan variabel environment yang sama seperti v14, dan sebagian besar pengaturan tersebut dipindahkan ke panel admin pada UI web.
Jadi, WG_HOST dan PASSWORD_HASH tidak lagi berfungsi. Jika Anda menyalin file compose lama, container akan berjalan, mengabaikan baris tersebut, lalu meminta Anda membuat akun admin melalui browser. Ini bukan bug. Itulah alur penyiapan yang baru.
Per Oktober 2026, tag mayor yang perlu dipin adalah 15. Gunakan versi mayor secara spesifik, bukan latest, karena upgrade mayor mengubah format konfigurasi pada disk dan tidak dapat di-rollback dengan bersih. Peralihan ke 15 mengganti file JSON milik v14 dengan database SQLite.
File compose
Buat direktori untuk stack tersebut, lalu tulis file compose resmi ke dalamnya. Ini adalah file dari upstream, dengan baris opsional yang dikomentari tidak disertakan.
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.ymlIsinya terlihat seperti berikut:
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::/64etc_wireguard adalah volume bernama yang menyimpan kunci server dan setiap client yang Anda buat di dalam database SQLite wg-easy.db. Cadangkan volume tersebut. Jika tidak, proses rebuild akan menghapus semua peer Anda. Jika Anda ingin melihat file tersebut pada filesystem host, ganti volume itu dengan bind mount. Sebelum melakukannya, baca perbedaan antara bind mount dan volume bernama, karena perilaku permission-nya berbeda.
Mengapa memerlukan NET_ADMIN, SYS_MODULE, dan sysctl
Container tidak diizinkan menyentuh network stack secara default. Setiap baris ini menghapus satu pembatas tertentu.
NET_ADMIN memungkinkan container membuat interface wg0, menetapkan alamat, dan menulis route. Tanpanya, container akan start lalu berhenti saat mengaktifkan interface karena ip link add wg0 type wireguard mengembalikan Operation not permitted.
SYS_MODULE bersama mount /lib/modules yang read-only memungkinkan container memuat modul kernel WireGuard jika host belum memuatnya. Modul tersebut berada di kernel host, bukan di dalam image. Karena itu, direktori host harus dapat diakses. Pada kernel modern, modul ini biasanya sudah built-in. Anda dapat memastikannya dengan sudo modprobe wireguard && echo ok pada host.
net.ipv4.ip_forward=1 membuat kernel meneruskan packet yang tidak ditujukan ke host itu sendiri. Tanpanya, client dapat terhubung dan handshake berhasil, tetapi setiap packet ke internet akan di-drop. Akibatnya, ping 1.1.1.1 mengalami timeout meskipun VPN terlihat terhubung.
net.ipv4.conf.all.src_valid_mark=1 adalah bagian yang sering mengejutkan pengguna. WireGuard menandai packet keluar miliknya sendiri agar packet tersebut tidak dirutekan kembali ke tunnel. Reverse path filtering yang ketat melihat packet dengan alamat sumber yang tidak sesuai dengan route yang diharapkan, lalu men-drop packet tersebut. sysctl ini memberi tahu kernel untuk menerima packet yang ditandai. Dengan begitu, full tunnel tidak memutus koneksinya sendiri.
Jalankan dan buat akun admin
cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -fGunakan docker compose up dan docker compose down, bukan start dan stop. start hanya me-restart container yang sudah ada, sehingga perubahan apa pun pada file compose, termasuk pengaturan network dan sysctl, tidak diterapkan, sedangkan up membuat ulang komponen yang berubah. Jika Anda ingin stack kembali berjalan setelah reboot, restart: unless-stopped sudah menanganinya, dan perilaku boot service compose menjelaskan hal-hal yang dijanjikan dan tidak dijanjikan oleh kebijakan tersebut.
Web UI mendengarkan pada TCP 51821. Saat pertama kali dibuka, UI menampilkan halaman setup untuk membuat akun admin dan mengonfirmasi alamat host yang akan digunakan klien untuk mengakses server. Alamat host tersebut dicantumkan pada baris Endpoint di setiap konfigurasi klien, sehingga harus berupa IP publik atau nama DNS VPS. Jika alamatnya salah, QR code yang Anda berikan ke ponsel mengarah ke lokasi yang tidak dapat dijangkau dan handshake tidak pernah selesai.
Ada satu hal lagi tentang port tersebut: wg-easy tidak pernah menyediakan TLS sendiri dan tidak menolak HTTP biasa. Melalui http://, UI tetap dapat dibuka dan menampilkan banner peringatan. Perubahan default hanya menyangkut session cookie, yang memiliki flag Secure. Karena itu, browser yang mengakses UI melalui HTTP biasa pada alamat publik akan membuang cookie tersebut sehingga login tidak bertahan. INSECURE=true menghapus flag itu dan tidak melakukan hal lain. Jika Anda menempatkan reverse proxy di depannya untuk melakukan TLS termination, bahkan dengan sertifikat self-signed, wg-easy tidak perlu diubah sama sekali.
Jangan publikasikan port UI ke Internet
File compose memublikasikan 51821 pada semua interface. Port tersebut menyediakan halaman login untuk server yang dapat merutekan trafik Anda, sehingga tidak boleh terbuka bagi semua orang. Publikasi port di Docker menulis rule ke chain DOCKER, yang dievaluasi sebelum ufw. Karena itu, rule deny pada ufw tidak menutup port tersebut. Perangkap ini perlu dipahami secara khusus, dan alasan port yang dipublikasikan Docker mengabaikan ufw membahasnya secara lengkap.
Solusi sederhananya adalah mengikat UI ke loopback dan mengaksesnya melalui SSH tunnel:
ports:
- "51820:51820/udp"
- "127.0.0.1:51821:51821/tcp"
environment:
- INSECURE=trueKemudian, dari laptop Anda:
ssh -L 51821:127.0.0.1:51821 youruser@your.server.addressBuka http://127.0.0.1:51821 di browser pada laptop Anda. Trafik dienkripsi oleh SSH dan port tersebut tidak menjawab permintaan dari pihak lain. INSECURE=true hanya menghapus flag Secure dari session cookie agar browser mempertahankan login pada alamat HTTP biasa ini. Cara tersebut aman karena hop HTTP biasa tidak pernah meninggalkan interface loopback.
Buka UDP 51820, lalu periksa kedua firewall
WireGuard memerlukan UDP 51820 agar dapat dijangkau dari Internet. Docker memublikasikan port tersebut, tetapi banyak provider menempatkan firewall jaringan terpisah di depan VPS yang tidak diketahui oleh Docker. Buka port tersebut di kedua tempat. Jika Anda mengelola firewall host dengan ufw, aturan dasar ufw untuk VPS adalah cara yang lebih singkat daripada menulis nftables secara manual.
Periksa apakah container benar-benar sedang mendengarkan:
sudo ss -ulnp | grep 51820Anda seharusnya melihat socket UDP yang sedang mendengarkan. Jika tidak ada apa pun pada baris tersebut, container tidak pernah berhasil mengaktifkan interface, dan sudo docker compose logs wg-easy akan menyebutkan penyebabnya.
Buat client dan pindai pada ponsel
Di UI, buat client dan beri nama yang mudah Anda kenali nanti, misalnya nama perangkat yang menggunakannya. wg-easy mengalokasikan alamat tunnel kosong berikutnya dan membuat pasangan kunci untuk Anda. Setiap baris client menyediakan kode QR dan file .conf yang dapat diunduh.
Instal aplikasi WireGuard resmi pada ponsel, pilih opsi untuk menambahkan tunnel dari kode QR, lalu arahkan kamera ke kode pada layar. Tunnel akan muncul dengan nama yang Anda masukkan. Aktifkan tunnel tersebut. Baris client di UI akan mulai menampilkan penghitung transfer dan waktu handshake terbaru. Setelah ponsel terhubung ke tunnel, ponsel dapat mengakses layanan yang tidak pernah Anda publikasikan ke internet. Dengan begitu, ponsel dapat terus mengunggah foto ke server foto yang di-host sendiri dari mana saja tanpa server tersebut membuka satu pun port ke seluruh internet. Cara yang sama dapat digunakan untuk media. Library Jellyfin yang dibuat ulang seperti toko video era 90-an dapat dijelajahi dengan nyaman dari kamar hotel, sementara aksesnya tetap privat seperti di LAN Anda. Notifikasi juga dapat berjalan melalui tunnel yang sama. Server ntfy yang di-host sendiri dapat mengirim pesan ke ponsel tersebut segera setelah tugas backup gagal, tanpa pernah menerima permintaan dari internet publik.
Client yang tidak menampilkan handshake setelah diaktifkan sama sekali tidak dapat menjangkau server. Periksa UDP 51820, baik pada firewall provider maupun alamat endpoint yang tertanam dalam konfigurasi. Client yang menampilkan handshake tetapi tidak memiliki akses internet yang berfungsi biasanya mengalami masalah pada forwarding atau DNS.
Pada desktop, unduh file .conf dan impor file tersebut ke client WireGuard, bukan mengetiknya kembali. Private key dalam file tersebut dibuat satu kali dan hanya ditampilkan satu kali. Perlakukan file tersebut seperti Anda memperlakukan private key SSH.
Kapan harus beralih dari UI
wg-easy tepat digunakan selama peer Anda adalah pengguna dan ponsel. UI lebih cepat daripada mengedit file konfigurasi. Mencabut akses ponsel yang hilang juga cukup dilakukan dengan satu klik.
Anda akan mencapai batasnya ketika memerlukan sesuatu yang tidak dimodelkan oleh UI. Routing site-to-site, ketika AllowedIPs milik peer mencakup seluruh subnet remote, bukan hanya satu alamat, biasanya menjadi batas pertama. Split tunnel dengan aturan routing per peer atau konfigurasi yang dibuat oleh tool provisioning menjadi kebutuhan berikutnya. Pada tahap itu, penyiapan manual tidak lebih sulit. Caranya hanya berbeda. Panduan WireGuard biasa menunjukkan tunnel yang sama dengan menggunakan wg0.conf. Jika Anda lebih memilih untuk tidak menjalankan control plane sama sekali, perbandingan WireGuard dan Tailscale membahas opsi terkelola tersebut. Apakah pertukaran ini sepadan bergantung pada jaringan yang benar-benar dapat dijangkau oleh server koordinasi. Baca model kepercayaan Tailscale sebelum memberikan akses ke jaringan Anda. Biaya biasanya menjadi pertanyaan berikutnya. cakupan paket gratis Tailscale yang sebenarnya adalah hingga enam pengguna tanpa biaya, hanya untuk penggunaan nonkomersial. Dengan demikian, rumah tangga tidak perlu membayar, sedangkan tim bisnis kecil tidak memenuhi syarat, berapa pun sedikitnya jumlah anggotanya. Setelah melewati batas tersebut, penagihan menghitung jumlah pengguna, bukan perangkat. Struktur biayanya berbeda dari VPS yang sudah Anda bayar. Karena itu, periksa biaya Tailscale setelah Anda melewati paket gratis sebelum memigrasikan tim. Tunnel penuh yang baru Anda bangun memiliki padanan langsung di sana. mengiklankan VPS sebagai exit node Tailscale memberi Anda rute keluar yang sama melalui server, tetapi persetujuannya dilakukan di konsol admin, bukan ditulis dalam konfigurasi setiap client. Batas subnet juga memiliki padanan. mengiklankan seluruh jaringan privat dari VPS memberikan jaringan tersebut kepada setiap perangkat di tailnet tanpa perlu mengedit AllowedIPs per peer, yang sebelumnya membuat Anda meninggalkan UI. Jika Anda menginginkan dashboard dan routing mesh otomatis, tetapi tidak ingin menggunakan server koordinasi milik pihak lain, menjalankan server NetBird sendiri pada VPS membuat control plane tetap berada pada hardware milik Anda. Sebagai gantinya, Anda harus menyiapkan DNS dan TLS, yang tidak diperlukan oleh wg-easy.
Jika sintaks compose di atas yang belum Anda kenal, bukan bagian WireGuard-nya, dasar-dasar Docker Compose pada VPS menjelaskan format file dan perintah sehari-hari.
FAQ
Mengapa wg-easy mengabaikan WG_HOST dan PASSWORD_HASH saya?
Variabel tersebut digunakan oleh wg-easy 14. Versi 15 ditulis ulang, dan upstream memindahkan hampir seluruh konfigurasi ke panel admin dalam web UI. Container tidak membaca kedua variabel tersebut, sehingga container berjalan normal lalu meminta Anda membuat akun admin saat pertama kali dibuka. Tetapkan alamat host untuk klien pada halaman setup tersebut.
Apakah saya memerlukan SYS_MODULE jika kernel saya sudah memiliki WireGuard?
Tidak. SYS_MODULE dan mount /lib/modules tersedia agar container dapat memuat module ketika host belum memilikinya. Pada host yang sudah berhasil menjalankan sudo modprobe wireguard, capability tersebut tidak digunakan. Menghapusnya merupakan langkah hardening yang wajar, dan NET_ADMIN tetap diperlukan.
Klien terhubung, tetapi tidak ada Internet. Apa masalahnya?
Handshake tanpa trafik hampir selalu menunjukkan masalah forwarding. Pastikan net.ipv4.ip_forward=1 dan net.ipv4.conf.all.src_valid_mark=1 masih ada di file compose, karena salinan yang diedit secara manual sering kehilangan keduanya. Jika forwarding aktif, periksa DNS server yang diterima klien. Tunnel yang mengirim seluruh trafik melalui VPN tetapi menunjuk ke DNS server yang sudah tidak dapat dijangkaunya akan terlihat seperti koneksi yang mati di browser.
Bagaimana cara mencadangkan klien saya?
Semua data berada di named volume etc_wireguard. Pada versi 15, data tersebut berupa database SQLite di /etc/wireguard/wg-easy.db dalam container. Dokumentasi v15 tidak menjelaskan fitur backup atau export, jadi cadangkan volume itu sendiri: jalankan docker compose down, salin isi volume ke lokasi di luar server, lalu jalankan kembali stack dengan docker compose up -d. Lakukan ini sebelum upgrade apa pun. File wg0.json yang disebut dalam panduan lama merupakan milik wg-easy 14. File tersebut adalah hasil export dari tombol backup v14, dan pada v15 hanya digunakan sebagai file yang diunggah dalam setup wizard saat memigrasikan instalasi lama.
Apakah wg-easy dapat dijalankan di belakang reverse proxy?
Ya. Tempatkan proxy di depan port web UI, lakukan TLS termination di sana, dan biarkan INSECURE tidak disetel. Container menerima hop HTTP biasa dari proxy tanpa perubahan, dan contoh resmi untuk Caddy serta Traefik sama sekali tidak menetapkan variabel INSECURE. Karena browser berkomunikasi melalui HTTPS dengan proxy, session cookie harus tetap memiliki flag Secure. Itulah satu-satunya hal yang dihapus oleh INSECURE=true. Tetap publikasikan UDP 51820 secara langsung karena trafik VPN menggunakan UDP dan tidak melewati HTTP proxy.