SSD Nodes Learn 8GB RAM — $66/rok
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-01

wg-easy w Dockerze: WireGuard z panelem webowym

Konfiguracja wg-easy z Docker Compose: porty, NET_ADMIN, wymagane sysctls i kody QR. Uwzględniono zmianę w wersji 15, która przeniosła ustawienia poza zmienne środowiskowe.

Co jest budowane

wg-easy to WireGuard z interfejsem webowym, uruchamiany jako jeden kontener Docker. Zarządza interfejsem WireGuard i udostępnia przeglądarkowy interfejs do tworzenia klientów. Każdy utworzony klient otrzymuje plik konfiguracyjny i kod QR. Dzięki temu telefon dołącza do VPN po skierowaniu aparatu na ekran.

Sam tunel jest standardowym tunelem WireGuard. Moduł jądra obsługuje przesyłanie pakietów, dlatego przepustowość jest taka sama jak w konfiguracji utworzonej ręcznie. Zyskuje się możliwość zarządzania cyklem życia klientów: dodawania, wyłączania i usuwania peerów bez edytowania pliku konfiguracyjnego przez SSH. Traci się natomiast bezpośrednią kontrolę nad tym plikiem. Ten temat opisano w sekcji ręczna konfiguracja WireGuard na VPS.

Wymagany jest VPS KVM z publicznym adresem IPv4, Docker Engine z wtyczką Compose oraz dostęp root. Wirtualizacja kontenerowa współdzieląca jądro hosta, taka jak OpenVZ lub LXC, zwykle nie może załadować modułu WireGuard. W takiej sytuacji kontener nie uruchomi interfejsu.

Wersja 15 przeniosła ustawienia poza zmienne środowiskowe

Większość dostępnych instrukcji została napisana dla wg-easy 14. W tej wersji WG_HOST ustawiano jako adres serwera, a PASSWORD_HASH jako skrót bcrypt hasła administratora. Obie wartości były ustawiane jako zmienne środowiskowe. Wersja 15 została napisana od nowa. Oficjalne informacje dotyczące migracji jasno wskazują, że v15 nie używa tych samych zmiennych środowiskowych co v14 oraz że większość ustawień przeniesiono do panelu administracyjnego w interfejsie webowym.

Dlatego WG_HOST i PASSWORD_HASH nie mają już żadnego działania. Po skopiowaniu starego pliku compose kontener uruchamia się, ignoruje te wiersze, a następnie żąda utworzenia konta administratora w przeglądarce. Nie jest to błąd. Jest to nowy przebieg konfiguracji.

W lipcu 2026 głównym tagiem do przypięcia jest 15. Należy przypiąć wersję główną zamiast używać latest, ponieważ aktualizacja do nowej wersji głównej zmienia format konfiguracji na dysku i nie umożliwia poprawnego wycofania zmian.

Plik compose

Utwórz katalog dla tego stosu i zapisz w nim oficjalny plik compose. Jest to plik źródłowy, bez zmian.

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.yml

Zawartość wygląda następująco:

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::/64

etc_wireguard to nazwany wolumin przechowujący klucz serwera oraz wszystkich utworzonych klientów. Należy wykonać kopię zapasową tego woluminu. W przeciwnym razie przebudowa usunie wszystkich klientów. Jeśli pliki mają być widoczne w systemie plików hosta, należy zastąpić go montowaniem bind mount. Przed wykonaniem tej zmiany należy zapoznać się z różnicą między montowaniami bind mount a nazwanymi woluminami, ponieważ uprawnienia są obsługiwane inaczej.

Dlaczego wymagane są NET_ADMIN, SYS_MODULE i ustawienia sysctl

Kontener domyślnie nie może modyfikować stosu sieciowego. Każdy z tych wierszy usuwa jedną konkretną blokadę.

NET_ADMIN umożliwia kontenerowi utworzenie interfejsu wg0, przypisanie mu adresu oraz dodanie tras. Bez tego kontener uruchamia się, a następnie kończy działanie podczas podnoszenia interfejsu, ponieważ ip link add wg0 type wireguard zwraca Operation not permitted.

SYS_MODULE wraz z montowaniem /lib/modules w trybie tylko do odczytu umożliwia kontenerowi załadowanie modułu jądra WireGuard, jeśli host nie załadował go wcześniej. Moduł znajduje się w jądrze hosta, a nie wewnątrz obrazu. Z tego powodu katalog hosta musi być widoczny. W nowoczesnym jądrze moduł jest zwykle wbudowany. Można to potwierdzić za pomocą sudo modprobe wireguard && echo ok na hoście.

net.ipv4.ip_forward=1 powoduje, że jądro przekazuje pakiety, które nie są adresowane do samego hosta. Bez tego klient nawiązuje połączenie, uzgadnianie przebiega pomyślnie, a następnie każdy pakiet kierowany do internetu zostaje odrzucony. W efekcie ping 1.1.1.1 kończy się przekroczeniem limitu czasu, mimo że VPN wygląda na połączony.

