Странствующий техник
Подкинуть работёнку
Назад к статьям
DEV11 августа 2026·6 MIN

VLESS+Reality в Docker: селективный прокси только для Telegram-клиента

Селективный VLESS+Reality: как в Docker пустить через прокси только Telegram-клиент, а Playwright и HTTP-запросы — напрямую, без задержек и риска бана по IP.

В мультисервисном Docker-приложении редко бывает так, что весь трафик нужно проксировать одинаково. Типичная ситуация: Telegram API заблокирован в юрисдикции, где крутится сервер, а остальные внешние вызовы — к сторонним API, к headless-браузеру для автоматизации — работают напрямую без проблем.

Заворачивать весь исходящий трафик контейнера в прокси в этом случае — плохая идея. Ниже — рабочая схема, где через VLESS+Reality идёт только Telegram-клиент, а всё остальное продолжает стучаться в сеть напрямую.

Стоит сразу оговориться, чего в статье нет: большинство материалов по запросу "VLESS Reality Telegram бот" на деле про боты, которые продают VPN-подписки конечным пользователям — это другая задача. Здесь речь о том, как выборочно завести в прокси один клиент внутри собственного приложения, а не о продукте для перепродажи доступа.

Что такое VLESS+Reality и зачем он тут

VLESS — легковесный протокол проксирования из экосистемы Xray/V2Ray, сам по себе он ничего не шифрует и не маскирует. Reality — это надстройка над TLS-хендшейком: она делает так, что соединение с прокси-сервером визуально неотличимо от обычного HTTPS-запроса к настоящему сайту (обычно берут популярный домен как "маску").

DPI-системы, которые блокируют по сигнатурам протокола (характерный TLS ClientHello, паттерны прежних VPN-протоколов), не видят в Reality-соединении ничего подозрительного — трафик выглядит как поход браузера на example.com. Это и отличает Reality от классических VPN-протоколов, которые DPI научился фильтровать по отпечатку.

Для задачи "обойти блокировку Telegram API из конкретного региона" это один из немногих протоколов, который стабильно продолжает работать, когда более старые схемы (обычный VMess, Shadowsocks без обфускации) уже отлавливаются и режутся.

Почему нельзя просто завернуть весь трафик контейнера

Первая идея, которая приходит в голову — поднять прокси на уровне контейнера целиком: прописать HTTP_PROXY/HTTPS_PROXY в переменных окружения сервиса или завернуть весь исходящий трафик через iptables/tun-интерфейс. Звучит просто, но в мультисервисном приложении это создаёт три конкретные проблемы.

Задержка на каждый запрос. VLESS+Reality добавляет TLS-хендшейк до прокси-сервера поверх обычного TLS до целевого хоста — по сути, двойное шифрование на каждый чих. Для Telegram API, где обмен происходит раз в несколько секунд, это незаметно. Для headless-браузера, который за одну сессию делает десятки запросов к статике, стилям и скриптам стороннего сайта, — это заметное замедление автоматизации.

Риск бана по IP. Прокси-сервер обычно один и тот же для всех исходящих соединений. Если через него пойдёт ещё и трафик автоматизации к сторонним ресурсам, любая антибот-система на той стороне видит один и тот же IP для потенциально сотен запросов с разным поведением. Для Telegram-бота это не проблема — но для браузерной автоматизации, где IP-репутация напрямую влияет на то, банят сессию или нет, это лишний риск, который решение не должно создавать.

Ненужное усложнение топологии. Не весь трафик контейнера нуждается в обходе блокировки. Прокси — это ещё одна точка отказа: если сервер недоступен или ловит рейт-лимит, класть из-за этого вообще все внешние обращения сервиса — избыточная плата за проблему, которая касается только одного клиента.

Прокси должен быть свойством конкретного HTTP-клиента, а не сетевого namespace контейнера целиком.

Как настроить селективную маршрутизацию

Решение не требует ни split-tunnel на уровне ОС, ни отдельного sidecar-контейнера с policy-based routing. Прокси настраивается explicit — прямо в конструкторе конкретного клиента, который его использует.

Xray-клиент как локальный SOCKS5/HTTP-прокси

Сам VLESS+Reality — это протокол между Xray-клиентом и Xray-сервером. Приложению он не нужен напрямую: локальный Xray-клиент поднимает обычный SOCKS5 (или HTTP) прокси на localhost, а уже его адрес передаётся конкретному HTTP-клиенту внутри кода.

Минимальный config.json для клиента — inbound слушает локально, outbound уходит в Reality:

{
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": { "udp": true }
    }
  ],
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "proxy.example.com",
            "port": 443,
            "users": [
              {
                "id": "UUID-СЮДА",
                "encryption": "none",
                "flow": "xtls-rprx-vision"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "serverName": "www.microsoft.com",
          "publicKey": "PUBLIC-KEY-СЮДА",
          "shortId": "abcd1234",
          "fingerprint": "chrome"
        }
      }
    }
  ]
}

serverName — тот самый домен-маска, под который Reality подстраивает TLS-хендшейк. Он должен реально существовать и отвечать TLS 1.3 — сервер использует его "рукопожатие" как эталон.

