SSD Nodes Learn 8GB RAM — $66/рік
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-01

wg-easy: WireGuard у Docker із вебінтерфейсом

Налаштуйте wg-easy у Docker Compose: порти, NET_ADMIN, sysctls для WireGuard, відмінності версії 15 і QR-коди для підключення телефонів.

Що ви налаштовуєте

wg-easy — це WireGuard із вебінтерфейсом, який працює в одному контейнері Docker. Він керує інтерфейсом WireGuard і додає вебінтерфейс для створення клієнтів. Для кожного створеного клієнта він генерує файл конфігурації та QR-код. Щоб телефон підключився до VPN, достатньо навести камеру на екран.

Сам тунель працює на основі звичайного WireGuard. Пакети передає модуль ядра, тому пропускна здатність така сама, як у конфігурації, створеної вручну. Ви отримуєте керування життєвим циклом клієнтів: можете додавати, вимикати та видаляти вузли без редагування файлу конфігурації через SSH. Натомість ви втрачаєте прямий контроль над цим файлом. Це питання розглянуто в розділі налаштування WireGuard вручну на VPS.

Вам потрібен KVM VPS із публічною IPv4-адресою, Docker Engine із Compose plugin і доступом root. Контейнерна віртуалізація зі спільним ядром хоста, наприклад OpenVZ або LXC, зазвичай не може завантажити модуль WireGuard. Через це контейнер не зможе підняти інтерфейс.

Версія 15 перенесла налаштування з середовища

Більшість посібників, які ви знайдете, написано для wg-easy 14. У них WG_HOST задавали адресу сервера, а PASSWORD_HASH — bcrypt-хеш пароля адміністратора. Обидва значення передавали як змінні середовища. Версія 15 є переписаною версією. В офіційних примітках щодо міграції прямо зазначено, що v15 не використовує ті самі змінні середовища, що й v14, а більшість із них перенесено до панелі адміністратора у вебінтерфейсі.

Тому WG_HOST і PASSWORD_HASH більше нічого не роблять. Якщо скопіювати старий compose-файл, контейнер запуститься, проігнорує ці рядки, а потім запропонує створити обліковий запис адміністратора у браузері. Це не помилка. Це новий процес початкового налаштування.

Станом на July 2026 для фіксації слід використовувати основний тег 15. Фіксуйте основну версію, а не використовуйте latest, оскільки оновлення основної версії змінює формат конфігурації на диску й не дає змоги коректно виконати відкат.

Файл compose

Створіть каталог для стеку та запишіть у нього офіційний файл compose. Це файл upstream без змін.

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

Вміст має такий вигляд:

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 — іменований том, у якому зберігаються ключ сервера та всі створені вами клієнти. Створіть резервну копію цього тому, інакше повторне розгортання видалить усі ваші однорангові вузли. Якщо ви хочете бачити ці файли у файловій системі хоста, замініть його на bind mount і перед цим прочитайте про різницю між bind mount та іменованими томами, оскільки права доступу працюють по-різному.

Чому потрібні NET_ADMIN, SYS_MODULE і sysctl-параметри

Контейнер за замовчуванням не може змінювати мережевий стек, і кожен із цих рядків усуває окреме обмеження.

NET_ADMIN дає контейнеру змогу створити інтерфейс wg0, призначити йому адресу та додати маршрути. Без цього контейнер запускається, а потім завершує роботу під час налаштування інтерфейсу, оскільки ip link add wg0 type wireguard повертає Operation not permitted.

SYS_MODULE разом із монтуванням /lib/modules у режимі лише для читання дає контейнеру змогу завантажити модуль ядра WireGuard, якщо його ще не завантажено на хості. Модуль працює в ядрі хоста, а не всередині образу, тому каталог хоста має бути доступний у контейнері. У сучасному ядрі модуль зазвичай вбудований. Це можна перевірити за допомогою sudo modprobe wireguard && echo ok на хості.

net.ipv4.ip_forward=1 вказує ядру пересилати пакети, адресовані не самому хосту. Без цього клієнт підключається, рукостискання завершується успішно, але кожен пакет до інтернету відкидається. Тому ping 1.1.1.1 завершується з тайм-аутом, хоча VPN показує активне підключення.