net.ipv4.conf.all.src_valid_mark=1 często powoduje problemy. WireGuard oznacza własne pakiety wychodzące, aby nie były ponownie kierowane do tunelu. Ścisłe filtrowanie odwrotnej ścieżki wykrywa pakiet, którego adres źródłowy nie odpowiada oczekiwanej trasie, i odrzuca go. To ustawienie sysctl nakazuje jądru akceptować oznaczone pakiety. Dzięki temu pełny tunel nie przerywa własnego działania.

Uruchomienie i utworzenie konta administratora

cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -f

Należy użyć docker compose up i docker compose down, a nie start i stop. Projekt upstream ostrzega, że użycie start w kontenerze utworzonym przy użyciu innych ustawień pozostawia sieć w niespójnym stanie. Jeśli stos ma być automatycznie uruchamiany po ponownym uruchomieniu systemu, restart: unless-stopped już to zapewnia. Sposób uruchamiania usług compose wyjaśnia, co obejmuje ta zasada, a czego nie gwarantuje.

Interfejs WWW nasłuchuje na porcie TCP 51821. Przy pierwszym otwarciu wyświetlana jest strona konfiguracji, na której tworzy się konto administratora i potwierdza adres hosta używany przez klientów do łączenia się z serwerem. Ten adres hosta trafia do wiersza Endpoint w konfiguracji każdego klienta, dlatego musi być publicznym adresem IP lub nazwą DNS serwera VPS. Jeśli jest nieprawidłowy, kod QR przekazany do telefonu wskazuje nieosiągalny adres i uzgadnianie połączenia nigdy się nie kończy.

Należy uwzględnić jeszcze jedną kwestię dotyczącą tego portu: wg-easy 15 odrzuca zwykłe połączenia HTTP, chyba że zostanie ustawione INSECURE=true. Można łączyć się z nim przez HTTPS z niezaufanym certyfikatem albo zakończyć TLS na odwrotnym serwerze proxy umieszczonym przed nim. Przy domyślnych ustawieniach nie można łączyć się przez http://.

Nie publikować portu interfejsu użytkownika w Internecie

Plik compose publikuje port 51821 na każdym interfejsie. Jest to strona logowania do serwera, który może przekierowywać ruch sieciowy, dlatego port nie powinien być dostępny z Internetu. Publikowanie portu w Dockerze dodaje reguły do łańcucha DOCKER, który jest przetwarzany przed ufw. Z tego powodu reguła odmowy ufw nie zamyka tego portu. Warto zrozumieć tę pułapkę niezależnie. Szczegółowo opisano ją w artykule dlaczego opublikowane porty Dockera ignorują ufw.

Prostym rozwiązaniem jest powiązanie interfejsu użytkownika z loopback oraz dostęp przez tunel SSH:

    ports:
      - "51820:51820/udp"
      - "127.0.0.1:51821:51821/tcp"
    environment:
      - INSECURE=true

Następnie na komputerze przenośnym:

ssh -L 51821:127.0.0.1:51821 youruser@your.server.address

Otworzyć http://127.0.0.1:51821 w przeglądarce na komputerze przenośnym. Ruch jest szyfrowany przez SSH, a port nie odpowiada żadnemu innemu hostowi. W tym przypadku INSECURE=true jest bezpieczne, ponieważ połączenie HTTP w postaci jawnej nie opuszcza interfejsu loopback.

Otwórz UDP 51820 i sprawdź obie zapory

WireGuard wymaga, aby port UDP 51820 był dostępny z Internetu. Docker publikuje ten port, ale wielu dostawców umieszcza przed VPS oddzielną zaporę sieciową, o której Docker nic nie wie. Otwórz port w obu miejscach. Jeśli zapora hosta jest zarządzana za pomocą ufw, podstawowe reguły ufw dla VPS są prostszym rozwiązaniem niż ręczne tworzenie reguł nftables.

Sprawdź, czy kontener rzeczywiście nasłuchuje:

sudo ss -ulnp | grep 51820

Powinno zostać wyświetlone gniazdo nasłuchujące UDP. Brak wyniku w tym wierszu oznacza, że kontener nie uruchomił interfejsu, a sudo docker compose logs wg-easy wskaże przyczynę.

Utwórz klienta i zeskanuj kod na telefonie

W interfejsie utwórz klienta i nadaj mu nazwę, którą można później rozpoznać, na przykład nazwę urządzenia, do którego należy. wg-easy przydziela kolejny wolny adres tunelu i generuje parę kluczy. W każdym wierszu klienta dostępny jest kod QR oraz plik .conf do pobrania.

