Свой VPN за 5 минут: WireGuard, удобная админка и полная защита через Nginx Proxy Manager
WireGuard — один из самых быстрых, безопасных и современных VPN-протоколов. Благодаря высокой производительности, простоте настройки и работе непосредственно в ядре Linux он стал стандартом де-факто для организации личных VPN-серверов, безопасного удаленного доступа и объединения нескольких устройств в единую защищенную сеть.
Однако классическая настройка WireGuard требует работы через терминал и ручного редактирования конфигурационных файлов. Если необходимо регулярно добавлять новых пользователей, создавать конфигурации или генерировать QR-коды для смартфонов, гораздо удобнее использовать веб-интерфейс.
Для этой задачи существует wg-easy — легковесная панель управления WireGuard, работающая в Docker-контейнере.
В этой статье мы рассмотрим, как установить WireGuard VPN на Ubuntu 22.04 или Ubuntu 24.04 с помощью Docker, безопасно опубликовать веб-интерфейс через Nginx Proxy Manager, автоматически получить SSL-сертификат Let’s Encrypt и избежать распространённых ошибок, связанных с Docker Compose и Cloudflare.
Инструкция подходит практически для любого VPS, включая DigitalOcean, Contabo, Hetzner, Vultr, OVH и других провайдеров.
Что получится в результате
После выполнения инструкции вы получите:
- полностью рабочий WireGuard VPN-сервер;
- удобную веб-панель управления wg-easy;
- автоматическую генерацию QR-кодов для подключения смартфонов;
- защищённый HTTPS-доступ через Let’s Encrypt;
- отсутствие открытого административного порта;
- совместимость с Docker Compose V1 и Docker Compose V2;
- возможность подключения клиентов Windows, Linux, macOS, Android и iPhone.
Архитектура решения
В данной инструкции используется схема, при которой VPN и веб-интерфейс работают независимо друг от друга.
VPN-трафик
Клиенты WireGuard подключаются напрямую к UDP-порту сервера (51820). Такой подход обеспечивает минимальные задержки и максимальную производительность.
Веб-интерфейс
Порт панели управления (51821) не публикуется в Интернет.
Контейнер wg-easy подключается к общей Docker-сети с Nginx Proxy Manager, который:
- принимает HTTPS-запросы;
- автоматически получает SSL-сертификат Let’s Encrypt;
- проксирует запросы во внутреннюю Docker-сеть.
Таким образом административный интерфейс остается изолированным, а наружу доступны только HTTPS и VPN.
Предварительные требования
Перед началом убедитесь, что у вас имеются:
- VPS с Ubuntu 22.04 или Ubuntu 24.04;
- установленный Docker;
- установленный Docker Compose (V1 или V2);
- установленный Nginx Proxy Manager;
- Docker-сеть, к которой подключён Nginx Proxy Manager (в примере используется
proxy-network); - домен или поддомен, указывающий на IP-адрес вашего VPS.
Проверяем установленную версию Docker Compose
Сначала определим, какая версия Docker Compose используется на сервере.
Попробуйте выполнить:
docker compose version
Если отображается номер версии — используется Docker Compose V2.
Если появляется ошибка:
docker: unknown command: compose
проверьте старую команду:
docker-compose version
Если она работает — значит установлен Docker Compose V1.
Скрипт, используемый далее в статье, автоматически определяет доступную версию Docker Compose, поэтому дополнительных действий не требуется.
Шаг 1. Настройка DNS и Cloudflare
Создайте A-запись для поддомена, например:
vpn.example.com
которая указывает на IP-адрес вашего VPS.
Если используется Cloudflare, обязательно переведите запись в режим DNS Only (серое облако).
Это важно, поскольку WireGuard использует UDP-трафик, который Cloudflare не проксирует.
Шаг 2. Открываем порт WireGuard
Если используется UFW, выполните:
sudo ufw allow 51820/udp
Если используется другой фаервол — откройте UDP-порт 51820 соответствующим способом.
Шаг 3. Создаем скрипт автоматической установки
Создайте файл:
nano install_vpn.sh
И вставьте код скрипта:
#!/bin/bash
#===============================================================================#
# WIREGUARD VPN (wg-easy) + NGINX PROXY MANAGER
# Compatible with Docker Compose V1 and V2
#===============================================================================#
set -Eeuo pipefail
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# Проверенная версия wg-easy. Для ветки v14 в GHCR публикуется мажорный
# тег "14"; если хотите жёстко зафиксировать результат, укажите точный
# тег со страницы https://github.com/wg-easy/wg-easy/releases
WG_EASY_VERSION="14"
VPN_DIR="$HOME/vpn-docker"
VPN_PORT=51820
UI_PORT=51821
# ===================================================================
# ИЗМЕНИТЕ ЭТИ ПАРАМЕТРЫ ПОД СВОЮ ИНФРАСТРУКТУРУ
# ===================================================================
DOMAIN="vpn.example.com"
NPM_NETWORK="proxy-network"
# ===================================================================
echo -e "${CYAN}=== Установка WireGuard VPN (wg-easy) ===${NC}"
# -------------------------------------------------------------------
# Проверка параметров
# -------------------------------------------------------------------
if [ "$DOMAIN" = "vpn.example.com" ]; then
echo -e "${RED}Измените DOMAIN в начале скрипта.${NC}"
exit 1
fi
# -------------------------------------------------------------------
# Проверка Docker
# -------------------------------------------------------------------
if ! command -v docker >/dev/null 2>&1; then
echo -e "${RED}Docker не установлен.${NC}"
exit 1
fi
# -------------------------------------------------------------------
# Определяем Docker Compose
# -------------------------------------------------------------------
if docker compose version >/dev/null 2>&1; then
COMPOSE="docker compose"
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE="docker-compose"
else
echo -e "${RED}Docker Compose не найден.${NC}"
echo ""
echo "Установите Docker Compose и повторите попытку."
exit 1
fi
echo -e "${GREEN}Используется:${NC} ${COMPOSE}"
# -------------------------------------------------------------------
# Проверка Docker Network
# -------------------------------------------------------------------
if ! docker network inspect "$NPM_NETWORK" >/dev/null 2>&1; then
echo -e "${RED}Docker-сеть '$NPM_NETWORK' не существует.${NC}"
echo ""
echo "Доступные сети:"
docker network ls
exit 1
fi
# -------------------------------------------------------------------
# Подготовка каталогов
# -------------------------------------------------------------------
mkdir -p "$VPN_DIR/data"
cd "$VPN_DIR"
# -------------------------------------------------------------------
# Пароль администратора
# -------------------------------------------------------------------
while true; do
echo -e "${YELLOW}Введите пароль администратора:${NC}"
read -r -s ADMIN_PASS
echo
echo -e "${YELLOW}Повторите пароль:${NC}"
read -r -s ADMIN_PASS_CONFIRM
echo
if [ -z "$ADMIN_PASS" ]; then
echo -e "${RED}Пароль не может быть пустым.${NC}"
continue
fi
if [ "$ADMIN_PASS" != "$ADMIN_PASS_CONFIRM" ]; then
echo -e "${RED}Пароли не совпадают.${NC}"
continue
fi
break
done
# -------------------------------------------------------------------
# Скачивание образа
# -------------------------------------------------------------------
WG_IMAGE="ghcr.io/wg-easy/wg-easy:${WG_EASY_VERSION}"
echo -e "${CYAN}Загрузка образа ${WG_IMAGE}...${NC}"
if ! docker pull "$WG_IMAGE"; then
echo -e "${RED}Не удалось скачать образ ${WG_IMAGE}.${NC}"
echo "Проверьте подключение к Интернету и доступность ghcr.io."
exit 1
fi
# -------------------------------------------------------------------
# Генерация BCrypt-хеша
# -------------------------------------------------------------------
echo -e "${CYAN}Генерация хеша пароля...${NC}"
RAW_OUTPUT=$(docker run --rm "$WG_IMAGE" wgpw "$ADMIN_PASS" 2>/dev/null)
if [ -z "$RAW_OUTPUT" ]; then
echo -e "${RED}Не удалось создать хеш пароля.${NC}"
exit 1
fi
# wgpw может вернуть "PASSWORD_HASH='$2a$12$...'" или просто хеш.
# Извлекаем чистый bcrypt-хеш через sed (POSIX-совместимо).
HASH=$(printf '%s\n' "$RAW_OUTPUT" | sed -n "s/.*\\(\\\$2[aby]\\\$[^ '\"]*\\).*/\\1/p" | tail -1)
if [ -z "$HASH" ]; then
echo -e "${RED}Не удалось извлечь bcrypt-хеш из вывода wgpw.${NC}"
echo "Вывод команды: $RAW_OUTPUT"
exit 1
fi
echo -e "${GREEN}Хеш пароля создан.${NC}"
# -------------------------------------------------------------------
# .env
# -------------------------------------------------------------------
cat > .env <<EOF
WG_HOST=$DOMAIN
WG_PORT=$VPN_PORT
PORT=$UI_PORT
PASSWORD_HASH=$HASH
EOF
if [ ! -f .env ]; then
echo -e "${RED}Не удалось создать файл .env${NC}"
exit 1
fi
# -------------------------------------------------------------------
# docker-compose.yml
# -------------------------------------------------------------------
cat > docker-compose.yml <<EOF
services:
wg-easy:
image: $WG_IMAGE
container_name: wg-easy
restart: unless-stopped
environment:
- LANG=ru
- WG_HOST
- PASSWORD_HASH
- PORT
- WG_PORT
- WG_DEFAULT_DNS=1.1.1.1,1.0.0.1
- WG_ALLOWED_IPS=0.0.0.0/0
- WG_MTU=1420
volumes:
- ./data:/etc/wireguard
ports:
- "\${WG_PORT}:\${WG_PORT}/udp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
networks:
- $NPM_NETWORK
networks:
$NPM_NETWORK:
external: true
EOF
if [ ! -f docker-compose.yml ]; then
echo -e "${RED}Не удалось создать файл docker-compose.yml${NC}"
exit 1
fi
# -------------------------------------------------------------------
# Диагностика при сбое запуска
# -------------------------------------------------------------------
show_diagnostics() {
echo ""
echo "Диагностика:"
if docker container inspect wg-easy >/dev/null 2>&1; then
docker logs wg-easy --tail 20 2>&1 || true
echo ""
fi
docker ps -a --filter name=wg-easy
}
# -------------------------------------------------------------------
# Запуск
# -------------------------------------------------------------------
echo -e "${CYAN}Запуск контейнера...${NC}"
$COMPOSE down >/dev/null 2>&1 || true
if ! $COMPOSE up -d; then
echo -e "${RED}Ошибка запуска контейнера.${NC}"
exit 1
fi
# -------------------------------------------------------------------
# Ожидание запуска
# -------------------------------------------------------------------
echo -e "${CYAN}Ожидание запуска контейнера...${NC}"
STARTED=false
for i in $(seq 1 20); do
if docker ps --format '{{.Names}}' | grep -q '^wg-easy$'; then
STARTED=true
break
fi
sleep 1
done
if [ "$STARTED" != "true" ]; then
echo -e "${RED}Контейнер wg-easy не запустился.${NC}"
show_diagnostics
exit 1
fi
# Контейнер появился в списке — убедимся, что он не упал через
# несколько секунд после старта (типично при ошибке в конфигурации).
sleep 3
RUNNING=$(docker inspect -f '{{.State.Running}}' wg-easy 2>/dev/null || echo "false")
if [ "$RUNNING" != "true" ]; then
echo -e "${RED}Контейнер wg-easy запустился, но сразу завершился.${NC}"
show_diagnostics
exit 1
fi
echo ""
echo -e "${GREEN}====================================================${NC}"
echo -e "${GREEN} WireGuard VPN успешно установлен!${NC}"
echo -e "${GREEN}====================================================${NC}"
echo ""
echo -e "Домен : ${GREEN}${DOMAIN}${NC}"
echo -e "VPN UDP порт : ${GREEN}${VPN_PORT}${NC}"
echo -e "UI порт (внутр.) : ${GREEN}${UI_PORT}${NC}"
echo -e "Образ : ${GREEN}${WG_IMAGE}${NC}"
echo -e "Docker Compose : ${GREEN}${COMPOSE}${NC}"
echo -e "Данные WireGuard : ${GREEN}${VPN_DIR}/data${NC}"
echo ""
echo "Следующий шаг:"
echo "Создайте Proxy Host в Nginx Proxy Manager:"
echo ""
echo " Domain Names : ${DOMAIN}"
echo " Scheme : http"
echo " Forward Hostname/IP : wg-easy"
echo " Forward Port : ${UI_PORT}"
echo " Block Common Exploits : включить"
echo " SSL : Request a new SSL Certificate"
echo " Force SSL : включить"
echo " HTTP/2 Support : включить"
echo ""
echo "После этого откройте: https://${DOMAIN}"
echo ""Обратите внимание: скрипт содержит проверку параметра
DOMAIN— если оставить значение по умолчанию (vpn.example.com), он завершится с ошибкой и не будет создавать файлы. Это защита от случайного запуска без настройки.
Поэтому перед запуском скрипта измените следующие параметры:
DOMAIN="vpn.example.com"
NPM_NETWORK="proxy-network"DOMAIN
Полное доменное имя панели управления. Например:
DOMAIN="vpn.example.com"
NPM_NETWORK
Имя Docker-сети, к которой подключен Nginx Proxy Manager. Узнать список сетей можно командой:
docker network ls
Например:
NETWORK ID NAME
xxxxxxxxxxxx bridge
xxxxxxxxxxxx proxy-network
В этом случае необходимо указать:
NPM_NETWORK="proxy-network"
Сохраните файл (Ctrl+O, Enter, Ctrl+X).
После этого сделайте его исполняемым:
chmod +x install_vpn.sh
Запустите:
./install_vpn.sh
Во время выполнения скрипт попросит придумать пароль администратора, автоматически создаст необходимые файлы, скачает образ wg-easy и запустит контейнер.
Шаг 4. Проверяем успешный запуск
После установки рекомендуется убедиться, что контейнер действительно работает.
Самый универсальный способ:
docker ps
В списке должен присутствовать контейнер:
wg-easy
со статусом:
Up (healthy)
Если используется Docker Compose V2, можно также выполнить:
docker compose ps
Если используется Docker Compose V1:
docker-compose ps
Где хранятся данные VPN
Все данные WireGuard — конфигурация сервера и список созданных клиентов — находятся в каталоге ~/vpn-docker/data. Именно этот каталог стоит регулярно резервировать: в нём хранятся ключи и профили всех подключённых устройств.
Если понадобится перенести VPN на другой сервер, достаточно скопировать туда весь каталог ~/vpn-docker (включая data/, .env и docker-compose.yml) и запустить docker compose up -d (или docker-compose up -d) — все клиенты продолжат работать без переустановки.
Шаг 5. Настраиваем Nginx Proxy Manager
Откройте панель управления Nginx Proxy Manager.
Перейдите:
Hosts → Proxy Hosts → Add Proxy Host
Во вкладке Details заполните поля:
Domain Names
vpn.example.com
Scheme
http
Forward Hostname / IP
wg-easy
Forward Port
51821
Включите опцию:
- Block Common Exploits.
Перейдите во вкладку SSL.
Выберите:
- Request a new SSL Certificate.
После этого включите:
- Force SSL;
- HTTP/2 Support.
Примите условия Let’s Encrypt и нажмите Save.
Через несколько секунд сертификат будет выпущен автоматически.
Проверяем работу VPN
Откройте браузер и перейдите по адресу:
https://vpn.example.com
Должна открыться страница авторизации wg-easy.
После входа вы сможете:
- создавать новых пользователей;
- скачивать готовые конфигурации WireGuard;
- отображать QR-коды для Android и iPhone;
- удалять клиентов;
- отключать пользователей.
Типичные ошибки
Ошибка docker: unknown command: compose
На сервере установлен Docker Compose V1.
Это нормально. Скрипт автоматически использует команду docker-compose.
Ошибка 502 Bad Gateway
Обычно это означает, что Nginx Proxy Manager не может подключиться к контейнеру wg-easy.
Проверьте:
docker ps
Контейнер wg-easy должен присутствовать в списке.
Также убедитесь, что wg-easy и Nginx Proxy Manager подключены к одной Docker-сети.
VPN не подключается
Проверьте:
- открыт ли UDP-порт 51820;
- правильно ли настроена DNS-запись;
- используется ли режим DNS Only в Cloudflare;
- совпадает ли значение
WG_HOSTс вашим доменным именем.
Часто задаваемые вопросы
Можно ли использовать Cloudflare?
Да. Однако поддомен WireGuard должен работать в режиме DNS Only. Использование режима Proxied для VPN недопустимо.
Подходит ли инструкция для Ubuntu 22.04?
Да. Она полностью совместима как с Ubuntu 22.04, так и с Ubuntu 24.04.
Можно ли использовать DigitalOcean, Contabo или Hetzner?
Да. Инструкция не зависит от VPS-провайдера и подходит для большинства облачных серверов.
Нужно ли открывать порт 51821?
Нет. Порт панели управления остается доступным только внутри Docker-сети. В Интернет публикуется только HTTPS через Nginx Proxy Manager.
Какие устройства поддерживаются?
WireGuard работает на Windows, Linux, macOS, Android, iOS и большинстве современных маршрутизаторов.
Где хранятся созданные клиенты и что нужно бэкапить?
Список клиентов и ключи WireGuard хранятся в файле wg0.json внутри каталога ~/vpn-docker/data. При переносе сервера или создании резервной копии достаточно сохранить этот каталог целиком — переустанавливать клиентов заново не потребуется.
Почему в скрипте используется тег wg-easy:14, а не latest?
Тег latest указывает на самую свежую версию образа на момент скачивания — со временем разработчики wg-easy могут выпустить новый мажорный релиз с изменённым поведением или несовместимыми настройками, и тогда установка по этой инструкции у нового читателя может пойти не так, как описано в статье.
Чтобы инструкция оставалась воспроизводимой и через год давала тот же результат, что и сегодня, скрипт закрепляет конкретную проверенную версию — wg-easy:14. Если вы хотите получать более новые версии автоматически, замените в начале скрипта строку WG_EASY_VERSION="14" на WG_EASY_VERSION="latest", но учитывайте, что в этом случае поведение контейнера может со временем измениться.
Заключение
Использование WireGuard совместно с wg-easy, Docker и Nginx Proxy Manager позволяет развернуть современный VPN-сервер с удобным веб-интерфейсом и безопасной архитектурой. VPN-клиенты подключаются напрямую по UDP без дополнительных посредников, а административная панель остается скрытой за обратным прокси с автоматическими SSL-сертификатами Let’s Encrypt.
Такой подход сочетает высокую производительность, простоту администрирования и безопасность, что делает его подходящим как для личного использования, так и для небольших команд или организаций.
-
Полное руководство по настройке VPS на Contabo (Ubuntu 24.04 LTS) и развертыванию безопасной Docker-инфраструктуры

-
Как перенести домен с Domain.com (или другого регистратора) на Cloudflare без простоя сайта: пошаговое руководство

-
Руководство: Как подключиться к VPS через FileZilla по SFTP с помощью SSH-ключа

-
Мониторинг VPS: Установка Netdata и Nginx Proxy Manager через Docker