net.ipv4.conf.all.src_valid_mark=1 часто спричиняє несподівані проблеми. WireGuard позначає власні вихідні пакети, щоб вони не маршрутизувалися назад у тунель. Сувора фільтрація зворотного шляху бачить пакет, адреса джерела якого не відповідає очікуваному маршруту, і відкидає його. Цей sysctl-параметр указує ядру приймати позначені пакети. Саме це запобігає самоблокуванню повного тунелю.

Запустіть його та створіть обліковий запис адміністратора

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

Використовуйте docker compose up і docker compose down, а не start і stop. Upstream попереджає, що start для контейнера, створеного з іншими параметрами, залишає мережу в некоректному стані. Якщо після перезавантаження потрібно автоматично відновлювати стек, restart: unless-stopped уже забезпечує це, а в розділі поведінка сервісів compose під час завантаження пояснюється, що саме гарантує ця політика, а чого вона не гарантує.

Вебінтерфейс прослуховує TCP-порт 51821. Під час першого відкриття він показує сторінку налаштування, де можна створити обліковий запис адміністратора та підтвердити адресу хоста, яку клієнти використовуватимуть для підключення до сервера. Ця адреса хоста потрапляє до рядка Endpoint у конфігурації кожного клієнта, тому це має бути публічна IP-адреса або DNS-ім’я VPS. Якщо адреса неправильна, QR-код, який ви передасте на телефон, вказуватиме на недоступну адресу, і рукостискання не завершиться.

Є ще один важливий момент щодо цього порту: wg-easy 15 відмовляється використовувати звичайний HTTP, якщо не встановити INSECURE=true. Підключення через HTTPS із сертифікатом, якому не довіряє клієнт, або завершення TLS на reverse proxy перед ним — обидва варіанти підходять. Підключення через http:// із параметрами за замовчуванням — ні.

Не публікуйте порт UI в Інтернеті

Файл compose публікує порт 51821 на всіх інтерфейсах. Це сторінка входу до системи, яка може маршрутизувати ваш трафік, тому вона не повинна бути відкритою для всіх. Публікація порту в Docker додає правила до ланцюжка DOCKER, який обробляється до ufw. Тому правило заборони ufw не закриває цей порт. Цю особливість важливо розуміти окремо. Докладний опис наведено в матеріалі чому опубліковані Docker порти ігнорують ufw.

Просте рішення — прив’язати UI до loopback і підключатися до нього через SSH-тунель:

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

Потім на вашому ноутбуці:

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

Відкрийте http://127.0.0.1:51821 у браузері на ноутбуці. Трафік шифрується через SSH, порт не відповідає іншим підключенням, а INSECURE=true безпечний у цьому випадку, оскільки незашифрований HTTP-трафік не залишає loopback-інтерфейс.

Відкрийте UDP 51820 і перевірте обидва брандмауери

Самому WireGuard потрібен доступний з інтернету UDP 51820. Docker публікує цей порт, але багато провайдерів розміщують перед VPS окремий мережевий брандмауер, про який Docker нічого не знає. Відкрийте порт в обох місцях. Якщо ви керуєте брандмауером хоста за допомогою ufw, базові правила ufw для VPS є коротшим шляхом, ніж ручне написання правил nftables.

Перевірте, чи контейнер справді прослуховує порт:

sudo ss -ulnp | grep 51820

Ви маєте побачити сокет UDP у стані прослуховування. Якщо цей рядок відсутній, контейнер не зміг підняти інтерфейс, а sudo docker compose logs wg-easy покаже причину.

Створіть клієнта та відскануйте його на телефоні

У вебінтерфейсі створіть клієнта та надайте йому ім’я, яке ви зможете розпізнати пізніше, наприклад назву пристрою, якому він належить. wg-easy виділяє наступну вільну адресу тунелю та автоматично генерує пару ключів. У кожному рядку клієнта доступні QR-код і файл .conf для завантаження.

Встановіть на телефоні офіційний застосунок WireGuard, виберіть додавання тунелю з QR-коду та наведіть камеру на код на екрані. Тунель з’явиться під введеним вами ім’ям. Увімкніть його. Після цього в рядку клієнта у вебінтерфейсі почнуть відображатися лічильники переданих даних і час останнього успішного рукостискання.