Zainstaluj na telefonie oficjalną aplikację WireGuard. Wybierz dodanie tunelu za pomocą kodu QR i skieruj aparat na kod wyświetlany na ekranie. Tunel pojawi się pod wprowadzoną nazwą. Włącz go. Wiersz klienta w interfejsie zacznie wyświetlać liczniki transferu oraz czas ostatniego uzgodnienia.

Klient, przy którym po włączeniu nie jest wyświetlane żadne uzgodnienie, w ogóle nie łączy się z serwerem. Przyczyną jest UDP 51820, zapora u dostawcy albo adres endpointu zapisany w konfiguracji. Klient, przy którym jest wyświetlane uzgodnienie, ale internet nie działa, wskazuje raczej na problem z przekazywaniem pakietów lub DNS.

Na komputerze pobierz plik .conf i zaimportuj go do klienta WireGuard zamiast przepisywać jego zawartość. Klucz prywatny w tym pliku jest generowany i wyświetlany tylko raz. Chroń ten plik tak samo jak klucz prywatny SSH.

Kiedy przekroczyć możliwości interfejsu

wg-easy jest właściwym narzędziem, dopóki użytkownikami są osoby korzystające z telefonów. Interfejs jest szybszy niż edytowanie plików konfiguracyjnych, a unieważnienie konfiguracji utraconego telefonu wymaga jednego kliknięcia.

Ograniczenia pojawiają się, gdy potrzebna jest funkcja, której interfejs nie obsługuje. Routing site-to-site, w którym AllowedIPs peera obejmuje całą zdalną podsieć zamiast pojedynczego adresu, jest zazwyczaj pierwszą taką granicą. Kolejne przypadki to tunele split tunnel z regułami routingu dla poszczególnych peerów albo konfiguracja generowana przez narzędzie provisioningowe. Wtedy konfiguracja tworzona ręcznie nie jest trudniejsza, lecz po prostu inna, a zwykły przewodnik po WireGuard pokazuje utworzenie tego samego tunelu z użyciem wg0.conf. Jeśli preferowane jest całkowite zaprzestanie uruchamiania control plane, WireGuard a Tailscale omawia opcję zarządzaną.

Jeśli nieznana była składnia compose, a nie zagadnienia związane z WireGuard, Podstawy Docker Compose na VPS wyjaśniają format pliku i codziennie używane polecenia.

FAQ

Dlaczego wg-easy ignoruje moje zmienne WG_HOST i PASSWORD_HASH?

Te zmienne należą do wg-easy 14. Wersja 15 została napisana od nowa, a projekt nadrzędny przeniósł prawie całą konfigurację do panelu administracyjnego w interfejsie internetowym. Kontener nie odczytuje żadnej z tych zmiennych. Uruchamia się normalnie, a następnie podczas pierwszej wizyty prosi o utworzenie konta administratora. Adres hosta widoczny dla klientów należy ustawić na tej stronie konfiguracji.

Czy SYS_MODULE jest potrzebne, jeśli jądro ma już WireGuard?

Nie. SYS_MODULE i punkt montowania /lib/modules umożliwiają kontenerowi załadowanie modułu, gdy host go nie ma. Na hoście, na którym sudo modprobe wireguard już działa, ta możliwość nie jest wykorzystywana. Usunięcie tego elementu jest uzasadnionym działaniem wzmacniającym bezpieczeństwo. NET_ADMIN jest nadal wymagane.

Klient łączy się, ale nie ma dostępu do Internetu. Co jest nieprawidłowe?

Ustanowiony handshake bez ruchu sieciowego prawie zawsze oznacza problem z przekazywaniem pakietów. Należy sprawdzić, czy net.ipv4.ip_forward=1 i net.ipv4.conf.all.src_valid_mark=1 nadal znajdują się w pliku compose, ponieważ w ręcznie edytowanej kopii często ich brakuje. Jeśli przekazywanie pakietów jest włączone, należy sprawdzić serwer DNS otrzymany przez klienta. Tunel, który kieruje cały ruch przez VPN, ale wskazuje serwer DNS, do którego klient nie może już dotrzeć, w przeglądarce wygląda dokładnie jak nieaktywne połączenie.

Jak utworzyć kopię zapasową klientów?

Wszystkie dane znajdują się w nazwanym wolumenie etc_wireguard, w pliku wg0.json. Interfejs użytkownika ma również przycisk tworzenia kopii zapasowej, który eksportuje te same dane. Przed każdą aktualizacją należy skopiować ten plik poza serwer. Przywracanie polega na przesłaniu pliku podczas etapu konfiguracji nowego kontenera.

Czy można uruchomić wg-easy za odwrotnym proxy?

Tak. Proxy należy umieścić przed TCP 51821, zakończyć TLS na proxy i ustawić INSECURE=true w kontenerze, aby akceptował zwykły hop HTTP z proxy. UDP 51820 należy nadal publikować bezpośrednio, ponieważ ruch VPN korzysta z UDP i nie przechodzi przez proxy HTTP.