Docker-сеть: отдельный сервис для Xray-клиента

В docker-compose.yml Xray-клиент удобно вынести отдельным сервисом — он не относится к бизнес-логике и его конфиг не должен пересекаться с остальными сервисами:

services:
  xray-client:
    image: teddysun/xray:latest
    volumes:
      - ./xray-client-config.json:/etc/xray/config.json:ro
    networks:
      - app-net
 
  telegram-bot:
    build: ./telegram-bot
    environment:
      - PROXY_URL=socks5://xray-client:10808
    depends_on:
      - xray-client
    networks:
      - app-net
 
  browser-automation:
    build: ./browser-automation
    # никаких PROXY_URL — трафик идёт напрямую
    networks:
      - app-net
 
networks:
  app-net:

Ключевой момент — PROXY_URL объявлен только у сервиса telegram-bot. Остальные сервисы в той же Docker-сети прокси даже не видят, если явно на него не сослаться.

На уровне кода: прокси только у одного клиента

Дальше — самая важная часть: прокси должен быть параметром конкретного HTTP-клиента, а не глобальной переменной окружения процесса. На aiogram (Telegram Bot API для Python) это выглядит так:

import os
from aiogram import Bot
from aiogram.client.session.aiohttp import AiohttpSession
 
proxy_url = os.environ.get("PROXY_URL")  # socks5://xray-client:10808
 
session = AiohttpSession(proxy=proxy_url) if proxy_url else AiohttpSession()
bot = Bot(token=BOT_TOKEN, session=session)

А клиент для стороннего API или для управления headless-браузером конфигурируется отдельно и о существовании PROXY_URL вообще не знает:

import httpx
 
# без прокси — прямое соединение
client = httpx.AsyncClient(timeout=30.0)

Для Playwright то же самое — прокси не передаётся при запуске браузера, и весь трафик автоматизации идёт с реального IP сервера:

from playwright.async_api import async_playwright
 
async with async_playwright() as p:
    browser = await p.chromium.launch()  # без proxy=...

Ни HTTP_PROXY, ни HTTPS_PROXY на уровне контейнера здесь не выставляются вообще — если бы они были в окружении процесса, httpx и Playwright подхватили бы их автоматически, и вся эта изоляция была бы бессмысленной.

Итог: где проходит граница ответственности

Селективность в этой схеме держится на одном простом правиле: прокси — это explicit-параметр конструктора клиента, а не факт сетевого окружения контейнера. VLESS+Reality решает задачу обхода блокировки конкретно для Telegram API, а Docker-сеть и переменные окружения остаются нетронутыми для всех остальных сервисов.

Это даёт систему, где добавление ещё одного клиента, которому тоже понадобится прокси (или наоборот — которому прокси нужно будет убрать), — это правка одной строчки конфигурации конкретного сервиса, а не пересборка сетевой топологии всего приложения.

Pro Tips: как получить максимум

  • Проверяйте serverName заранее — домен-маска для Reality должен реально существовать и поддерживать TLS 1.3 на 443 порту, иначе хендшейк будет выглядеть подозрительно и Reality потеряет смысл.
  • Держите Xray-клиент отдельным сервисом, а не библиотекой внутри кода бота — так его конфиг и обновления не тянут за собой пересборку остальных сервисов.
  • Пробрасывайте прокси через явный параметр клиента (proxy=, PROXY_URL только в нужном сервисе), а не через HTTP_PROXY/HTTPS_PROXY в окружении — иначе рискуете завернуть в прокси то, что проксировать не планировали.
  • Тестируйте оба пути раздельно — curl через socks5://xray-client:10808 для проверки прокси и прямой curl из контейнера автоматизации, чтобы убедиться, что он действительно ходит напрямую.
  • Логируйте исходящий IP на старте сервиса — один лог-вызов к сервису вида ifconfig.me при старте контейнера сразу показывает, применился прокси или нет, без разбора трафика.

Common Pitfalls: где люди ошибаются

  • Прописывают HTTP_PROXY глобально в docker-compose.yml — переменная окружения на уровне сервиса подхватывается всеми HTTP-клиентами внутри контейнера без разбора, включая те, которым прокси не нужен.
  • Используют домен-маску, который не поддерживает TLS 1.3 — Reality-хендшейк не проходит проверку подлинности, и соединение либо не работает, либо выглядит подозрительно для DPI.
  • Забывают про depends_on для Xray-клиента — бот стартует раньше прокси-сервиса, первые запросы падают по connection refused, пока Xray не поднялся.
  • Гоняют весь трафик автоматизации через тот же прокси-сервер, что и Telegram-бота — общий исходящий IP для разных типов активности повышает шанс блокировки на стороне, куда стучится автоматизация.
  • Не проверяют, что прокси реально используется — при неверном парсинге PROXY_URL клиент молча падает обратно на прямое соединение, и проблема с блокировкой остаётся незамеченной до продакшена.

Идентификация

Денис Заплахов

Исследую Пустоши рутинных задач: настраиваю AI-инструменты, создаю скиллы и тестирую нейросети на реальных кейсах.

Досье в Pip-Boy 3000
© 2026 79z.ru · Странствующий техник