Якщо після ввімкнення клієнта рукостискання не відображається, клієнт узагалі не досягає сервера. Причина пов’язана з UDP 51820: або з брандмауером провайдера, або з адресою кінцевої точки, вбудованою в конфігурацію. Якщо рукостискання відображається, але інтернет не працює, проблема пов’язана з пересиланням трафіку або DNS.

На настільному комп’ютері завантажте файл .conf та імпортуйте його у клієнт WireGuard, а не вводьте дані вручну. Закритий ключ у цьому файлі генерується один раз і відображається один раз. Поводьтеся з цим файлом так само, як із закритим ключем SSH.

Коли UI перестає відповідати вимогам

wg-easy добре підходить, доки вашими одноранговими вузлами є люди та телефони. UI швидший за редагування файлів конфігурації, а відкликання доступу для втраченого телефона виконується одним натисканням.

Обмеження з’являються, коли потрібно налаштувати те, чого немає в моделі UI. Маршрутизація між сайтами, де AllowedIPs однорангового вузла охоплює цілу віддалену підмережу, а не одну адресу, зазвичай стає першою такою перешкодою. Далі виникають тунелі з роздільною маршрутизацією та правилами маршрутизації для кожного однорангового вузла або конфігурація, створена вашим інструментом підготовки. На цьому етапі налаштування вручну не складніше, воно просто відрізняється, а посібник зі звичайного WireGuard показує, як створити такий самий тунель за допомогою wg0.conf. Якщо ви взагалі не хочете запускати площину керування, у матеріалі WireGuard у порівнянні з Tailscale описано керований варіант.

Якщо незнайомою частиною для вас був синтаксис compose, а не WireGuard, у матеріалі Основи Docker Compose на VPS пояснено формат файлу та основні повсякденні команди.

FAQ

Чому wg-easy ігнорує мої WG_HOST і PASSWORD_HASH?

Ці змінні належать wg-easy 14. Версія 15 переписана, і розробники перенесли майже всю конфігурацію до панелі адміністратора у вебінтерфейсі. Контейнер не читає жодну з цих змінних. Тому він запускається у штатному режимі, а під час першого відвідування просить створити обліковий запис адміністратора. Укажіть адресу хоста, доступну клієнтам, на цій сторінці налаштування.

Чи потрібен SYS_MODULE, якщо ядро вже має WireGuard?

Ні. SYS_MODULE і монтування /lib/modules потрібні для того, щоб контейнер міг завантажити модуль, якщо його немає на хості. Якщо на хості команда sudo modprobe wireguard уже виконується успішно, ця можливість не використовується. Її вилучення є обґрунтованим кроком посилення безпеки, а NET_ADMIN все одно потрібен.

Клієнт підключається, але доступу до інтернету немає. У чому проблема?

Рукостискання без трафіку майже завжди означає проблему з перенаправленням пакетів. Переконайтеся, що net.ipv4.ip_forward=1 і net.ipv4.conf.all.src_valid_mark=1 залишилися у compose-файлі, оскільки під час ручного редагування копії їх часто видаляють. Якщо перенаправлення пакетів увімкнено, перевірте DNS-сервер, який отримав клієнт. Тунель, що спрямовує весь трафік через VPN, але використовує DNS-сервер, до якого клієнт більше не має доступу, у браузері виглядає саме як непрацююче з’єднання.

Як створити резервну копію клієнтів?

Усі дані зберігаються в іменованому томі etc_wireguard, у файлі wg0.json. У вебінтерфейсі також є кнопка резервного копіювання, яка експортує ті самі дані. Скопіюйте цей файл за межі сервера перед будь-яким оновленням. Відновлення виконується завантаженням файлу під час етапу налаштування нового контейнера.

Чи можна запускати wg-easy за зворотним проксі?

Так. Розмістіть проксі перед TCP 51821, завершуйте TLS на ньому та встановіть INSECURE=true у контейнері, щоб він приймав незашифрований HTTP-трафік від проксі. Порт UDP 51820 залиште опублікованим безпосередньо, оскільки VPN-трафік використовує UDP і не проходить через HTTP-проксі.